├── .gitignore ├── LICENSE ├── README.md ├── TunneLS.go ├── boot ├── TunneLS.plist └── TunneLS.service ├── config.json ├── fileLogger.go ├── main.go ├── node.go ├── screenshot.png └── tls ├── cert.pem ├── key.pem └── openssl.cnf /.gitignore: -------------------------------------------------------------------------------- 1 | TunneLS 2 | -------------------------------------------------------------------------------- /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 [yyyy] [name of copyright owner] 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TunneLS 2 | 3 | ## THIS REPOSITORY IS DEPRECATED, I USE https://github.com/nhooyr/tlsmuxd and https://github.com/nhooyr/tlswrapd NOW 4 | 5 | ## Description 6 | 7 | TunneLS is a TLS wrapper/proxy in go. Wrap existing connections in TLS to bypass annoying DPI (deep packet filtering) from blocking your connections in other protocols or just secure insecure connections. It can also act as a proxy for an application that doesn't use TLS. 8 | 9 | **If you're a newbie, read all of the documentation I've wrote specifically for you to get you to not only use it effectivley but also understand how it works!** 10 | 11 | ## Install 12 | 13 | Go 1.5 only so make sure you have it installed and configured correctly. 14 | 15 | go get -u github.com/nhooyr/TunneLS 16 | 17 | Will install it into $GOPATH/bin. Make sure your $GOPATH/bin is in your $PATH. 18 | 19 | If so you should be able to launch it as 20 | 21 | TunneLS 22 | 23 | Use the -c flag to point it to a config file, the default location it looks for is /usr/local/etc/TunneLS/config.json. 24 | 25 | ####[How It Works](#how-it-works-1) 26 | High level overview of how the TunneLS works with some neat diagrams. 27 | 28 | ####[Configuration](#configuration-1) 29 | Learn how to configure the program options as well as how to get it to run as a daemon on linux/mac. 30 | 31 | ####[Configuring Certificates and Keys](#configuring-certificates-and-keys-1) 32 | If you want to understand how certificates work and how to generate your own certificates to use. 33 | 34 | ####[Example](#example-1) 35 | An easy to follow example to understand how the program works. Works along side the included config.json and certificate/key. 36 | 37 | **Do not actually use this cert in a real scenerio, please generate a new one, its described in the above section** 38 | 39 | 40 | ## How it works 41 | 42 | * --- represents plain data 43 | 44 | * \#\#\# represents the TLS tunnel around the plain data 45 | 46 | ### Normal connections 47 |
 48 | +----------+                                                    +----------+
 49 | |          |                                                    |          |
 50 | |  client  +----------------------------------------------------+  server  |
 51 | |          |                                                    |          |
 52 | +----------+                                                    +----------+
 53 | 
54 | 55 | The link between the client and server is either insecure or maybe it uses SSH as the protocol which is picked up by deep packet filtering and thus blocked. You can tunnel it instead through a TunneLS tunnel which is encrypted via TLS, which makes it much less likely to be blocked by DPI as the entire web uses TLS, its far too restrictive for most networks and you should be able to get through. 56 | 57 | ### TunneLS connections 58 |
 59 | +----------+      +---------------+      +---------------+      +----------+
 60 | |          |      |               +######+               |      |          |
 61 | |  client  +------+  gTLS client  |------|  gTLS server  +------+  server  |
 62 | |          |      |               +######+               |      |          |
 63 | +----------+      +---------------+      +---------------+      +----------+
 64 | 
65 | 66 | Now the difference is that whatever the client sends to the gTLS client is forwarded over to the gTLS server and then finally over to the real server. The advantage here is that the gTLS client and gTLS server communicate via TLS thus protecting the data if the client/server communicate insecurely and also likely bypassing any DPI as TLS is almost never blocked. 67 | 68 | Now that you understand how it works, also know that its pure TLS, know that no other protocol is being used other than TLS to tunnel so its not necessary to use both the server and client. If a application communicates via TLS but the other does not, you only need to wrap insecure one. Thus it can also act as just a proxy. 69 | 70 | [Here](http://pastebin.com/raw.php?i=44J505Te) is a diagram of how SSH tunneling can be tunneled through TunneLS. If you do tunnel ssh through TunneLS, use dynamic port forwarding and bam, you got yourself a SOCKS5 proxy being tunneled through TLS, perfect for web browsing without any restrictions! 71 | 72 | #### gTLS Client 73 | Basically the client listens on it's Accept address for plain connections and proxies them to its Connect address via TLS. 74 | 75 | #### gTLS Server 76 | 77 | Basically the server does the exact opposite. Listens on it's Accept address for TLS connections and proxies them to its Connect address via a plain connection. 78 | 79 | 80 | ## Configuration 81 | 82 | ###config.json 83 | 84 | The configuration file's syntax is JSON and it consists of an array of the nodes structs each with their own fields. Then its followed by StdErrLogging/StdErrPrefix/LogPath all of which are explained further below. Here is the example file included. 85 | 86 | { 87 | "Nodes": [ 88 | { 89 | "Name": "nc", 90 | "Mode": "client", 91 | "Accept": "localhost:5002", 92 | "Connect": "localhost:5001", 93 | "CA": "tls/cert.pem", 94 | "InsecureSkipVerify": true 95 | }, 96 | { 97 | "Name": "nc -l", 98 | "Mode": "server", 99 | "Accept": "localhost:5001", 100 | "Connect": "localhost:5000", 101 | "Cert": "tls/cert.pem", 102 | "Key": "tls/key.pem" 103 | } 104 | 105 | ], 106 | "StdErrLogging": true, 107 | "StdErrPrefix": true 108 | } 109 | 110 | Note: read [example](#example-1) for a hands on tutorial on using config.json and the rest of the program. 111 | 112 | First is the Nodes array which consists of structs that represent the nodes to run followed by StdErrLogging and StdErrPrefix, the first of which is self explainatory. StdErrPrefix just means to add the date and time in the logging (wouldn't need this with the systemd journal). 113 | 114 | ###Fields 115 | 116 | You can use relative file paths, relative to the config file. eg say the config file is in /usr/local/etc/TunneLS. If the value of the Cert field is "cert.pem" that really means "/usr/local/etc/TunneLS/cert.pem" 117 | 118 | When it says int just put a integer such as 10 for the value with no quotes, otherwise the value is implied as a string, eg whatever value with quotes. 119 | 120 | Don't bother with the optional fields if you don't care, they aren't necessary. Most people likely only want to use the required fields. 121 | 122 | ####Required 123 | 124 | Mode -- sets node as client/server 125 | 126 | Name -- name for logging 127 | 128 | Accept -- listen address; format is host:port. If host is missing, localhost is assumed 129 | 130 | Connect -- dial address; format is host:port. If host is missing, localhost is assumed 131 | 132 | 133 | ####Optional 134 | 135 | Timeout -- int -- duration to sleep in seconds after network errors, default is 15 136 | 137 | TCPKeepAliveInterval -- int -- interval between TCP keep alives in seconds, default is 15 138 | 139 | LogData -- bool -- determines whether or not to log the actual reading/writing of data 140 | 141 | Ciphers -- array of ciphers to use, see [this](https://golang.org/pkg/crypto/tls/#pkg-constants) for a list. Add the name without the "TLS\_" part. If you only want to use "TLS\_RSA\_WITH\_RC4\_128\_SHA" then it should look like 142 | 143 | "Ciphers": [ 144 | "RSA_WITH_RC4_128_SHA" 145 | ] 146 | 147 | ####Required Server Fields 148 | 149 | Cert -- path to the certificate that the server presents to clients 150 | 151 | Key -- path to the key file of the Cert 152 | 153 | 154 | ####Optional Server fields 155 | 156 | SessionTicketKeyRotationInterval -- int -- interval between session key rotation in seconds, default is 28800 or 8 hours. 157 | 158 | ClientAuth -- type of client authentication. see [this](https://golang.org/pkg/crypto/tls/#ClientAuthType) for the list. Just set it to one of the names. Json should look like 159 | 160 | "ClientAuth": "RequireAndVerifyClientCert" 161 | 162 | CRL -- path to certificate revocation list for client authentication (read every single time a client authenticates) 163 | 164 | ####Optional Client Options 165 | Cert -- path to the RootCA for the certificate from the server. Useful when using self signed certificates (like below) that are not in the operating systems store, you must use this option to point to the RootCA in those cases or you'll get a nasty error about the certificate not being trusted. 166 | 167 | InsecureSkipVerify -- skip trust checks on the certificate (please only use this when testing) 168 | 169 | ####LogPath 170 | Points to the file to log to. 171 | 172 | The format for logging is: 173 | 174 | TunneLS: year/month/date hour/minute/second mode name message 175 | 176 | When logging is global, the mode is global and name is empty 177 | 178 | For example 179 | 180 | TunneLS: 2015/09/03 07:04:42 global starting client node nc 181 | TunneLS: 2015/09/03 07:04:42 client nc initializing 182 | 183 | 184 | ####StdErrPrefix 185 | StdErrPrefix controls whether to add the date and time to stderr logging. 186 | 187 | year/month/date hour/minute/second 188 | 189 | This lets TunneLS's logging play nice with integrated system logging such as systemd's journal which have their own timestamp information as you can disable the logging prefix. 190 | 191 | This field only applies to the stderr logging and is off by default. LogPath's file will always have the prefix information. 192 | 193 | ###Run at boot 194 | In order to launch TunneLS at boot with your OS of choice follow the instructions. The boot files are located in the boot folder. 195 | 196 | ####Linux 197 | I've included a TunneLS.service file for systemd in linux. First copy it into /etc/systemd/system/ 198 | 199 | Next change the ExecStart field in TunneLS.service to the absolute path of the TunneLS executable on your system. Next remove the -c flag if the TunneLS config file is in /usr/local/etc/TunneLS/config.json, otherwise please add in the absolute path to the config file. 200 | 201 | Now reload systemd with 202 | 203 | sudo systemctl daemon-reload 204 | 205 | Finally enable it to start at boot with 206 | 207 | sudo systemctl enable TunneLS 208 | 209 | ####Mac 210 | I've included the TunneLS.plist launch daemon file for launchd in osx. First copy it into /Library/LaunchDaemons. Next change the first string tag of the ProgramArguments array to the absolute path of the TunneLS executable on your system. 211 | 212 | Now if you need to specify the location of the config file, please do so on the third tag. Otherwise delete the second and third tags. 213 | 214 | Finally load it with 215 | 216 | sudo launchctl load -w /Library/LaunchDaemons/TunneLS.plist 217 | 218 | 219 | ##Configuring Certificates and Keys 220 | TLS works with certificates and asymmetric cryptography. Lets first understand what that means. Skip this section if you already know how it all works and just want to get to generating the cert/key. 221 | 222 | ####Certificates? 223 | This is a very vast topic and this is a very dumbed down version, but sufficient enough for you to be able to use this program. 224 | 225 | #####Symmetric Cryptography 226 | This is the type of cryptography you already understand. Symmetric cryptography both parties must know the same key to decrypt/encrypt. However this is a problem on the web, you can't send the key over to the client to initiate a encrypted session. If someone is listening and they grab the key, your entire session can be very easily decrypted. This is where asymmetric cryptography comes into play. 227 | 228 | #####Asymmetric Cryptography 229 | Asymmetric cryptography is based on the premise of special maths and algorithms that allow you to generate two keys with a special property. Anything decrypted with one key, can only be decrypted by the other key as well as vice versa. Why is this crucial? 230 | 231 | This means that you can send out one key (the public key, or certificate) to other people to encrypt some data. They encrypt this data and send it back over to you, and you are the only one who can decrypt it because you are the only one who has the private key. Doesn't matter who's listening, they only get the public key, the key used to encrypt, not the key used to decrypt, and therefore they cannot decrypt. 232 | 233 | The other advantage of asymmetric cryptography is signing. eg If something is encrypted via the private key, it can only be decrypted via the public key correct? This means that whenever something is successfully decrypted via the public key you are 100% sure that the it comes from whoever has the private key, as no one else can encrypt data to be decrypted with the public key. 234 | 235 | #####Trust 236 | Signing comes into play with trust. If I setup a fake server with a certificate with the name www.google.com, it shouldn't be trusted to be legit. It should be rejected as insecure. But how, how will my computer know the difference between the legit certificate from google and the fake one? 237 | 238 | Well your computer comes with a set of root CAs (certificate authorities), basically a bunch of certificates that your computer 100% trusts as legit. These certificates are used to sign other certificates to validate their authenticity. eg they use their private key to sign certificates so that you can successfully decrypt certificates with the CAs public key, thus validating the certificate. (the root CAs are self signed, therefore you just have to trust them as being legit, trust has to begin somewhere) 239 | 240 | The root CAs private keys are very securely protected so that certificates are only signed to the actual domain owners and not random people. You can create your own CA, add it to your computers store, and then sign a bunch of certificates to use privately. But remember that these certificates will only be trusted by computers who trust the CA you created. 241 | 242 | In the example section we do this, the certificate used is actually a CA certificate. As long as this CA cert is in the clients trust store, the certificate is trusted. This is why the Client node accepts the Cert field. The Cert field for the client is what points it to the CA you want to use to validate the certificate from the server. Since the certificate we use on server is self signed, its CA is itself. 243 | 244 | Don't worry about the actual math behind it, I myself have a very primitive understanding. If you understand the above, you're good enough to use this program. If you don't, please take the time to research it a bit, it'll go a long way. 245 | 246 | ###Generating Certificates 247 | 248 | These commands generate a self signed CA certificate, this means that it won't be trusted by default on your client. You must either add the path to it in the Cert field of the client or add it to the client's trust store. 249 | 250 | To generate certificates we use openssl. I've already setup a openssl.cnf that should setup the correct openssl options for most people, this should make it much more streamlined for beginners. Just cd into the tls folder to where it is located before running any commands. 251 | 252 | First open openssl.cnf and modify the req\_distinguished\_name to fit your liking. Change the domain name (common name), email etc. 253 | 254 | In order to use the certificate with multiple domain names, uncomment subjectAltName, [ alt\_names ], DNS.1 and DNS.2 and replace COMMON.NAME with the domain name set in req\_distinguished\_name and replace SECOND.NAME with the second name you want to use. You can also add more names with DNS.n where n is the next number. 255 | 256 | You can also use wildcards in domain names to match all sub domains. eg you can set the common name to "\*.example.com" to match all of example.com's subdomains such as www.example.com, but it won't match example.com, you'll need to set second DNS name for that as in the above paragraph. 257 | 258 | Next choose whether or not you want to use ECDSA or RSA as the algorithm behind your certificate. I'd recommend ECDSA because the key sizes are smaller, its faster, and more secure. But if for some reason you want RSA, it works perfectly fine. 259 | 260 | ####ECDSA - RECOMMENDED 261 | Creating a ECDSA signed cert is a two step process. First we must generate the key. 262 | 263 | The first command uses the secp384r1 curve, tad slower but more secure, the second uses prime256v1 which is faster but less secure. Either works well, **run only one** 264 | 265 | openssl ecparam -genkey -name secp384r1 -out key.pem 266 | openssl ecparam -genkey -name prime256v1 -out key.pem 267 | 268 | That will generate a key.pem for you to use 269 | Next create the cert 270 | 271 | openssl req -new -x509 -config openssl.cnf -key key.pem -nodes -out cert.pem 272 | 273 | That will generate a self signed cert.pem for you to use. 274 | 275 | ####RSA 276 | You can edit the default\_bits field in the openssl.cnf if you don't want a RSA key size of 4096 but maybe instead 2048. 277 | 278 | This command will generate a self signed certificate and key for you, cert.pem and key.pem 279 | 280 | openssl req -new -x509 -config openssl.cnf -nodes -out cert.pem 281 | 282 | 283 | ##Example 284 | Lets take a look at the example configuration file, config.json to get an idea of how TunneLS is configured and how it works. 285 | First start a TunneLS instance with the -c flag pointing to the configuration file 286 | 287 | TunneLS -c config.json 288 | 289 | Leave that open and open a new terminal. Now run (this is for the BSD version of netcat, just change the syntax a bit if you use GNU) 290 | 291 | nc -l 5000 292 | 293 | This opens up the nc application listening and accepting connections on port 5000. It then outputs whatever is received on these connections to stdout, which in this case is connected to your terminal. 294 | 295 | Leave that nc running and open a new terminal. Now run 296 | 297 | nc localhost 5002 298 | 299 | This makes nc dial port 5002 on localhost. Now when you type anything into the nc terminal, and press enter it appears on the other nc instance running in the other terminal! but wait.... the port numbers are different how could this be, how are they connected??? Thats TunneLS doing its magic. 300 | 301 | In that configuration file there are two TunneLS "nodes" defined, 1 server and 1 client. The client's Listen address is port 5002 and Connect is to port 5001. This means it accepts plain connections on port 5002 and proxies them to port 5001 with TLS. The Server's listen address is port 5001, and Connect address is port 5000. This means it accepts TLS connections on port 5001 and proxies them to port 5000 with plain connections. 302 | 303 | The entire ordeal looks as follow. 304 | 305 |
306 |               port 5002              port 5001              port 5000
307 | +----------+      +---------------+      +---------------+      +----------+
308 | |          |      |               +######+               |      |          |
309 | |    nc    +------+  gTLS client  |------|  gTLS server  +------+   nc -l  |
310 | |          |      |               +######+               |      |          |
311 | +----------+      +---------------+      +---------------+      +----------+
312 | 
313 | 314 | Hopefully it makes more sense now to you. nc does everything over plain text and TunneLS allows you to wrap its insecure connection in TLS. You can take out the server node of the config.json, and take it and actually run it on a server somewhere, just change the Connect address of the client node to the new servers listening address and everything will work the same. Quite fun right? :P 315 | 316 | Read the log messages from TunneLS, you can see what its doing, the tunnels its creating, the certificates its loading, errors etc. Logging is always done to stderr, but you can set a seperate logging file with the LogPath option. I've used /dev/null as LogPath to have it not log to a file. Setting it to /dev/null is the same as not having but, but I set it to demonstrate the option. Set LogPath to "logs" to have logging done to a file called logs in the same directory as config.json. Go ahead and try it! 317 | 318 | StdErrPrefix set to true is needed for timestamps on the logging to stderr (stderr is connected to your terminal). By default this option is off so stderr logs don't have the logging prefix, this allows for better integration with systemd's journal and the like which usually have their own timestamp information logs. See [StdErrPrefix](#stderrprefix) for more information. 319 | 320 | Note: The client and server are configured with a default self signed certificate I've provided (the cert is expired so insecureskipverify is needed). When actually using this program for real purposes, please look at the [Configuring Certificates and Keys](#configuring-certificates-and-keys-1) section to generate a new key pair. Anyone who has this key.pem file can decrypt your communications (the configuring certificates section also includes a small introduction, please read it if you do not know what I mean). 321 | 322 | ##ITS ALIVE! 323 | 324 | 325 | 326 | ## Contribute 327 | 328 | Contributions are very welcome. File issues for bugs, fix bugs with a pull request and if you think there is a very essential feature missing from TunneLS, feel free to either submit a pull request or open a issue. 329 | 330 | Editing the code is very easy, its not complicated and very well documented. Start at main.go and branch from there and you'll understand exactly how everything works very quickly. 331 | 332 | ## Contact 333 | 334 | Feel free to contact me at anmol@aubble.com 335 | -------------------------------------------------------------------------------- /TunneLS.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "encoding/json" 5 | "io/ioutil" 6 | "log" 7 | ) 8 | 9 | // TunneLS represents the the settings for the nodes and the logFile 10 | type TunneLS struct { 11 | // slice of the configured nodes to launch 12 | Nodes []*node 13 | 14 | // path to the logFile 15 | LogPath string 16 | 17 | // controls whether or not to log to stderr 18 | StderrLogging bool 19 | 20 | // controls whether or not to have timestamps/prefix on the stderr logging 21 | StderrPrefix bool 22 | } 23 | 24 | // read config file into tls 25 | func (tls *TunneLS) parseFile(path string) { 26 | raw, err := ioutil.ReadFile(path) 27 | if err != nil { 28 | log.Fatalln(globalPrefix, err) 29 | } 30 | if err = json.Unmarshal(raw, tls); err != nil { 31 | log.Fatalln(globalPrefix, err) 32 | } 33 | } 34 | 35 | // logln logs to the global fileLogger as global 36 | // arguements are handled same as fmt.Println 37 | func (tls *TunneLS) logln(v ...interface{}) { 38 | l.println(append([]interface{}{globalPrefix}, v...)...) 39 | } 40 | 41 | // logf logs to the global fileLogger as global 42 | // arguements are handled same as fmt.Printf 43 | func (tls *TunneLS) logf(format string, v ...interface{}) { 44 | l.printf(globalPrefix+format, v...) 45 | } 46 | -------------------------------------------------------------------------------- /boot/TunneLS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Label 6 | TunneLS 7 | ProgramArguments 8 | 9 | /usr/local/bin/TunneLS 10 | -c 11 | /usr/local/etc/TunneLS/config.json 12 | 13 | KeepAlive 14 | 15 | ThrottleInterval 16 | 30 17 | RunAtLoad 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /boot/TunneLS.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=TunneLS TLS proxy 3 | Wants=network-online.target 4 | After=network-online.target 5 | 6 | [Service] 7 | ExecStart=/usr/local/bin/TunneLS -c /usr/local/etc/TunneLS/config.json 8 | Restart=always 9 | RestartSec=30 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | { 2 | "Nodes": [ 3 | { 4 | "Name": "nc", 5 | "Mode": "client", 6 | "Accept": "localhost:5002", 7 | "Connect": "localhost:5001", 8 | "CA": "tls/cert.pem", 9 | "InsecureSkipVerify": true 10 | }, 11 | { 12 | "Name": "nc -l", 13 | "Mode": "server", 14 | "Accept": "localhost:5001", 15 | "Connect": "localhost:5000", 16 | "Cert": "tls/cert.pem", 17 | "Key": "tls/key.pem" 18 | } 19 | 20 | ], 21 | "StdErrLogging": true, 22 | "StdErrPrefix": true 23 | } 24 | -------------------------------------------------------------------------------- /fileLogger.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "os" 7 | "os/signal" 8 | "syscall" 9 | ) 10 | 11 | // fileLogger represents a logger that logs to a file 12 | type fileLogger struct { 13 | // pointer to internal logger 14 | *log.Logger 15 | 16 | // whether or not to log to stderr 17 | stderr bool 18 | } 19 | 20 | func newFileLogger(stderrPrefix, stdErrLogging bool, logPath string) *fileLogger { 21 | l = &fileLogger{stderr: stdErrLogging} 22 | if stderrPrefix == false { 23 | log.SetFlags(0) 24 | } else { 25 | log.SetFlags(log.Ldate | log.Ltime) 26 | } 27 | go func() { 28 | sigs := make(chan os.Signal, 1) 29 | signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) 30 | l.printf("%s got signal %s; terminating", globalPrefix, <-sigs) 31 | os.Exit(0) 32 | }() 33 | if logPath != "" { 34 | logFile, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644) 35 | if err != nil { 36 | log.Fatal(err) 37 | } 38 | l.Logger = log.New(logFile, "cserver: ", 3) 39 | } 40 | return l 41 | } 42 | 43 | // first checks to make sure file exists 44 | // then prints arguments to logger as fmt.Print 45 | func (l *fileLogger) print(v ...interface{}) { 46 | if l.Logger != nil { 47 | l.Print(v...) 48 | } 49 | if l.stderr == true { 50 | log.Print(v...) 51 | } 52 | } 53 | 54 | // first checks to make sure file exists 55 | // then prints arguments to logger as fmt.Printf 56 | func (l fileLogger) printf(format string, v ...interface{}) { 57 | if l.Logger != nil { 58 | l.Printf(format, v...) 59 | } 60 | if l.stderr == true { 61 | log.Printf(format, v...) 62 | } 63 | } 64 | 65 | // first checks to make sure file exists 66 | // then prints arguments to logger as fmt.Println 67 | func (l *fileLogger) println(v ...interface{}) { 68 | if l.Logger != nil { 69 | l.Println(v...) 70 | } 71 | if l.stderr == true { 72 | log.Println(v...) 73 | } 74 | } 75 | 76 | // fatal is equal to l.print followed by a call to os.Exit(1) 77 | func (l *fileLogger) fatal(v ...interface{}) { 78 | l.print(v...) 79 | os.Exit(1) 80 | } 81 | 82 | // fatalf is equal to l.printf followed by a call to os.Exit(1) 83 | func (l *fileLogger) fatalf(format string, v ...interface{}) { 84 | l.printf(format, v...) 85 | os.Exit(1) 86 | } 87 | 88 | // fatalln is equal to l.Println followed by a call to os.Exit(1) 89 | func (l *fileLogger) fatalln(v ...interface{}) { 90 | l.println(v...) 91 | os.Exit(1) 92 | } 93 | 94 | // panic is equal to l.print followed by a call to panic 95 | func (l *fileLogger) panic(v ...interface{}) { 96 | s := fmt.Sprint(v...) 97 | l.print(s) 98 | panic(s) 99 | } 100 | 101 | // panicf is equal to l.printf followed by a call to panic 102 | func (l *fileLogger) panicf(format string, v ...interface{}) { 103 | s := fmt.Sprintf(format, v...) 104 | l.printf(s) 105 | panic(s) 106 | } 107 | 108 | // panicln is equal to l.println followed by a call to panic 109 | func (l *fileLogger) panicln(v ...interface{}) { 110 | s := fmt.Sprintln(v...) 111 | l.println(s) 112 | panic(s) 113 | } 114 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "log" 6 | "os" 7 | "path/filepath" 8 | "sync" 9 | ) 10 | 11 | // logger is the global fileLogger 12 | var l *fileLogger 13 | 14 | const globalPrefix = "global" 15 | 16 | // main does initialization and launches all the nodes 17 | // first it reads the config file into a TunneLS struct 18 | // next if a LogPath is provided it sets up logging 19 | // finally it launches all the nodes and waits for them to end 20 | func main() { 21 | log.SetFlags(0) 22 | // read and parse config file 23 | path := flag.String("c", "/usr/local/etc/TunneLS/config.json", "path to configuration file") 24 | flag.Parse() 25 | tls := new(TunneLS) 26 | tls.parseFile(*path) 27 | dir, _ := filepath.Split(*path) 28 | if dir != "" { 29 | if err := os.Chdir(dir); err != nil { 30 | log.Fatalf("%s %s", globalPrefix, err) 31 | } 32 | } 33 | l = newFileLogger(tls.StderrPrefix, tls.StderrLogging, tls.LogPath) 34 | var wg sync.WaitGroup 35 | wg.Add(len(tls.Nodes)) 36 | // launch nodes and wait for their return 37 | for _, n := range tls.Nodes { 38 | // add space to non empty names 39 | if n.Name != "" { 40 | n.Name = " " + n.Name 41 | } 42 | go n.run(&wg) 43 | } 44 | wg.Wait() 45 | l.printf("%s too many errors, terminating", globalPrefix) 46 | } 47 | -------------------------------------------------------------------------------- /node.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "crypto/rand" 5 | "crypto/tls" 6 | "crypto/x509" 7 | "fmt" 8 | "io" 9 | "io/ioutil" 10 | "net" 11 | "strings" 12 | "sync" 13 | "time" 14 | ) 15 | 16 | // node implements one end of a TunneLS tunnel 17 | type node struct { 18 | // name for logging 19 | Name string 20 | 21 | // node mode 22 | Mode string 23 | 24 | // listen address 25 | Accept string 26 | 27 | // dial address 28 | Connect string 29 | 30 | // Unencrypted connections can be made by the server side 31 | // to a UNIX domain socket at ConnectPath if this value is 32 | // provided. 33 | ConnectPath string 34 | 35 | // path to ca 36 | CA string 37 | 38 | // path to certificate chain presented to other side of the connection 39 | Cert string 40 | 41 | // path to key for certificate 42 | Key string 43 | 44 | // Duration for sleep after network error in seconds, default is 15 45 | Timeout time.Duration 46 | 47 | // interval between session ticket key rotations in seconds, default is 28800 or 8 hours 48 | SessionTicketKeyRotationInterval time.Duration 49 | 50 | // tcp keep alive interval in seconds, default is 15 51 | TCPKeepAliveInterval time.Duration 52 | 53 | // ciphers to use 54 | Ciphers []string 55 | 56 | // type of client authentication to use 57 | ClientAuth string 58 | 59 | // path to CRL for client-authentication 60 | CRL string 61 | 62 | // tls configuration 63 | tlsConfig *tls.Config 64 | 65 | // listen on Accept address 66 | listen func() (net.Listener, error) 67 | 68 | // dials the Connect address 69 | dial func() (net.Conn, error) 70 | 71 | // controls logging the actual writing/reading of data 72 | LogData bool 73 | 74 | // insecurely connect 75 | InsecureSkipVerify bool 76 | } 77 | 78 | func (n *node) parseFields() { 79 | // get real time.Duration for time fields 80 | n.Timeout *= time.Second 81 | n.SessionTicketKeyRotationInterval *= time.Second 82 | n.TCPKeepAliveInterval *= time.Second 83 | if n.Ciphers != nil { 84 | n.tlsConfig.CipherSuites = n.parseCiphers() 85 | } 86 | } 87 | 88 | // set defaults for time fields 89 | func (n *node) setDefaults() { 90 | if n.Timeout == 0 { 91 | n.Timeout = 15 92 | } 93 | if n.SessionTicketKeyRotationInterval == 0 { 94 | n.SessionTicketKeyRotationInterval = 28800 95 | } 96 | if n.TCPKeepAliveInterval == 0 { 97 | n.TCPKeepAliveInterval = 15 98 | } 99 | // set mutual TLSConfig fields 100 | n.tlsConfig = new(tls.Config) 101 | n.tlsConfig.MinVersion = tls.VersionTLS11 102 | n.tlsConfig.NextProtos = []string{"http/1.1"} 103 | } 104 | 105 | // initialize and then run the node according to its mode 106 | // also set some mutual TLSConfig parameters 107 | func (n *node) run(wg *sync.WaitGroup) { 108 | n.logln("initializing") 109 | defer func() { 110 | if r := recover(); r != nil { 111 | n.logln(r) 112 | } 113 | n.logln("exiting") 114 | wg.Done() 115 | }() 116 | // you can use 5000 as a port instead of :5000 117 | if !strings.Contains(n.Accept, ":") { 118 | n.Accept = ":" + n.Accept 119 | } 120 | if !strings.Contains(n.Connect, ":") { 121 | n.Connect = ":" + n.Connect 122 | } 123 | n.setDefaults() 124 | n.parseFields() 125 | if n.Cert != "" { 126 | var err error 127 | n.tlsConfig.Certificates = make([]tls.Certificate, 1) 128 | n.tlsConfig.Certificates[0], err = tls.LoadX509KeyPair(n.Cert, n.Key) 129 | if err != nil { 130 | panic(err) 131 | } 132 | } 133 | switch strings.ToLower(n.Mode) { 134 | case "server": 135 | n.server() 136 | case "client": 137 | n.client() 138 | default: 139 | panic("invalid mode") 140 | } 141 | } 142 | 143 | func (n *node) readCAIntoPool() (pool *x509.CertPool) { 144 | if n.CA != "" { 145 | ca, err := ioutil.ReadFile(n.CA) 146 | if err != nil { 147 | panic(err) 148 | } 149 | pool = x509.NewCertPool() 150 | ok := pool.AppendCertsFromPEM(ca) 151 | if !ok { 152 | panic("could not append cert to RootCAs pool") 153 | } 154 | } 155 | return 156 | } 157 | 158 | func (n *node) parseCiphers() []uint16 { 159 | var ( 160 | ok bool 161 | c = make([]uint16, len(n.Ciphers)) 162 | ) 163 | for i, s := range n.Ciphers { 164 | c[i], ok = ciphers[s] 165 | if !ok { 166 | panic(fmt.Sprintf("%s is not a valid cipher", s)) 167 | } 168 | } 169 | return c 170 | } 171 | 172 | var clientAuthTypes = map[string]tls.ClientAuthType{ 173 | "NoClientCert": tls.NoClientCert, 174 | "RequestClientCert": tls.RequestClientCert, 175 | "RequireAnyClientCert": tls.RequireAnyClientCert, 176 | "VerifyClientCertIfGiven": tls.VerifyClientCertIfGiven, 177 | "RequireAndVerifyClientCert": tls.RequireAndVerifyClientCert, 178 | } 179 | 180 | func (n *node) parseClientAuthType() tls.ClientAuthType { 181 | ca, ok := clientAuthTypes[n.ClientAuth] 182 | if !ok { 183 | panic(fmt.Sprintf("%s is not a valid client authentication type", n.ClientAuth)) 184 | } 185 | return ca 186 | } 187 | 188 | func (n *node) initializeSessionTicketKeyRotation() { 189 | updateKey := func(key *[32]byte) { 190 | if _, err := rand.Read((*key)[:]); err != nil { 191 | n.logln(err) 192 | n.logln("cannot create new session ticket key") 193 | } 194 | } 195 | keys := make([][32]byte, 3) 196 | updateKey(&keys[0]) 197 | updateKey(&keys[1]) 198 | updateKey(&keys[2]) 199 | go func() { 200 | for { 201 | n.tlsConfig.SetSessionTicketKeys(keys) 202 | n.logf("session ticket key rotation sleeping for %vs", float64(n.SessionTicketKeyRotationInterval/time.Second)) 203 | time.Sleep(n.SessionTicketKeyRotationInterval) 204 | n.logln("updating session ticket rotation keys") 205 | keys[2] = keys[1] 206 | keys[1] = keys[0] 207 | updateKey(&keys[0]) 208 | } 209 | }() 210 | } 211 | 212 | // run the node as a server 213 | // accept TLS TCP and dial plain TCP 214 | // then copying all data between the two connections 215 | func (n *node) server() { 216 | if n.ClientAuth != "" { 217 | n.tlsConfig.ClientAuth = n.parseClientAuthType() 218 | } 219 | n.tlsConfig.PreferServerCipherSuites = true 220 | n.tlsConfig.ClientCAs = n.readCAIntoPool() 221 | n.initializeSessionTicketKeyRotation() 222 | n.listen = func() (tlsLn net.Listener, err error) { 223 | ln, err := net.Listen("tcp", n.Accept) 224 | if err != nil { 225 | return nil, err 226 | } 227 | tlsLn = tls.NewListener(tcpKeepAliveListener{ln.(*net.TCPListener), n.TCPKeepAliveInterval}, n.tlsConfig) 228 | if n.CRL != "" { 229 | tlsLn = &crlListener{tlsLn, n} 230 | } 231 | return 232 | } 233 | 234 | if n.ConnectPath != "" { 235 | n.dial = func() (c net.Conn, err error) { 236 | return net.Dial("unix", n.ConnectPath) 237 | } 238 | } else { 239 | n.dial = func() (c net.Conn, err error) { 240 | d := &net.Dialer{KeepAlive: n.TCPKeepAliveInterval} 241 | return d.Dial("tcp", n.Connect) 242 | } 243 | } 244 | n.listenAndServe() 245 | } 246 | 247 | // runs the node as a client 248 | // accept plain TCP and dial TLS TCP 249 | // then copying all data between the two connections 250 | func (n *node) client() { 251 | host, _, err := net.SplitHostPort(n.Connect) 252 | if err != nil { 253 | panic(err) 254 | } 255 | if host == "" { 256 | host = "localhost" 257 | } 258 | n.tlsConfig.ServerName = host 259 | n.tlsConfig.RootCAs = n.readCAIntoPool() 260 | n.tlsConfig.InsecureSkipVerify = n.InsecureSkipVerify 261 | n.dial = func() (net.Conn, error) { 262 | d := &net.Dialer{KeepAlive: n.TCPKeepAliveInterval} 263 | return tls.DialWithDialer(d, "tcp", n.Connect, n.tlsConfig) 264 | } 265 | n.listen = func() (net.Listener, error) { 266 | ln, err := net.Listen("tcp", n.Accept) 267 | if err != nil { 268 | return nil, err 269 | } 270 | return tcpKeepAliveListener{ln.(*net.TCPListener), n.TCPKeepAliveInterval}, nil 271 | } 272 | n.listenAndServe() 273 | } 274 | 275 | // listenAndServeErr accepts connections on n.Accept 276 | // it then dials n.Connect and copies all data between the two 277 | // connections. Listening is done with the n.Listen function 278 | // and dialing is done with the n.Dial function. Any errors are returned 279 | func (n *node) listenAndServeErr() error { 280 | ln, err := n.listen() 281 | if err != nil { 282 | return err 283 | } 284 | defer ln.Close() 285 | n.logln("listening on", n.Accept) 286 | for { 287 | c1, err := ln.Accept() 288 | if err != nil { 289 | return err 290 | } 291 | n.logln("connection from", c1.RemoteAddr()) 292 | go n.dialAndTunnel(c1) 293 | } 294 | } 295 | 296 | // ListenAndServe creates a loop to call listenAndServeErr and then 297 | // on the return of errors timeout and restart 298 | func (n *node) listenAndServe() { 299 | for { 300 | err := n.listenAndServeErr() 301 | n.logln(err) 302 | n.logf("sleeping for %vs", float64(n.Timeout/time.Second)) 303 | time.Sleep(n.Timeout) 304 | } 305 | } 306 | 307 | // takes the first conn, dials the connection address, then creates a tunnel between the two 308 | func (n *node) dialAndTunnel(c1 net.Conn) { 309 | n.logln("connecting to", n.Connect) 310 | c2, err := n.dial() 311 | if err != nil { 312 | c1.Close() 313 | n.logln(err) 314 | return 315 | } 316 | n.tunnel(c1, c2) 317 | } 318 | 319 | // creates the tunnel between c1 and c2 320 | func (n *node) tunnel(c1, c2 net.Conn) { 321 | n.logf("beginning tunnel from %s to %s then %s to %s", c1.RemoteAddr(), c1.LocalAddr(), c2.LocalAddr(), c2.RemoteAddr()) 322 | var wg sync.WaitGroup 323 | wg.Add(2) 324 | go n.copy(c1, c2, wg) 325 | go n.copy(c2, c1, wg) 326 | wg.Wait() 327 | n.logf("closed tunnel from %s to %s then %s to %s", c1.RemoteAddr(), c1.LocalAddr(), c2.LocalAddr(), c2.RemoteAddr()) 328 | } 329 | 330 | // copy copies all data from src to dst 331 | // then calls Done() on the copyWG to allow 332 | // the calling routine to stop waiting followed by closing dst 333 | func (n *node) copy(dst io.WriteCloser, src io.Reader, wg sync.WaitGroup) { 334 | defer dst.Close() 335 | var f func(io.Writer, io.Reader) (int64, error) 336 | if n.LogData { 337 | f = n.copyBuffer 338 | } else { 339 | f = io.Copy 340 | } 341 | if _, err := f(dst, src); err != nil { 342 | n.logln(err) 343 | } 344 | wg.Done() 345 | } 346 | 347 | func (n *node) copyBuffer(dst io.Writer, src io.Reader) (written int64, err error) { 348 | // If the reader has a WriteTo method, use it to do the copy. 349 | // Avoids an allocation and a copy. 350 | if wt, ok := src.(io.WriterTo); ok { 351 | return wt.WriteTo(dst) 352 | } 353 | // Similarly, if the writer has a ReadFrom method, use it to do the copy. 354 | if rt, ok := dst.(io.ReaderFrom); ok { 355 | return rt.ReadFrom(src) 356 | } 357 | buf := make([]byte, 32*1024) 358 | for { 359 | nr, er := src.Read(buf) 360 | n.logf("read %d bytes", nr) 361 | if nr > 0 { 362 | nw, ew := dst.Write(buf[0:nr]) 363 | if nw > 0 { 364 | written += int64(nw) 365 | } 366 | if ew != nil { 367 | err = ew 368 | break 369 | } 370 | if nr != nw { 371 | err = io.ErrShortWrite 372 | break 373 | } 374 | n.logf("written %d bytes", nw) 375 | } 376 | if er == io.EOF { 377 | break 378 | } 379 | if er != nil { 380 | err = er 381 | break 382 | } 383 | } 384 | return written, err 385 | } 386 | 387 | // logln logs to the global fileLogger as global 388 | // arguments are handled same as fmt.Println 389 | func (n *node) logln(v ...interface{}) { 390 | l.println(append([]interface{}{n.Mode + n.Name}, v...)...) 391 | } 392 | 393 | // logf logs to the global fileLogger as global 394 | // arguments are handled same as fmt.Printf 395 | func (n *node) logf(format string, v ...interface{}) { 396 | l.printf(n.Mode+n.Name+" "+format, v...) 397 | } 398 | 399 | var ciphers = map[string]uint16{ 400 | "FALLBACK_SCSV": tls.TLS_FALLBACK_SCSV, 401 | "RSA_WITH_RC4_128_SHA": tls.TLS_RSA_WITH_RC4_128_SHA, 402 | "RSA_WITH_AES_128_CBC_SHA": tls.TLS_RSA_WITH_AES_128_CBC_SHA, 403 | "RSA_WITH_AES_256_CBC_SHA": tls.TLS_RSA_WITH_AES_256_CBC_SHA, 404 | "ECDHE_RSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_RSA_WITH_RC4_128_SHA, 405 | "ECDHE_ECDSA_WITH_RC4_128_SHA": tls.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA, 406 | "RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_RSA_WITH_3DES_EDE_CBC_SHA, 407 | "ECDHE_RSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, 408 | "ECDHE_RSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, 409 | "ECDHE_ECDSA_WITH_AES_128_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA, 410 | "ECDHE_ECDSA_WITH_AES_256_CBC_SHA": tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, 411 | "ECDHE_RSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 412 | "ECDHE_RSA_WITH_AES_256_GCM_SHA256": tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 413 | "ECDHE_ECDSA_WITH_AES_128_GCM_SHA256": tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 414 | "ECDHE_ECDSA_WITH_AES_256_GCM_SHA384": tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, 415 | "ECDHE_RSA_WITH_3DES_EDE_CBC_SHA": tls.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, 416 | } 417 | 418 | // tcpKeepAliveListener wraps a TCPListener to 419 | // activate TCP keep alive on every accepted connection 420 | type tcpKeepAliveListener struct { 421 | // inner TCPlistener 422 | *net.TCPListener 423 | 424 | // interval between keep alives to set on accepted conns 425 | keepAliveInterval time.Duration 426 | } 427 | 428 | // Accept a TCP Conn and enable TCP keep alive 429 | func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) { 430 | tc, err := ln.AcceptTCP() 431 | if err != nil { 432 | return 433 | } 434 | err = tc.SetKeepAlive(true) 435 | if err != nil { 436 | return 437 | } 438 | err = tc.SetKeepAlivePeriod(ln.keepAliveInterval) 439 | if err != nil { 440 | return 441 | } 442 | return tc, nil 443 | } 444 | 445 | type crlListener struct { 446 | net.Listener 447 | n *node 448 | } 449 | 450 | func (ln *crlListener) Accept() (net.Conn, error) { 451 | listen: 452 | for { 453 | c, err := ln.Listener.Accept() 454 | if err != nil { 455 | return nil, err 456 | } 457 | clrRaw, err := ioutil.ReadFile(ln.n.CRL) 458 | if err != nil { 459 | return nil, err 460 | } 461 | clr, err := x509.ParseCRL(clrRaw) 462 | if err != nil { 463 | return nil, err 464 | } 465 | tlsC := c.(*tls.Conn) 466 | err = tlsC.Handshake() 467 | if err != nil { 468 | ln.n.logln(err) 469 | continue 470 | } 471 | certs := tlsC.ConnectionState().PeerCertificates 472 | cert := certs[len(certs)-1] 473 | for _, revoked := range clr.TBSCertList.RevokedCertificates { 474 | if cert.SerialNumber.Cmp(revoked.SerialNumber) == 0 { 475 | ln.n.logf("got revoked %s certificate from %s", cert.Subject.CommonName, c.RemoteAddr()) 476 | c.Close() 477 | continue listen 478 | } 479 | } 480 | ln.n.logf("accepted %s certificate from %s", cert.Subject.CommonName, c.RemoteAddr()) 481 | return c, err 482 | } 483 | } 484 | -------------------------------------------------------------------------------- /screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nhooyr/TunneLS/fffca6c379f42731b8e324d710cd80d6018e44ea/screenshot.png -------------------------------------------------------------------------------- /tls/cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICQjCCAcmgAwIBAgIJANtgsOApjQG5MAoGCCqGSM49BAMCMH4xCzAJBgNVBAYT 3 | AkNBMRkwFwYDVQQIDBBNZVN0YXRlL1Byb3ZpbmNlMQ8wDQYDVQQHDAZNZUNpdHkx 4 | DjAMBgNVBAoMBWplc3VzMRIwEAYDVQQDDAlsb2NhbGhvc3QxHzAdBgkqhkiG9w0B 5 | CQEWEGluZm9AZXhhbXBsZS5jb20wHhcNMTUwOTAyMjI1NDM1WhcNMTUxMDAyMjI1 6 | NDM1WjB+MQswCQYDVQQGEwJDQTEZMBcGA1UECAwQTWVTdGF0ZS9Qcm92aW5jZTEP 7 | MA0GA1UEBwwGTWVDaXR5MQ4wDAYDVQQKDAVqZXN1czESMBAGA1UEAwwJbG9jYWxo 8 | b3N0MR8wHQYJKoZIhvcNAQkBFhBpbmZvQGV4YW1wbGUuY29tMHYwEAYHKoZIzj0C 9 | AQYFK4EEACIDYgAETjWaXz7bdFwLVFKTMO9o+3WL2Jw2GQN2Uj5FNbFVUexUSKZ0 10 | 9NS7sV/G/bQOzN+p0je6XsppyDqNXtM2DU58Cmf90uT6l2UEX5fBKYcR2rEPtiph 11 | vZTChUiZIcUe84YboxMwETAPBgNVHRMBAf8EBTADAQH/MAoGCCqGSM49BAMCA2cA 12 | MGQCMADUukDG/2Jb7SkyVJBso7sS4jYJyHwIVNAELX+zjgYoSomlIP4m/NR3U2d/ 13 | POOI6QIwcxy6c5N4v7l7sJRzxckZeafCE4bGxzS0AQ1pTCoCY6V2wvspr5gXdNsD 14 | HSNKMqKW 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /tls/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PARAMETERS----- 2 | BgUrgQQAIg== 3 | -----END EC PARAMETERS----- 4 | -----BEGIN EC PRIVATE KEY----- 5 | MIGkAgEBBDBitafm7xLCTg0QpEg766EiqF63TrZnl5lKjwbUiHmSrwm2rrvJKX5/ 6 | rJz4SdPIEnigBwYFK4EEACKhZANiAARONZpfPtt0XAtUUpMw72j7dYvYnDYZA3ZS 7 | PkU1sVVR7FRIpnT01LuxX8b9tA7M36nSN7peymnIOo1e0zYNTnwKZ/3S5PqXZQRf 8 | l8EphxHasQ+2KmG9lMKFSJkhxR7zhhs= 9 | -----END EC PRIVATE KEY----- 10 | -------------------------------------------------------------------------------- /tls/openssl.cnf: -------------------------------------------------------------------------------- 1 | [ req ] 2 | default_bits = 4096 3 | default_keyfile = key.pem 4 | default_md = sha256 5 | distinguished_name = req_distinguished_name 6 | prompt = no 7 | x509_extensions = v3_ca 8 | encrypt_key = no 9 | 10 | [ req_distinguished_name ] 11 | C = CA #2 letter country code 12 | ST = MeState #state/province 13 | L = MeCity #city 14 | O = MeCompanyName #company name 15 | CN = localhost #common.name, eg full name of server, etc www.example.com 16 | emailAddress = info@example.com 17 | 18 | [ v3_ca ] 19 | basicConstraints = critical, CA:true 20 | #subjectAltName = @alt_names 21 | 22 | #[ alt_names ] 23 | #DNS.1 = COMMON.NAME 24 | #DNS.2 = *.SECOND.NAME 25 | --------------------------------------------------------------------------------