├── LICENSE ├── README.md ├── alpn.30443.conf └── mul.js /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 neil 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # nginx-multiplexer 2 | TCP and/or TLS-alpn multiplexer just by nginx conf 3 | 4 | You don't need to use any other tools like sslh or sshttp. 5 | 6 | 7 | ## 1. TCP multiplexer. 8 | Using nginx script to run multiple tcp protocols at the same port: http, ssl, and ssh. 9 | 10 | 11 | ``` 12 | # In file: /etc/nginx/nginx.conf 13 | 14 | #load nginx script module 15 | load_module modules/ngx_stream_js_module.so; 16 | load_module modules/ngx_http_js_module.so; 17 | 18 | http { 19 | #The real http server listen at 80 20 | listen 80; 21 | .... 22 | 23 | #The real https server listens at 8443 24 | listen 8443 ssl; 25 | .... 26 | } 27 | 28 | 29 | stream { 30 | 31 | #Inport the js file first 32 | js_import /etc/nginx/stream.d/mul.js; 33 | 34 | 35 | server { 36 | # Them multiplexer listens at the public 443 port 37 | listen 443; 38 | resolver 1.1.1.1 8.8.8.8 valid=300s; 39 | 40 | set $upstream "127.0.0.1:8443"; #default to loacalhost 8443 41 | 42 | js_preread mul.preread; 43 | 44 | proxy_pass $upstream; 45 | } 46 | 47 | } 48 | 49 | 50 | ``` 51 | 52 | 53 | ## 2. TLS-alpn multiplexer 54 | See: [alpn.30443.conf](alpn.30443.conf) 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /alpn.30443.conf: -------------------------------------------------------------------------------- 1 | 2 | #See: https://superuser.com/questions/1135208/can-nginx-serve-ssh-and-https-at-the-same-time-on-the-same-port 3 | 4 | 5 | 6 | 7 | server { 8 | listen 30443; 9 | listen unix:/etc/nginx/socks/alpn.30443.sock; 10 | ssl_preread on; 11 | proxy_pass $ssl_multiplexer; 12 | # proxy_protocol on; 13 | # set_real_ip_from 172.18.0.0/32; 14 | } 15 | 16 | 17 | 18 | 19 | 20 | map $ssl_preread_alpn_protocols $ssl_multiplexer { 21 | 22 | default 127.0.0.1:443; # default to nginx 23 | 24 | "h2" unix:/etc/nginx/socks/alpn.h2.sock; #for h2 alpn 25 | 26 | 27 | "xmpp-server" unix:/etc/nginx/socks/alpn.xmpp.sock; xmpp-server 28 | 29 | "h3" unix:/etc/nginx/socks/alpn.h3.sock; #for h3 alpn, I use for ssh 30 | 31 | } 32 | 33 | # ssl termination for c2s connections 34 | server { 35 | listen unix:/etc/nginx/socks/alpn.h2.sock ssl ; 36 | ssl_certificate /etc/nginx/certs/default.crt; 37 | ssl_certificate_key /etc/nginx/certs/default.key; 38 | proxy_ssl off; 39 | proxy_pass localhost:20001; 40 | } 41 | 42 | # ssl termination for s2s connections 43 | server { 44 | listen unix:/etc/nginx/socks/alpn.xmpp.sock ssl ;#proxy_protocol; 45 | # ... <- tls keys and options here 46 | ssl_certificate /etc/nginx/certs/default.crt; 47 | ssl_certificate_key /etc/nginx/certs/default.key; 48 | 49 | proxy_ssl off; 50 | proxy_pass localhost:5269; 51 | } 52 | 53 | # ssl termination for ssh connections 54 | server { 55 | listen unix:/etc/nginx/socks/alpn.h3.sock ssl ;#proxy_protocol; 56 | ssl_certificate /etc/nginx/certs/default.crt; 57 | ssl_certificate_key /etc/nginx/certs/default.key; 58 | 59 | proxy_ssl off; 60 | proxy_pass localhost:22; 61 | } 62 | 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /mul.js: -------------------------------------------------------------------------------- 1 | 2 | 3 | //https://github.com/nginx/njs-examples#choosing-upstream-in-stream-based-on-the-underlying-protocol-stream-detect-http 4 | 5 | 6 | 7 | 8 | var upstreams = { 9 | 10 | //Use any valid domains(eg: www.google.com), or ip(12.35.26.3) or "127.0.0.1" 11 | //Or use unix socks: unix:/etc/nginx/socks/alpn.8443.sock 12 | //but don't use string "localhost" here 13 | 14 | ssl: "127.0.0.1:8443" 15 | , ssh: "127.0.0.1:22" 16 | , http: "127.0.0.1:80" 17 | , default: "127.0.0.1:20001" //other tcp connections 18 | 19 | }; 20 | 21 | 22 | ////////////////////////////////// don't modify the code bellow unless you know 23 | 24 | function preread(s) { 25 | 26 | s.on('upstream', function(data, flags) { 27 | 28 | if (data.length) { 29 | 30 | if(upstreams.ssl && data[0] == 0x16 && data[1] == 0x03 && isSSL(data)) { 31 | s.variables.upstream = upstreams.ssl; 32 | s.done(); 33 | return; 34 | } 35 | 36 | if(upstreams.ssh && data[0] == 0x53 && data[1] == 0x53 && data[2] == 0x48 && data[3] == 0x2d && isSSH(data)) { 37 | s.variables.upstream = upstreams.ssh; 38 | s.done(); 39 | return; 40 | 41 | } 42 | 43 | if(upstreams.http && isHTTP(data)) { 44 | s.variables.upstream = upstreams.http; 45 | s.done(); 46 | return; 47 | } 48 | 49 | if(upstreams.default) { 50 | s.variables.upstream = upstreams.default; 51 | s.done(); 52 | return; 53 | } 54 | 55 | 56 | s.done(); 57 | } 58 | 59 | 60 | 61 | if(flags.last) { 62 | s.done(); 63 | } 64 | 65 | }); 66 | 67 | 68 | } 69 | 70 | 71 | 72 | function isSSL(data) { 73 | return true; //no more checks for now 74 | 75 | } 76 | 77 | function isSSH(data) { 78 | return true; //no more checks for now 79 | 80 | 81 | } 82 | 83 | function isHTTP(data) { 84 | switch(data[0]) { 85 | case 0x47: // 'G' 86 | { 87 | //'E' 'T' 88 | return data[1] == 0x45 && data[2] == 0x54 && data[3] == 0x20; 89 | } 90 | case 0x50: // 'P' 91 | { 92 | //OST 93 | if( data[1] == 0x4f && data[2] == 0x53 && data[3] == 0x54 && data[4] == 0x20) { 94 | return true; 95 | } 96 | //UT 97 | return data[1] == 0x55 && data[2] == 0x54 && data[3] == 0x20; 98 | 99 | 100 | } 101 | case 0x44: // 'D' 102 | { 103 | //ELETE 104 | if( data[1] == 0x45 && data[2] == 0x4c && data[3] == 0x45 && data[4] == 0x54 && data[5] == 0x45 && data[6] == 0x20) { 105 | return true; 106 | } 107 | 108 | } 109 | case 0x55: // 'U' 110 | { 111 | //PDATE 112 | if( data[1] == 0x50 && data[2] == 0x44 && data[3] == 0x41 && data[4] == 0x54 && data[5] == 0x45 && data[6] == 0x20) { 113 | return true; 114 | } 115 | 116 | } 117 | default: { 118 | return false; 119 | } 120 | 121 | 122 | } 123 | 124 | } 125 | 126 | 127 | 128 | 129 | 130 | 131 | export default { preread } --------------------------------------------------------------------------------