├── .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 |
--------------------------------------------------------------------------------