2
2
mirror of https://git.coolaj86.com/coolaj86/telebit.js.git synced 2025-04-22 12:00:36 +00:00

Compare commits

..

133 Commits

Author SHA1 Message Date
d715d5a9d9 v0.12.0 2018-06-07 01:43:21 -06:00
774e8a26da notify user to check email 2018-06-07 01:42:10 -06:00
388e2f7a58 update access token 2018-06-07 01:27:56 -06:00
536afb2ad5 show name of command which times out 2018-06-07 00:45:42 -06:00
274c527768 www.telebit.cloud => telebit.cloud 2018-06-07 00:11:06 -06:00
553c385e34 move intentional user messages and client data to CLI/UI 2018-06-07 00:08:26 -06:00
3a8f5fee4e add User Agent info 2018-06-06 03:24:14 -06:00
021a44d47a old auth works again 2018-06-06 01:01:56 -06:00
b9779715f8 send auth info 2018-06-05 02:21:12 -06:00
7ec69f8d05 a little cleanup 2018-06-05 02:04:32 -06:00
0c705061a2 note log checking 2018-06-05 01:27:36 -06:00
82fb0ee8ac read relay prompt variable correctly 2018-06-05 01:22:10 -06:00
ac36474b9b properly use mktemp 2018-06-05 01:13:38 -06:00
b9065540db update uninstaller 2018-06-05 01:07:32 -06:00
a0c32662a0 use nano as fallback editor 2018-06-05 01:02:43 -06:00
36f73c1051 use only editor basename, not full path 2018-06-05 01:01:31 -06:00
e831774c34 fix bad comment 2018-06-05 00:55:59 -06:00
6198223b46 add uninstall script 2018-06-05 00:53:20 -06:00
4ec1f3b30a fix sudo_cmd whitespace, editor detection, other things... 2018-06-05 00:46:06 -06:00
570c64f104 fix conditional 2018-06-05 00:09:24 -06:00
ffc045ab43 update uninstall instruction 2018-06-05 00:05:48 -06:00
99d1e7cb3d add uninstall instructions 2018-06-05 00:04:05 -06:00
a43934a405 update docs 2018-06-04 23:50:02 -06:00
e96a52212f read prompt from /dev/tty 2018-06-04 23:23:59 -06:00
9d920bc6f9 swap sudo with $sudo_cmd 2018-06-04 23:02:50 -06:00
0af3e60b63 used TELEBIT_PATH/usr/share, replace 'vim' with 'edit', change sudo_cmd 2018-06-04 23:00:28 -06:00
082866f300 Update 'README.md' 2018-06-05 04:50:39 +00:00
b0444ec575 v0.11.0 2018-06-04 18:33:47 -06:00
08348fae0c update cli options 2018-06-04 18:33:24 -06:00
6d85f12409 update docs a little bit 2018-06-04 18:32:18 -06:00
f2f772a645 fix example config 2018-06-04 18:29:36 -06:00
5e9a852375 change default branch 2018-06-04 18:25:35 -06:00
9736dfe92b update config examples 2018-06-04 18:23:33 -06:00
da78847d91 show /opt, not /etc 2018-06-05 00:16:15 +00:00
e361c48353 whitespace fix 2018-06-05 00:14:49 +00:00
582fde8691 update symlink to system config 2018-06-05 00:13:03 +00:00
e21e7265e7 fix agreeTos config typo 2018-06-04 23:53:26 +00:00
a5a78400bf agree_tos is dependent on email 2018-06-04 23:52:22 +00:00
d931896a73 add agree_tos 2018-06-04 23:50:34 +00:00
05d317e443 fix echo newline expansion 2018-06-04 23:48:17 +00:00
0708357b86 add missing 'relay' config 2018-06-04 23:43:11 +00:00
7e7f419eb8 use default relay 2018-06-02 04:05:12 -06:00
aca4342dd1 fix config bug 2018-06-02 04:00:08 -06:00
cbfbc71edf seems to work with launchd 2018-06-02 03:57:38 -06:00
682f35f5a0 got further 2018-06-02 03:31:31 -06:00
a8b8e34c60 fixes 2018-06-02 03:30:13 -06:00
09d6aa6df3 rename installer 2018-06-02 03:05:17 -06:00
6748e81d5c fix remote require path 2018-06-02 02:59:53 -06:00
c01fbe0b63 lots up cleanup 2018-06-02 02:58:42 -06:00
0097735152 a place for apps 2018-06-02 02:28:58 -06:00
b453281a6b explain installer, update system services 2018-06-02 02:25:41 -06:00
986dc1c17f update installer 2018-06-01 13:14:59 -06:00
a5d3576ab6 echo tcp 2018-06-01 03:50:27 -06:00
65b77f994a update grant message 2018-06-01 03:34:55 -06:00
182c4d8888 update grant message 2018-06-01 03:13:04 -06:00
33063d3efe put ssh detection on full auto 2018-06-01 02:45:38 -06:00
e85bd78905 ssh detection works 2018-06-01 02:10:27 -06:00
9239153681 ws 2018-06-01 01:36:49 -06:00
3a6df4db63 small refactor 2018-06-01 01:36:29 -06:00
7a08220838 cleanup 2018-05-31 14:48:12 -06:00
ef751e5b32 keep address, localPort (service), and encrypted 2018-05-31 05:24:58 -06:00
30d1ce89fe working cleanup 2018-05-31 04:10:47 -06:00
de1bea7b60 update systemd configs 2018-05-31 02:38:01 -06:00
3cd10fda0c refactor and upgrade to proxy-packer 2018-05-31 00:19:53 -06:00
37591d11c6 handle connection within telebit 2018-05-29 03:00:25 -06:00
6f343a875d accept given token or secret-generated token 2018-05-29 01:45:47 -06:00
a816e327e5 warn when no tokens given 2018-05-29 01:44:50 -06:00
945f77449d getting closer... 2018-05-27 05:02:19 -06:00
157983018b fix whitespace 2018-05-27 04:38:29 -06:00
f7685f8b6a rename lib file 2018-05-27 04:32:00 -06:00
e8c580d115 progress towards telebit 2018-05-27 04:26:34 -06:00
ca2e825fe7 merge 2018-05-27 01:59:43 -06:00
3b2e1771df moving to telebit 2018-05-27 01:58:37 -06:00
eeb6299268 Update 'README.md' 2018-05-15 07:50:37 +00:00
333b06c33f Update 'README.md' 2018-05-15 07:42:43 +00:00
26bfdd06a1 bump 2018-04-25 13:33:35 -06:00
b58cb25077 remove bad locked versions 2018-04-25 13:32:57 -06:00
f95ca1bdc0 update links 2018-04-24 01:54:39 +00:00
4b0172a456 update links 2018-04-24 01:54:21 +00:00
3f77cb16b3 update README.md 2018-02-14 23:27:15 -07:00
ef9fc9d46d update 2018-02-14 23:25:15 -07:00
dc11cbbd63 update urls 2017-11-10 16:43:06 -07:00
tigerbot
c433c6ccaf v0.10.3 2017-10-03 11:48:02 -06:00
tigerbot
2a7121f79f fixed issue with hanging promise on first appended token 2017-10-02 18:29:00 -06:00
tigerbot
dcdd4f8142 v0.10.2 2017-09-11 15:59:28 -06:00
tigerbot
f94df07219 improved throttle of connections based on websocket speed 2017-09-11 15:35:03 -06:00
tigerbot
ccaf1a517f improved some of the logs for pausing/resuming 2017-09-11 14:52:49 -06:00
tigerbot
e9c24efd5f implemented throttling when we buffer too much data 2017-09-08 10:54:47 -06:00
tigerbot
bbc3ffaf6d v0.10.1 2017-06-15 14:50:02 -06:00
tigerbot
7a00be681c fixed problem with burst appending tokens on start 2017-06-15 14:49:50 -06:00
tigerbot
6b57f6fae8 v0.10.0 2017-06-06 17:33:15 -06:00
tigerbot
2831d09a4d Merge branch 'side-channel' 2017-06-06 17:32:51 -06:00
tigerbot
a95f6ecead use .destroy if .end fails to close connection 2017-06-05 11:28:53 -06:00
tigerbot
e3fbd768a2 added support for wildcard domains 2017-06-05 11:20:58 -06:00
tigerbot
6ea903a3f1 added audience to the tokens we generate 2017-06-05 11:20:15 -06:00
tigerbot
635523b155 allow client to be created with no initial tokens 2017-05-25 13:46:09 -06:00
tigerbot
00fbd2c27f handled case where append is called with closed websocket 2017-04-28 15:28:21 -06:00
tigerbot
6901a4ef2d added handling for 'hello' from the server 2017-04-28 15:02:44 -06:00
tigerbot
425cf4bc24 changed adding tokens to work on reconnect 2017-04-28 10:57:48 -06:00
4f9ea342f5 use stream-pair, which actually does what we want 2017-04-28 03:03:07 +00:00
04f10090ea clarify myDuplex => reader, myDuplex2 => writer 2017-04-28 02:47:41 +00:00
tigerbot
dbf8832aa9 added ability to add/clear tokens on active websocket 2017-04-27 19:29:16 -06:00
tigerbot
160c591d7f v0.9.9 2017-04-27 18:39:18 -06:00
tigerbot
d8bf926123 gave more power to custom createConnection functions 2017-04-27 18:38:01 -06:00
tigerbot
5a3fe4468a suppressing log when trying to send on closing websocket 2017-04-26 17:37:52 -06:00
tigerbot
7a9d06e67b v0.9.8 2017-04-14 18:28:59 -06:00
tigerbot
43ea38523b exposed .end method on the wsclient 2017-04-14 16:27:25 -06:00
tigerbot
53185c1760 v0.9.7 2017-04-10 11:40:12 -06:00
tigerbot
913f36043a implemented timeout over websocket connection 2017-04-10 11:39:27 -06:00
tigerbot
76495e674d cleaned up the exit handling 2017-04-07 18:49:52 -06:00
tigerbot
35d5f0c568 changed expected behavior when closing connections 2017-04-07 18:01:47 -06:00
tigerbot
cbdff3e55b rearranged some of the handler functions 2017-04-07 17:38:55 -06:00
61a8d29c5e install via git 2017-04-05 16:36:01 -06:00
1784a13346 take out the trash 2017-04-05 16:33:00 -06:00
tigerbot
9eabc956dd v0.9.6 2017-04-03 15:15:30 -06:00
tigerbot
646cfedabe changed method for mapping services to local ports 2017-04-03 15:11:59 -06:00
ec245dff63 v0.9.5 2017-03-29 19:29:41 -06:00
1cbb594ef6 fix deps and commandline opts 2017-03-29 19:29:37 -06:00
a8d1448c08 v0.9.4 2017-03-29 17:58:41 -06:00
dcb150daa6 Merge branch 'v1' 2017-03-29 17:54:56 -06:00
c32c0bad05 v0.9.3 2017-03-29 17:54:52 -06:00
a09a3835ae Merge branch 'v1' 2017-03-29 13:36:38 -06:00
519911fa23 v0.9.2 2017-03-29 13:36:33 -06:00
993f04a824 fix deps typo 2017-03-29 13:36:29 -06:00
7f754688cc Merge branch 'v1' 2017-03-29 13:26:26 -06:00
c00e3085e3 v0.9.1 2017-03-29 13:26:20 -06:00
5719237a0c v0.9.1 2017-03-29 13:24:09 -06:00
97f731ebb8 allow undefined domains option 2017-03-29 13:19:52 -06:00
c9d83437e1 v0.9.0 2017-03-29 12:29:34 -06:00
65f090315b add domains option 2017-03-29 12:29:23 -06:00
5ee3a45cf8 v0.8.2 2017-03-28 15:37:40 -06:00
5ade07e224 get domains by device 2017-03-28 15:31:45 -06:00
4eb6a42bb8 add simple tunnel usage 2017-03-26 01:37:26 -06:00
43 changed files with 1142 additions and 5155 deletions

15
.gitignore vendored
View File

@ -1,19 +1,4 @@
node_modules.* node_modules.*
*.*.sw*
etc/acme/
bin/node
bin/npm
bin/npx
bin/telebit
bin/telebitd
bin/telebit_uninstall
usr/share/dist/Library/LaunchDaemons/cloud.telebit.remote.plist
usr/share/dist/etc/skel/Library/LaunchAgents/cloud.telebit.remote.plist
usr/share/dist/etc/systemd/system/telebit.service
usr/share/dist/etc/skel/.config/systemd/user/telebit.service
./etc/
./include/
./share/
# Logs # Logs
logs logs

212
README.md
View File

@ -1,14 +1,12 @@
# Telebit™ Remote | a [Root](https://rootprojects.org) project # Telebit™ Remote
Because friends don't let friends localhost™ Because friends don't let friends localhost™
| Sponsored by [ppl](https://ppl.family)
| **Telebit Remote** | **Telebit Remote**
| [Telebit Relay](https://git.coolaj86.com/coolaj86/telebit-relay.js) | [Telebit Relay](https://git.coolaj86.com/coolaj86/telebitd.js)
| [sclient](https://telebit.cloud/sclient)
| |
<img align="center" src="https://git.coolaj86.com/coolaj86/telebit.js/raw/branch/master/usr/share/docs/terminal-example-1.png">
Break out of localhost. Break out of localhost.
======= =======
@ -33,32 +31,41 @@ Features
Examples Examples
======== ========
You do this: ```
telebit --config /opt/telebit/etc/telebit.yml
```
curl -fsSL https://get.telebit.io | bash ```
Connect to your device by any of the following means:
You get this: SSH+HTTPS
ssh+https://young-grasshopper-37.telebit.cloud:443
ex: ssh -o ProxyCommand='openssl s_client -connect %h:%p -quiet' young-grasshopper-37.telebit.cloud -p 443
~/telebit http 3000 SSH
> Forwarding lucky-duck-42.telebit.cloud => localhost:3000 ssh://ssh.telebit.cloud:32852
ex: ssh ssh.telebit.cloud -p 32852
~/telebit http ~/sites/example.com/ TCP
> Serving ~/sites/example.com/ as lucky-duck-42.telebit.cloud tcp://tcp.telebit.cloud:32852
ex: netcat tcp.telebit.cloud 32852
And this: HTTPS
https://young-grasshopper-37.telebit.cloud
ex: curl https://young-grasshopper-37.telebit.cloud
```
~/telebit tcp 5050 <!-- TODO use some imagery
> Forwarding telebit.cloud:1337 => localhost:5050 ```
telebit http /path/to/root
And even this: telebit http 3000
telebit http /path/to/handler.js
~/telebit ssh auto telebit ssh 22
> Forwarding ssh telebit.cloud -p 1337 => localhost:22 telebit tcp 3000
> Forwarding ssh+https (openssl proxy) => localhost:22 telebit tcp echo
telebit tcp /path/to/handler.js
No privileged ports. No sudo. End-to-end encryption. ```
-->
Fastest way to test a site, share a file, and pair over ssh.
Install Install
======= =======
@ -69,150 +76,67 @@ Mac & Linux
Open Terminal and run this install script: Open Terminal and run this install script:
``` ```
curl -fsSL https://get.telebit.io | bash curl -fsSL https://get.telebit.cloud | bash
``` ```
<!-- <!--
``` ```
bash <( curl -fsSL https://get.telebit.io ) bash <( curl -fsSL https://get.telebit.cloud )
``` ```
<small> <small>
Note: **fish**, **zsh**, and other **non-bash** users should do this Note: **fish**, **zsh**, and other **non-bash** users should do this
``` ```
curl -fsSL https://get.telebit.io/ > get.sh; bash get.sh curl -fsSL https://get.telebit.cloud/ > get.sh; bash get.sh
``` ```
</small> </small>
--> -->
What does the installer do? Of course, feel free to inspect the install script before you run it: `curl -fsSL https://get.telebit.cloud`
* install Telebit Remote to `~/Applications/telebit/` This will install Telebit Remote to `/opt/telebit` and
* symlink the executable to `~/telebit` for convenience put a symlink to `/opt/telebit/bin/telebit.js` in `/usr/local/bin/telebit`
* create the appropriate system launcher file for convenience.
* `/etc/systemd/system/telebit.service`
* `~/Library/LaunchAgents/cloud.telebit.remote.plist`
* create local user config
* `~/.config/telebit/telebit.yml`
* `~/.local/share/telebit`
Of course, feel free to inspect it before you run it: `curl -fsSL https://get.telebit.io`
**You can customize the installation**: **You can customize the installation**:
```bash ```bash
export NODEJS_VER=v10.2 # v10.2 is tested working, but we can test other versions export NODEJS_VER=v10.2
export TELEBIT_VERSION=master # git tag or branch to install from
export TELEBIT_USERSPACE=no # install as a system service (launchd, systemd only)
export TELEBIT_PATH=/opt/telebit export TELEBIT_PATH=/opt/telebit
export TELEBIT_USER=telebit curl -fsSL https://get.telebit.cloud/
export TELEBIT_GROUP=telebit
curl -fsSL https://get.telebit.io/ | bash
``` ```
That will change the bundled version of node.js is bundled with Telebit Relay That will change the bundled version of node.js is bundled with Telebit Relay
and the path to which Telebit Relay installs. and the path to which Telebit Relay installs.
You can get rid of the tos + email and server domain name prompts by providing them right away:
```bash
curl -fsSL https://get.telebit.cloud/ | bash -- jon@example.com example.com telebit.example.com xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
```
Windows & Node.js Windows & Node.js
----------------- -----------------
1. Install [node.js](https://nodejs.org) 1. Install [node.js](https://nodejs.org)
2. Open _Node.js_ 2. Open _Node.js_
2. Run the command `npm install -g telebit` 2. Run the command `npm install -g telebit`
2. Copy the example daemon config to your user folder `.config/telebit/telebitd.yml` (such as `/Users/John/.config/telebit/telebitd.yml`)
2. Copy the example remote config to your user folder `.config/telebit/telebit.yml` (such as `/Users/John/.config/telebit/telebit.yml`)
2. Change the email address
2. Run `npx telebit init` and follow the instructions
2. Run `npx telebit list`
**Note**: Use node.js **v10.2.1** **Note**: Use node.js v8.x or v10.x
(there are specific bugs in each of There is [a bug](https://github.com/nodejs/node/issues/20241) in node v9.x that causes telebit to crash.
v8.x,
[v9.x](https://github.com/nodejs/node/issues/20241),
v10.0,
and v10.3
that each cause telebit to crash)
Remote Usage Usage
============ ====
```
# commands
telebit <command>
# domain and port control
telebit <service> <handler> [servername] [options ...]
```
Examples:
```
telebit status # whether enabled or disabled
telebit enable # disallow incoming connections
telebit disable # allow incoming connections
telebit restart # kill daemon and allow system launcher to restart it
telebit list # list rules for servernames and ports
################
# HTTP #
################
telebit http <handler> [servername] [opts]
telebit http none # remove all https handlers
telebit http 3000 # forward all https traffic to port 3000
telebit http /module/path # load a node module to handle all https traffic
telebit http none example.com # remove https handler from example.com
telebit http 3001 example.com # forward https traffic for example.com to port 3001
telebit http /module/path example.com # forward https traffic for example.com to port 3001
################
# TCP #
################
telebit tcp <handler> [servername] [opts]
telebit tcp none # remove all tcp handlers
telebit tcp 5050 # forward all tcp to port 5050
telebit tcp /module/path # handle all tcp with a node module
telebit tcp none 6565 # remove tcp handler from external port 6565
telebit tcp 5050 6565 # forward external port 6565 to local 5050
telebit tcp /module/path 6565 # handle external port 6565 with a node module
telebit ssh disable # disable ssh access
telebit ssh 22 # port-forward all ssh connections to port 22
telebit save # save http and tcp configuration changes
```
### Using SSH
SSH over HTTPS
```
ssh -o ProxyCommand='openssl s_client -connect %h:443 -servername %h -quiet' lucky-duck-42.telebit.cloud
```
SSH over non-standard port
```
ssh lucky-duck-42.telebit.cloud -p 3031
```
Daemon Usage (non-global)
============
```bash ```bash
~/Applications/bin/node ~/Applications/bin/telebitd.js --config ~/.config/telebit/telebitd.yml telebit --config /opt/telebit/etc/telebit.yml
``` ```
Options Options
`~/.config/telebit/telebitd.yml:` `/opt/telebit/etc/telebit.yml:`
``` ```
email: 'jon@example.com' # must be valid (for certificate recovery and security alerts) email: 'jon@example.com' # must be valid (for certificate recovery and security alerts)
agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes
@ -229,7 +153,7 @@ servernames: # servernames that will be forwarded here
Choosing A Relay Choosing A Relay
================ ================
You can create a free or paid account at <https://telebit.cloud> You can create a free or paid account at https://telebit.cloud
or you can run [Telebit Relay](https://git.coolaj86.com/coolaj86/telebitd.js) or you can run [Telebit Relay](https://git.coolaj86.com/coolaj86/telebitd.js)
open source on a VPS (Vultr, Digital Ocean) open source on a VPS (Vultr, Digital Ocean)
or your Raspberry Pi at home (with port-forwarding). or your Raspberry Pi at home (with port-forwarding).
@ -266,7 +190,7 @@ port_forward:
greenlock: greenlock:
store: le-store-certbot # certificate storage plugin store: le-store-certbot # certificate storage plugin
config_dir: etc/acme # directory for ssl certificates config_dir: /etc/acme # directory for ssl certificates
``` ```
Using Telebit with node.js Using Telebit with node.js
@ -309,25 +233,25 @@ Or if you want to bow down to the kings of the centralized dictator-net:
How to use Telebit Remote with your own instance of Telebit Relay: How to use Telebit Remote with your own instance of Telebit Relay:
```bash ```bash
telebitd \ telebit \
--locals <<external domain name>> \ --locals <<external domain name>> \
--relay wss://<<tunnel domain>>:<<tunnel port>> \ --relay wss://<<tunnel domain>>:<<tunnel port>> \
--secret <<128-bit hex key>> --secret <<128-bit hex key>>
``` ```
```bash ```bash
telebitd --locals john.example.com --relay wss://tunnel.example.com:443 --secret abc123 telebit --locals john.example.com --relay wss://tunnel.example.com:443 --secret abc123
``` ```
```bash ```bash
telebitd \ telebit \
--locals <<protocol>>:<<external domain name>>:<<local port>> \ --locals <<protocol>>:<<external domain name>>:<<local port>> \
--relay wss://<<tunnel domain>>:<<tunnel port>> \ --relay wss://<<tunnel domain>>:<<tunnel port>> \
--secret <<128-bit hex key>> --secret <<128-bit hex key>>
``` ```
```bash ```bash
telebitd \ telebit \
--locals http:john.example.com:3000,https:john.example.com \ --locals http:john.example.com:3000,https:john.example.com \
--relay wss://tunnel.example.com:443 \ --relay wss://tunnel.example.com:443 \
--secret abc123 --secret abc123
@ -486,17 +410,17 @@ Check Logs
**Linux**: **Linux**:
``` ```
SYSTEMD_LOG_LEVEL=debug journalctl -xef --user-unit=telebit sudo journalctl -xefu telebit
``` ```
**macOS**: **macOS**:
``` ```
tail -f ~/local/share/telebit/var/log/info.log sudo tail -f /opt/telebit/var/log/info.log
``` ```
``` ```
tail -f ~/.local/share/telebit/var/log/error.log sudo tail -f /opt/telebit/var/log/error.log
``` ```
Uninstall Uninstall
@ -505,18 +429,16 @@ Uninstall
**Linux**: **Linux**:
``` ```
systemctl --user disable telebit; systemctl --user stop telebit sudo systemctl disable telebit; sudo systemctl stop telebit
rm -f ~/.config/systemd/user/telebit.service sudo rm -rf /etc/systemd/system/telebit.service /opt/telebit /usr/local/bin/telebit
rm -rf ~/telebit ~/Applications/telebit
rm -rf ~/.config/telebit ~/.local/share/telebit rm -rf ~/.config/telebit ~/.local/share/telebit
``` ```
**macOS**: **macOS**:
``` ```
launchctl unload -w ~/Library/LaunchAgents/cloud.telebit.remote.plist sudo launchctl unload -w /Library/LaunchDaemons/cloud.telebit.remote.plist
rm -f ~/Library/LaunchAgents/cloud.telebit.remote.plist sudo rm -rf /Library/LaunchDaemons/cloud.telebit.remote.plist /opt/telebit /usr/local/bin/telebit
rm -rf ~/telebit ~/Applications/telebit
rm -rf ~/.config/telebit ~/.local/share/telebit rm -rf ~/.config/telebit ~/.local/share/telebit
``` ```
@ -528,4 +450,4 @@ This is implemented with websockets, so you should be able to
LICENSE LICENSE
======= =======
Copyright 2016-2018+ AJ ONeal Copyright 2016 AJ ONeal

View File

@ -1,812 +0,0 @@
#!/usr/bin/env node
(function () {
'use strict';
var pkg = require('../package.json');
var os = require('os');
//var url = require('url');
var fs = require('fs');
var path = require('path');
var http = require('http');
//var https = require('https');
var YAML = require('js-yaml');
var TOML = require('toml');
var TPLS = TOML.parse(fs.readFileSync(path.join(__dirname, "../lib/en-us.toml"), 'utf8'));
/*
if ('function' !== typeof TOML.stringify) {
TOML.stringify = require('json2toml');
}
*/
var recase = require('recase').create({});
var camelCopy = recase.camelCopy.bind(recase);
//var snakeCopy = recase.snakeCopy.bind(recase);
var urequest = require('@coolaj86/urequest');
var common = require('../lib/cli-common.js');
var argv = process.argv.slice(2);
var argIndex = argv.indexOf('--config');
if (-1 === argIndex) {
argIndex = argv.indexOf('-c');
}
var confpath;
var useTty;
var state = {};
if (-1 === argIndex) {
argIndex = argv.indexOf('-c');
}
if (-1 !== argIndex) {
confpath = argv.splice(argIndex, 2)[1];
}
argIndex = argv.indexOf('--tty');
if (-1 !== argIndex) {
useTty = argv.splice(argIndex, 1);
}
function help() {
var keys = Object.keys(TPLS.help).filter(function (key) {
return 'remote' !== key;
});
var key = keys.filter(function (key) {
return -1 !== process.argv.indexOf(key);
})[0] || 'remote';
console.info(TPLS.help[key].replace(/{version}/g, pkg.version));
}
var verstr = [ pkg.name + ' remote v' + pkg.version ];
if (!confpath) {
confpath = path.join(os.homedir(), '.config/telebit/telebit.yml');
verstr.push('(--config \'' + confpath.replace(new RegExp('^' + os.homedir()), '~') + '\')');
}
if ([ '-h', '--help', 'help' ].some(function (arg) {
return -1 !== argv.indexOf(arg);
})) {
help();
process.exit(0);
}
if (!confpath || /^--/.test(confpath)) {
help();
process.exit(1);
}
function askForConfig(state, mainCb) {
var fs = require('fs');
var ttyname = '/dev/tty';
var stdin = useTty ? fs.createReadStream(ttyname, {
fd: fs.openSync(ttyname, fs.constants.O_RDONLY | fs.constants.O_NOCTTY)
}) : process.stdin;
var readline = require('readline');
var rl = readline.createInterface({
input: stdin
, output: process.stdout
// https://github.com/nodejs/node/issues/21771
// https://github.com/nodejs/node/issues/21319
, terminal: !/^win/i.test(os.platform()) && !useTty
});
state._useTty = useTty;
// NOTE: Use of setTimeout
// We're using setTimeout just to make the user experience a little
// nicer, as if we're doing something inbetween steps, so that it
// is a smooth rather than jerky experience.
// >= 300ms is long enough to become distracted and change focus (a full blink, time for an idea to form as a thought)
// <= 100ms is shorter than normal human reaction time (ability to place events chronologically, which happened first)
// ~ 150-250ms is the sweet spot for most humans (long enough to notice change and not be jarred, but stay on task)
var firstSet = [
function askEmail(cb) {
if (state.config.email) { cb(); return; }
console.info(TPLS.remote.setup.email);
// TODO attempt to read email from npmrc or the like?
rl.question('email: ', function (email) {
email = /@/.test(email) && email.trim();
if (!email) { askEmail(cb); return; }
state.config.email = email.trim();
state.config.agreeTos = true;
console.info("");
setTimeout(cb, 250);
});
}
, function askRelay(cb) {
function checkRelay(relay) {
// TODO parse and check https://{{relay}}/.well-known/telebit.cloud/directives.json
if (!relay) { relay = 'telebit.cloud'; }
relay = relay.trim();
var urlstr = common.parseUrl(relay) + common.apiDirectory;
urequest({ url: urlstr, json: true }, function (err, resp, body) {
if (err) {
console.error("[Network Error] Failed to retrieve '" + urlstr + "'");
console.error(err);
askRelay(cb);
return;
}
if (200 !== resp.statusCode || (Buffer.isBuffer(body) || 'object' !== typeof body) || !body.api_host) {
console.warn("===================");
console.warn(" WARNING ");
console.warn("===================");
console.warn("");
console.warn("[" + resp.statusCode + "] '" + urlstr + "'");
console.warn("This server does not describe a current telebit version (but it may still work).");
console.warn("");
console.warn(body);
} else if (body && body.pair_request) {
state._can_pair = true;
}
state.config.relay = relay;
cb();
});
}
if (state.config.relay) { checkRelay(state.config.relay); return; }
console.info("");
console.info("");
console.info("What relay will you be using? (press enter for default)");
console.info("");
rl.question('relay [default: telebit.cloud]: ', checkRelay);
}
, function checkRelay(cb) {
nextSet = [];
if ('telebit.cloud' !== state.config.relay) {
nextSet = nextSet.concat(standardSet);
}
if (!state._can_pair) {
nextSet = nextSet.concat(fossSet);
}
cb();
}
];
var standardSet = [
// There are questions that we need to aks in the CLI
// if we can't guarantee that they are being asked in the web interface
function askAgree(cb) {
if (state.config.agreeTos) { cb(); return; }
console.info("");
console.info("");
console.info("Do you accept the terms of service for each and all of the following?");
console.info("");
console.info("\tTelebit - End-to-End Encrypted Relay");
console.info("\tGreenlock - Automated HTTPS");
console.info("\tLet's Encrypt - TLS Certificates");
console.info("");
console.info("Type 'y' or 'yes' to accept these Terms of Service.");
console.info("");
rl.question('agree to all? [y/N]: ', function (resp) {
resp = resp.trim();
if (!/^y(es)?$/i.test(resp) && 'true' !== resp) {
throw new Error("You didn't accept the Terms of Service... not sure what to do...");
}
state.config.agreeTos = true;
console.info("");
setTimeout(cb, 250);
});
}
, function askUpdates(cb) {
// required means transactional, security alerts, mandatory updates
var options = [ 'newsletter', 'important', 'required' ];
if (-1 !== options.indexOf(state._updates)) { cb(); return; }
console.info("");
console.info("");
console.info("What updates would you like to receive? (" + options.join(',') + ")");
console.info("");
rl.question('messages (default: important): ', function (updates) {
state._updates = (updates || '').trim().toLowerCase();
if (!state._updates) { state._updates = 'important'; }
if (-1 === options.indexOf(state._updates)) { askUpdates(cb); return; }
if ('newsletter' === state._updates) {
state.config.newsletter = true;
state.config.communityMember = true;
} else if ('important' === state._updates) {
state.config.communityMember = true;
}
setTimeout(cb, 250);
});
}
, function askTelemetry(cb) {
if (state.config.telemetry) { cb(); return; }
console.info("");
console.info("");
console.info("Contribute project telemetry data? (press enter for default [yes])");
console.info("");
rl.question('telemetry [Y/n]: ', function (telemetry) {
if (!telemetry || /^y(es)?$/i.test(telemetry)) {
state.config.telemetry = true;
}
setTimeout(cb, 250);
});
}
];
var fossSet = [
function askTokenOrSecret(cb) {
if (state._can_pair || state.token || state.config.token
|| state.secret || state.config.secret) { cb(); return; }
console.info("");
console.info("");
console.info("What's your authorization for '" + state.config.relay + "'?");
console.info("");
// TODO check .well-known to learn supported token types
console.info("Currently supported:");
console.info("");
console.info("\tToken (JWT format)");
console.info("\tShared Secret (HMAC hex)");
//console.info("\tPrivate key (hex)");
console.info("");
rl.question('auth: ', function (resp) {
var jwt = require('jsonwebtoken');
resp = (resp || '').trim();
try {
jwt.decode(resp);
state.config.token = resp;
} catch(e) {
// is not jwt
}
if (!state.config.token) {
resp = resp.toLowerCase();
if (resp === Buffer.from(resp, 'hex').toString('hex')) {
state.config.secret = resp;
}
}
if (!state.config.token && !state.config.secret) {
askTokenOrSecret(cb);
return;
}
setTimeout(cb, 250);
});
}
, function askServernames(cb) {
if (!state.config.secret || state.config._servernames) { cb(); return; }
console.info("");
console.info("");
console.info("What servername(s) will you be relaying here?");
console.info("(use a comma-separated list such as example.com,example.net)");
console.info("");
rl.question('domain(s): ', function (resp) {
resp = (resp || '').trim().split(/,/g);
if (!resp.length) { askServernames(); return; }
// TODO validate the domains
state.config._servernames = resp;
setTimeout(cb, 250);
});
}
, function askPorts(cb) {
if (!state.config.secret || state.config._ports) { cb(); return; }
console.info("");
console.info("");
console.info("What tcp port(s) will you be relaying here?");
console.info("(use a comma-separated list such as 2222,5050)");
console.info("");
rl.question('port(s) [default:none]: ', function (resp) {
resp = (resp || '').trim().split(/,/g);
if (!resp.length) { askPorts(); return; }
// TODO validate the domains
state.config._ports = resp;
setTimeout(cb, 250);
});
}
];
var nextSet = firstSet;
function next() {
var q = nextSet.shift();
if (!q) {
// https://github.com/nodejs/node/issues/21319
if (useTty) { try { stdin.push(null); } catch(e) { /*ignore*/ } }
rl.close();
if (useTty) { try { stdin.close(); } catch(e) { /*ignore*/ } }
mainCb(null, state);
return;
}
q(next);
}
next();
}
var utils = {
request: function request(opts, fn) {
if (!opts) { opts = {}; }
var service = opts.service || 'config';
var req = http.request({
socketPath: state._ipc.path
, method: opts.method || 'GET'
, path: '/rpc/' + service
}, function (resp) {
var body = '';
function finish() {
if (200 !== resp.statusCode) {
console.warn(resp.statusCode);
console.warn(body || ('get' + service + ' failed'));
//cb(new Error("not okay"), body);
return;
}
if (!body) { fn(null, null); return; }
try {
body = JSON.parse(body);
} catch(e) {
// ignore
}
fn(null, body);
}
if (resp.headers['content-length']) {
resp.on('data', function (chunk) {
body += chunk.toString();
});
resp.on('end', function () {
finish();
});
} else {
finish();
}
});
req.on('error', function (err) {
// ENOENT - never started, cleanly exited last start, or creating socket at a different path
// ECONNREFUSED - leftover socket just needs to be restarted
if ('ENOENT' === err.code || 'ECONNREFUSED' === err.code) {
if (opts._taketwo) {
console.error("Either the telebit service was not already (and could not be started) or its socket could not be written to.");
console.error(err);
return;
}
require('../usr/share/install-launcher.js').install({ env: process.env }, function (err) {
if (err) { fn(err); return; }
opts._taketwo = true;
setTimeout(function () {
utils.request(opts, fn);
}, 2500);
});
return;
}
if ('ENOTSOCK' === err.code) {
console.error(err);
return;
}
console.error(err);
return;
});
req.end();
}
, putConfig: function putConfig(service, args, fn) {
var req = http.request({
socketPath: state._ipc.path
, method: 'POST'
, path: '/rpc/' + service + '?_body=' + encodeURIComponent(JSON.stringify(args))
}, function (resp) {
function finish() {
if ('function' === typeof fn) {
fn(null, resp);
return;
}
console.info("");
if (200 !== resp.statusCode) {
console.warn("'" + service + "' may have failed."
+ " Consider peaking at the logs either with 'journalctl -xeu telebit' or /opt/telebit/var/log/error.log");
console.warn(resp.statusCode, body);
//cb(new Error("not okay"), body);
return;
}
if (!body) {
console.info("👌");
return;
}
try {
body = JSON.parse(body);
} catch(e) {
// ignore
}
if ("AWAIT_AUTH" === body.code) {
console.info(body.message);
} else if ("CONFIG" === body.code) {
delete body.code;
//console.info(TOML.stringify(body));
console.info(YAML.safeDump(body));
} else {
if ('http' === body.module) {
// TODO we'll support slingshot-ing in the future
if (String(body.local) === String(parseInt(body.local, 10))) {
console.info('> Forwarding https://' + body.remote + ' => localhost:' + body.local);
} else {
console.info('> Serving ' + body.local + ' as https://' + body.remote);
}
} else if ('tcp' === body.module) {
console.info('> Forwarding ' + state.config.relay + ':' + body.remote + ' => localhost:' + body.local);
} else if ('ssh' === body.module) {
//console.info('> Forwarding ' + state.config.relay + ' -p ' + JSON.stringify(body) + ' => localhost:' + body.local);
console.info('> Forwarding ssh+https (openssl proxy) => localhost:' + body.local);
} else {
console.info(JSON.stringify(body, null, 2));
}
console.info();
}
}
var body = '';
if (resp.headers['content-length']) {
resp.on('data', function (chunk) {
body += chunk.toString();
});
resp.on('end', function () {
finish();
});
} else {
finish();
}
});
req.on('error', function (err) {
console.error('Put Config Error:');
console.error(err);
return;
});
req.end();
}
};
// Two styles:
// http 3000
// http modulename
function makeRpc(key) {
if (key !== argv[0]) {
return false;
}
utils.putConfig(argv[0], argv.slice(1));
return true;
}
function packConfig(config) {
return Object.keys(config).map(function (key) {
var val = config[key];
if ('undefined' === val) {
throw new Error("'undefined' used as a string value");
}
if ('undefined' === typeof val) {
//console.warn('[DEBUG]', key, 'is present but undefined');
return;
}
if (val && 'object' === typeof val && !Array.isArray(val)) {
val = JSON.stringify(val);
}
return key + ':' + val; // converts arrays to strings with ,
});
}
function getToken(err, state) {
if (err) {
console.error("Error while initializing config [init]:");
throw err;
}
state.relay = state.config.relay;
// { _otp, config: {} }
common.api.token(state, {
error: function (err/*, next*/) {
console.error("[Error] common.api.token:");
console.error(err);
return;
}
, directory: function (dir, next) {
//console.log('[directory] Telebit Relay Discovered:');
//console.log(dir);
state._apiDirectory = dir;
next();
}
, tunnelUrl: function (tunnelUrl, next) {
//console.log('[tunnelUrl] Telebit Relay Tunnel Socket:', tunnelUrl);
state.wss = tunnelUrl;
next();
}
, requested: function (authReq, next) {
//console.log("[requested] Pairing Requested");
state.config._otp = state.config._otp = authReq.otp;
if (!state.config.token && state._can_pair) {
console.info("");
console.info("==============================================");
console.info(" Hey, Listen! ");
console.info("==============================================");
console.info(" ");
console.info(" GO CHECK YOUR EMAIL! ");
console.info(" ");
console.info(" DEVICE PAIR CODE: 0000 ".replace(/0000/g, state.config._otp));
console.info(" ");
console.info("==============================================");
console.info("");
}
next();
}
, connect: function (pretoken, next) {
//console.log("[connect] Enabling Pairing Locally...");
state.config.pretoken = pretoken;
state._connecting = true;
// TODO use php-style object querification
utils.putConfig('config', packConfig(state.config), function (err/*, body*/) {
if (err) {
state._error = err;
console.error("Error while initializing config [connect]:");
console.error(err);
return;
}
console.info(TPLS.remote.waiting.replace(/{email}/, state.config.email));
next();
});
}
, offer: function (token, next) {
//console.log("[offer] Pairing Enabled by Relay");
state.config.token = token;
if (state._error) {
return;
}
state._connecting = true;
try {
require('jsonwebtoken').decode(token);
//console.log(require('jsonwebtoken').decode(token));
} catch(e) {
console.warn("[warning] could not decode token");
}
utils.putConfig('config', packConfig(state.config), function (err/*, body*/) {
if (err) {
state._error = err;
console.error("Error while initializing config [offer]:");
console.error(err);
return;
}
//console.log("Pairing Enabled Locally");
next();
});
}
, granted: function (_, next) {
//console.log("[grant] Pairing complete!");
next();
}
, end: function () {
utils.putConfig('enable', [], function (err) {
if (err) { console.error(err); return; }
console.info(TPLS.remote.success);
// workaround for https://github.com/nodejs/node/issues/21319
if (state._useTty) {
setTimeout(function () {
console.info(TPLS.remote.next_steps);
process.exit(0);
}, 0.5 * 1000);
return;
}
// end workaround
parseCli(state);
});
}
});
}
function parseCli(/*state*/) {
var special = [
'false', 'none', 'off', 'disable'
, 'true', 'auto', 'on', 'enable'
];
if (-1 !== argv.indexOf('init')) {
utils.putConfig('list', []/*, function (err) {
}*/);
return;
}
if ([ 'ssh', 'http', 'tcp' ].some(function (key) {
if (key !== argv[0]) {
return false;
}
if (argv[1]) {
if (String(argv[1]) === String(parseInt(argv[1], 10))) {
// looks like a port
argv[1] = parseInt(argv[1], 10);
} else if (/\/|\\/.test(argv[1])) {
// looks like a path
argv[1] = path.resolve(argv[1]);
// TODO make a default assignment here
} else if (-1 === special.indexOf(argv[1])) {
console.error("Not sure what you meant by '" + argv[1] + "'.");
console.error("Remember: paths should begin with ." + path.sep + ", like '." + path.sep + argv[1] + "'");
return true;
}
utils.putConfig(argv[0], argv.slice(1));
return true;
}
return true;
})) {
return;
}
if ([ 'status', 'enable', 'disable', 'restart', 'list', 'save' ].some(makeRpc)) {
return;
}
help();
process.exit(11);
}
function handleConfig(err, config) {
//console.log('CONFIG');
//console.log(config);
state.config = config;
var verstrd = [ pkg.name + ' daemon v' + state.config.version ];
if (state.config.version && state.config.version !== pkg.version) {
console.info(verstr.join(' '), verstrd.join(' '));
} else {
console.info(verstr.join(' '));
}
if (err) { console.error(err); process.exit(101); return; }
//
// check for init first, before anything else
// because it has arguments that may help in
// the next steps
//
if (-1 !== argv.indexOf('init')) {
parsers.init(argv, getToken);
return;
}
if (!state.config.relay || !state.config.token) {
if (!state.config.relay) {
state.config.relay = 'telebit.cloud';
}
//console.log("question the user?", Date.now());
askForConfig(state, function (err, state) {
// no errors actually get passed, so this is just future-proofing
if (err) { throw err; }
if (!state.config.token && state._can_pair) {
state.config._otp = common.otp();
}
//console.log("done questioning:", Date.now());
if (!state.token && !state.config.token) {
getToken(err, state);
} else {
parseCli(state);
}
});
return;
}
//console.log("no questioning:");
parseCli(state);
}
function parseConfig(err, text) {
try {
state._clientConfig = JSON.parse(text || '{}');
} catch(e1) {
try {
state._clientConfig = YAML.safeLoad(text || '{}');
} catch(e2) {
try {
state._clientConfig = TOML.parse(text || '');
} catch(e3) {
console.error(e1.message);
console.error(e2.message);
process.exit(1);
return;
}
}
}
state._clientConfig = camelCopy(state._clientConfig || {}) || {};
common._init(
// make a default working dir and log dir
state._clientConfig.root || path.join(os.homedir(), '.local/share/telebit')
, (state._clientConfig.root && path.join(state._clientConfig.root, 'etc'))
|| path.resolve(common.DEFAULT_CONFIG_PATH, '..')
);
state._ipc = common.pipename(state._clientConfig, true);
if (!Object.keys(state._clientConfig).length) {
console.info('(' + state._ipc.comment + ": " + state._ipc.path + ')');
console.info("");
}
if ((err && 'ENOENT' === err.code) || !Object.keys(state._clientConfig).length) {
if (!err || 'ENOENT' === err.code) {
//console.warn("Empty config file. Run 'telebit init' to configure.\n");
} else {
console.warn("Couldn't load config:\n\n\t" + err.message + "\n");
}
}
utils.request({ service: 'config' }, handleConfig);
}
var parsers = {
init: function (argv, parseCb) {
var answers = {};
var boolish = [ '--advanced' ];
if ('init' !== argv[0]) {
throw new Error("init must be the first argument");
}
argv.shift();
// init --foo bar
argv.forEach(function (arg, i) {
if (!/^--/.test(arg)) { return; }
if (-1 !== boolish.indexOf(arg)) {
answers['_' + arg.replace(/^--/, '')] = true;
}
if (/^-/.test(argv[i + 1])) {
throw new Error(argv[i + 1] + ' requires an argument');
}
answers[arg] = argv[i + 1];
});
// init foo:bar
argv.forEach(function (arg) {
if (/^--/.test(arg)) { return; }
var parts = arg.split(/:/g);
if (2 !== parts.length) {
throw new Error("bad option to init: '" + arg + "'");
}
if (answers[parts[0]]) {
throw new Error("duplicate key to init '" + parts[0] + "'");
}
answers[parts[0]] = parts[1];
});
if (answers.relay) {
console.info("using --relay " + answers.relay);
}
// things that aren't straight-forward copy-over
if (!answers.advanced && !answers.relay) {
answers.relay = 'telebit.cloud';
}
if (Array.isArray(common._NOTIFICATIONS[answers.update])) {
common._NOTIFICATIONS[answers.update].forEach(function (name) {
state.config[name] = true;
});
}
if (answers.servernames) {
state.config._servernames = answers.servernames;
}
if (answers.ports) {
state.config._ports = answers.ports;
}
// things that are straight-forward copy-over
common.CONFIG_KEYS.forEach(function (key) {
if ('true' === answers[key]) { answers[key] = true; }
if ('false' === answers[key]) { answers[key] = false; }
if ('null' === answers[key]) { answers[key] = null; }
if ('undefined' === answers[key]) { delete answers[key]; }
if ('undefined' !== typeof answers[key]) {
state.config[key] = answers[key];
}
});
askForConfig(state, function (err, state) {
if (err) { parseCb(err); return; }
if (!state.config.token && state._can_pair) {
state.config._otp = common.otp();
}
argv.unshift('init');
parseCb(null, state);
});
}
};
fs.readFile(confpath, 'utf8', parseConfig);
}());

View File

@ -2,35 +2,430 @@
(function () { (function () {
'use strict'; 'use strict';
// var pkg = require('../package.json');
// node telebit daemon arg1 arg2 console.log(pkg.name, pkg.version);
//
if ('daemon' === process.argv[2]) { var url = require('url');
require('./telebitd.js'); var path = require('path');
var remote = require('../');
var state = {};
var argv = process.argv.slice(2);
var confIndex = argv.indexOf('--config');
var confpath;
if (-1 === confIndex) {
confIndex = argv.indexOf('-c');
}
confpath = argv[confIndex + 1];
function help() {
console.info('');
console.info('Telebit Remote v' + pkg.version);
console.info('');
console.info('Usage:');
console.info('');
console.info('\ttelebit --config <path>');
console.info('');
console.info('Example:');
console.info('');
console.info('\ttelebit --config /etc/telebit/telebit.yml');
console.info('');
console.info('Config:');
console.info('');
console.info('\tSee https://git.coolaj86.com/coolaj86/telebit.js');
console.info('');
console.info('');
process.exit(0);
}
if (-1 === confIndex) {
confpath = path.join(require('os').homedir(), '.config/telebit/telebit.yml');
console.info('Using default --config "' + confpath + '"');
}
if (-1 !== argv.indexOf('-h') || -1 !== argv.indexOf('--help')) {
help();
}
if (!confpath || /^--/.test(confpath)) {
help();
}
var tokenfile = 'access_token.txt';
var tokenpath = path.join(path.dirname(confpath), tokenfile);
var token;
try {
token = require('fs').readFileSync(tokenpath, 'ascii').trim();
} catch(e) {
// ignore
}
require('fs').readFile(confpath, 'utf8', function (err, text) {
var config;
var recase = require('recase').create({});
var camelCopy = recase.camelCopy.bind(recase);
if (err) {
console.error("\nCouldn't load config:\n\n\t" + err.message + "\n");
process.exit(1);
return;
}
try {
config = JSON.parse(text);
} catch(e1) {
try {
config = require('js-yaml').safeLoad(text);
} catch(e2) {
console.error(e1.message);
console.error(e2.message);
process.exit(1);
return;
}
}
state._confpath = confpath;
state.config = camelCopy(config);
if (state.config.token && token) {
console.warn();
console.warn("Found two tokens:");
console.warn();
console.warn("\t1. " + tokenpath);
console.warn("\n2. " + confpath);
console.warn();
console.warn("Choosing the first.");
console.warn();
}
state.config.token = token;
rawTunnel();
});
function connectTunnel() {
state.net = {
createConnection: function (info, cb) {
// data is the hello packet / first chunk
// info = { data, servername, port, host, remoteFamily, remoteAddress, remotePort }
var net = require('net');
// socket = { write, push, end, events: [ 'readable', 'data', 'error', 'end' ] };
var socket = net.createConnection({ port: info.port, host: info.host }, cb);
return socket;
}
};
state.greenlock = state.config.greenlock || {};
if (!state.config.sortingHat) {
state.config.sortingHat = path.resolve(__dirname, '..', 'lib/sorting-hat.js');
}
// TODO sortingHat.print(); ?
if (state.config.email && !state.token) {
console.info();
console.info('==================================');
console.info('= HEY! LISTEN! =');
console.info('==================================');
console.info('= =');
console.info('= 1. Open your email =');
console.info('= 2. Click the magic login link =');
console.info('= 3. Check back here for deets =');
console.info('= =');
console.info('==================================');
console.info();
}
// TODO Check undefined vs false for greenlock config
var tun = remote.connect({
relay: state.config.relay
, config: state.config
, _confpath: confpath
, sortingHat: state.config.sortingHat
, net: state.net
, insecure: state.config.relay_ignore_invalid_certificates
, token: state.token
, handlers: {
grant: function (grants) {
console.info("");
console.info("Connect to your device by any of the following means:");
console.info("");
grants.forEach(function (arr) {
if ('ssh+https' === arr[0]) {
console.info("SSH+HTTPS");
} else if ('ssh' === arr[0]) {
console.info("SSH");
} else if ('tcp' === arr[0]) {
console.info("TCP");
} else if ('https' === arr[0]) {
console.info("HTTPS");
}
console.log('\t' + arr[0] + '://' + arr[1] + (arr[2] ? (':' + arr[2]) : ''));
if ('ssh+https' === arr[0]) {
console.info("\tex: ssh -o ProxyCommand='openssl s_client -connect %h:%p -quiet' " + arr[1] + " -p 443\n");
} else if ('ssh' === arr[0]) {
console.info("\tex: ssh " + arr[1] + " -p " + arr[2] + "\n");
} else if ('tcp' === arr[0]) {
console.info("\tex: netcat " + arr[1] + " " + arr[2] + "\n");
} else if ('https' === arr[0]) {
console.info("\tex: curl https://" + arr[1] + "\n");
}
});
}
, access_token: function (opts) {
console.info("Updating '" + tokenpath + "' with new token:");
try {
require('fs').writeFileSync(tokenpath, opts.jwt);
} catch (e) {
console.error("Token not saved:");
console.error(e);
}
}
}
, greenlockConfig: {
version: state.greenlock.version || 'draft-11'
, server: state.greenlock.server || 'https://acme-v02.api.letsencrypt.org/directory'
, communityMember: state.greenlock.communityMember || state.config.communityMember
, telemetry: state.greenlock.telemetry || state.config.telemetry
, configDir: state.greenlock.configDir || path.resolve(__dirname, '..', '/etc/acme/')
// TODO, store: require(state.greenlock.store.name || 'le-store-certbot').create(state.greenlock.store.options || {})
, approveDomains: function (opts, certs, cb) {
// Certs being renewed are listed in certs.altnames
if (certs) {
opts.domains = certs.altnames;
cb(null, { options: opts, certs: certs });
return;
}
// by virtue of the fact that it's being tunneled through a
// trusted source that is already checking, we're good
//if (-1 !== state.config.servernames.indexOf(opts.domains[0])) {
opts.email = state.greenlock.email || state.config.email;
opts.agreeTos = state.greenlock.agree || state.config.agreeTos;
cb(null, { options: opts, certs: certs });
return;
//}
//cb(new Error("servername not found in allowed list"));
}
}
});
function sigHandler() {
console.info('Received kill signal. Attempting to exit cleanly...');
// We want to handle cleanup properly unless something is broken in our cleanup process
// that prevents us from exitting, in which case we want the user to be able to send
// the signal again and exit the way it normally would.
process.removeListener('SIGINT', sigHandler);
tun.end();
}
process.on('SIGINT', sigHandler);
}
function rawTunnel() {
if (!state.config.relay) {
throw new Error("'" + state._confpath + "' is missing 'relay'");
}
/*
if (!(state.config.secret || state.config.token)) {
console.error("You must use --secret or --token with --relay");
process.exit(1);
return;
}
*/
var location = url.parse(state.config.relay);
if (!location.protocol || /\./.test(location.protocol)) {
state.config.relay = 'wss://' + state.config.relay;
location = url.parse(state.config.relay);
}
var aud = location.hostname + (location.port ? ':' + location.port : '');
state.config.relay = location.protocol + '//' + aud;
if (!state.config.token && state.config.secret) {
var jwt = require('jsonwebtoken');
var tokenData = {
domains: Object.keys(state.config.servernames || {}).filter(function (name) { return /\./.test(name); })
, aud: aud
, iss: Math.round(Date.now() / 1000)
};
state.token = jwt.sign(tokenData, state.config.secret);
}
state.token = state.token || state.config.token;
// TODO sign token with own private key, including public key and thumbprint
// (much like ACME JOSE account)
connectTunnel();
}
/*
var domainsMap = {};
var services = {};
function collectDomains(val, memo) {
var vals = val.split(/,/g);
function parseProxy(location) {
// john.example.com
// http:john.example.com:3000
// http://john.example.com:3000
var parts = location.split(':');
if (1 === parts.length) {
// john.example.com -> :john.example.com:0
parts[1] = parts[0];
parts[0] = '';
parts[2] = 0;
}
else if (2 === parts.length) {
throw new Error("invalid arguments for --domains, should use the format <domainname> or <scheme>:<domainname>:<local-port>");
}
if (!parts[1]) {
throw new Error("invalid arguments for --domains, should use the format <domainname> or <scheme>:<domainname>:<local-port>");
}
parts[0] = parts[0].toLowerCase();
parts[1] = parts[1].toLowerCase().replace(/(\/\/)?/, '');
parts[2] = parseInt(parts[2], 10) || 0;
memo.push({
protocol: parts[0]
, hostname: parts[1]
, port: parts[2]
});
}
vals.map(function (val) {
return parseProxy(val);
});
return memo;
}
function collectProxies(val, memo) {
var vals = val.split(/,/g);
function parseProxy(location) {
// john.example.com
// https:3443
// http:john.example.com:3000
// http://john.example.com:3000
var parts = location.split(':');
var dual = false;
if (1 === parts.length) {
// john.example.com -> :john.example.com:0
parts[1] = parts[0];
parts[0] = '';
parts[2] = 0;
dual = true;
}
else if (2 === parts.length) {
// https:3443 -> https:*:3443
parts[2] = parts[1];
parts[1] = '*';
}
parts[0] = parts[0].toLowerCase();
parts[1] = parts[1].toLowerCase().replace(/(\/\/)?/, '') || '*';
parts[2] = parseInt(parts[2], 10) || 0;
if (!parts[2]) {
// TODO grab OS list of standard ports?
if (!parts[0] || 'http' === parts[0]) {
parts[2] = 80;
}
else if ('https' === parts[0]) {
parts[2] = 443;
}
else {
throw new Error("port must be specified - ex: tls:*:1337");
}
}
memo.push({
protocol: parts[0] || 'https'
, hostname: parts[1]
, port: parts[2] || 443
});
if (dual) {
memo.push({
protocol: 'http'
, hostname: parts[1]
, port: 80
});
}
}
vals.map(function (val) {
return parseProxy(val);
});
return memo;
}
var program = require('commander');
program
.version(pkg.version)
//.command('jsurl <url>')
.arguments('<url>')
.action(function (url) {
program.url = url;
})
.option('-k --insecure', 'Allow TLS connections to a Telebit Relay without valid certs (rejectUnauthorized: false)')
.option('--locals <LIST>', 'comma separated list of <proto>:<port> to which matching incoming http and https should forward (reverse proxy). Ex: https:8443,smtps:8465', collectProxies, [ ]) // --reverse-proxies
.option('--domains <LIST>', 'comma separated list of domain names to set to the tunnel (to capture a specific protocol to a specific local port use the format https:example.com:1337 instead). Ex: example.com,example.net', collectDomains, [ ])
.option('--device [HOSTNAME]', 'Tunnel all domains associated with this device instead of specific domainnames. Use with --locals <proto>:<port>. Ex: macbook-pro.local (the output of `hostname`)')
.option('--relay <URL>', 'the domain (or ip address) at which you are running Telebit Relay (the proxy)') // --proxy
.option('--secret <STRING>', 'the same secret used by the Telebit Relay (used for JWT authentication)')
.option('--token <STRING>', 'a pre-generated token for use with the Telebit Relay (instead of generating one with --secret)')
.option('--agree-tos', 'agree to the Telebit Terms of Service (requires user validation)')
.option('--email <EMAIL>', 'email address (or cloud address) for user validation')
.option('--oauth3-url <URL>', 'Cloud Authentication to use (default: https://oauth3.org)')
.parse(process.argv)
;
program.locals = (program.locals || []).concat(program.domains || []);
program.locals.forEach(function (proxy) {
// Create a map from which we can derive a list of all domains we want forwarded to us.
if (proxy.hostname && proxy.hostname !== '*') {
domainsMap[proxy.hostname] = true;
}
// Create a map of which port different protocols should be forwarded to, allowing for specific
// domains to go to different ports if need be (though that only works for HTTP and HTTPS).
if (proxy.protocol && proxy.port) {
services[proxy.protocol] = services[proxy.protocol] || {};
if (/http/.test(proxy.protocol) && proxy.hostname && proxy.hostname !== '*') {
services[proxy.protocol][proxy.hostname] = proxy.port;
}
else {
if (services[proxy.protocol]['*'] && services[proxy.protocol]['*'] !== proxy.port) {
console.error('cannot forward generic', proxy.protocol, 'traffic to multiple ports');
process.exit(1);
}
else {
services[proxy.protocol]['*'] = proxy.port;
}
}
}
});
if (Object.keys(domainsMap).length === 0) {
console.error('no domains specified');
process.exit(1);
return; return;
} }
// // Make sure we have generic ports for HTTP and HTTPS
// sclient proxies services.https = services.https || {};
// services.https['*'] = services.https['*'] || 8443;
if ('sclient' === process.argv[2]) {
process.argv.splice(1,1);
return;
}
if ('rsync' === process.argv[2]) {
require('sclient/bin/sclient.js');
return;
}
if ('ssh' === process.argv[2] && /[\w-]+\.[a-z]{2,}/i.test(process.argv[3])) {
process.argv.splice(1,1,'sclient');
process.argv.splice(2,1,'ssh');
require('sclient/bin/sclient.js');
return;
}
// services.http = services.http || {};
// telebit remote services.http['*'] = services.http['*'] || services.https['*'];
//
require('./telebit-remote.js'); program.services = services;
*/
}()); }());

File diff suppressed because it is too large Load Diff

View File

View File

@ -1,7 +0,0 @@
email: 'jon@example.com' # must be valid (for certificate recovery and security alerts)
agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes
community_member: true # receive infrequent relevant updates
telemetry: true # contribute to project telemetric data
relay: telebit.cloud # Which Telebit Relay to use
#secret: '' # Shared Secret with Telebit Relay for authorization
#token: '' # Token created by Telebit Relay for authorization

View File

@ -2,18 +2,13 @@ email: 'jon@example.com' # must be valid (for certificate recovery and se
agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes
community_member: true # receive infrequent relevant updates community_member: true # receive infrequent relevant updates
telemetry: true # contribute to project telemetric data telemetry: true # contribute to project telemetric data
relay: telebit.cloud # Which Telebit Relay to use relay: wss://telebit.cloud # Which Telebit Relay to use
#secret: '' # Shared Secret with Telebit Relay for authorization secret: '' # Shared Secret with Telebit Relay for authorization
#token: '' # Token created by Telebit Relay for authorization #token: '' # Token created by Telebit Relay for authorization
ssh_auto: 22 # forward ssh-looking packets, from any connection, to port 22 ssh_auto: 22 # forward ssh-looking packets, from any connection, to port 22
servernames: # hostnames that direct to the Telebit Relay admin console servernames: # hostnames that direct to the Telebit Relay admin console
example.com: example.com: {}
handler: 3000 example.net: {}
example.net:
handler: /path/to/module
ports:
5050:
handler: 54321
greenlock: greenlock:
version: 'draft-11' version: 'draft-11'
server: 'https://acme-staging-v02.api.letsencrypt.org/directory' server: 'https://acme-staging-v02.api.letsencrypt.org/directory'

View File

@ -1,310 +0,0 @@
'use strict';
module.exports.debug = (-1 !== (process.env.NODE_DEBUG||'').split(/\s+/g).indexOf('telebit'));
var common = module.exports;
var path = require('path');
var url = require('url');
var fs = require('fs');
var mkdirp = require('mkdirp');
var os = require('os');
var homedir = os.homedir();
var urequest = require('@coolaj86/urequest');
common._NOTIFICATIONS = {
'newsletter': [ 'newsletter', 'communityMember' ]
, 'important': [ 'communityMember' ]
};
common.CONFIG_KEYS = [
'newsletter'
, 'communityMember'
, 'telemetry'
, 'sshAuto'
, 'email'
, 'agreeTos'
, 'relay'
, 'token'
, 'pretoken'
, 'secret'
];
//, '_servernames' // list instead of object
//, '_ports' // list instead of object
//, '_otp' // otp should not be saved
//, '_token' // temporary token
common.getPort = function (config, cb) {
var portfile = path.resolve(config.sock || common.DEFAULT_SOCK_PATH, '..', 'telebit.port');
if (cb) {
return fs.readFile(portfile, 'utf8', function (err, text) {
cb(err, parseInt((text||'').trim(), 10) || null);
});
} else {
try {
return parseInt(fs.readFileSync(portfile, 'utf8').trim(), 10) || null;
} catch(e) {
return null;
}
}
};
common.setPort = function (config, num, cb) {
var portfile = path.resolve(config.sock || common.DEFAULT_SOCK_PATH, '..', 'telebit.port');
var numstr = (num || '').toString();
if (cb) {
return fs.writeFile(portfile, numstr, 'utf8', function (err) {
cb(err);
});
} else {
try {
return fs.writeFileSync(portfile, numstr, 'utf8');
} catch(e) {
return null;
}
}
};
common.removePort = function (config, cb) {
var portfile = path.resolve(config.sock || common.DEFAULT_SOCK_PATH, '..', 'telebit.port');
if (cb) {
return fs.unlink(portfile, function (err, text) {
cb(err, (text||'').trim());
});
} else {
try {
return fs.unlinkSync(portfile);
} catch(e) {
return null;
}
}
};
common.pipename = function (config) {
var _ipc = {
path: (config.sock || common.DEFAULT_SOCK_PATH)
, comment: (/^win/i.test(os.platform()) ? 'windows pipe' : 'unix socket')
, type: (/^win/i.test(os.platform()) ? 'pipe' : 'socket')
};
if ('pipe' === _ipc.type) {
// https://docs.microsoft.com/en-us/windows/desktop/ipc/pipe-names
// Allows all characters accept backslash as part of the name
_ipc.path = '\\\\.\\pipe\\' + _ipc.path.replace(/\\/g, '/');
}
return _ipc;
};
common.DEFAULT_SOCK_PATH = path.join(homedir, '.local/share/telebit/var/run', 'telebit.sock');
common.DEFAULT_CONFIG_PATH = path.join(homedir, '.config/telebit', 'telebitd.yml');
common.parseUrl = function (hostname) {
var location = url.parse(hostname);
if (!location.protocol || /\./.test(location.protocol)) {
hostname = 'https://' + hostname;
location = url.parse(hostname);
}
hostname = location.hostname + (location.port ? ':' + location.port : '');
hostname = location.protocol.replace(/https?/, 'https') + '//' + hostname + location.pathname;
return hostname;
};
common.parseHostname = function (hostname) {
var location = url.parse(hostname);
if (!location.protocol || /\./.test(location.protocol)) {
hostname = 'https://' + hostname;
location = url.parse(hostname);
}
//hostname = location.hostname + (location.port ? ':' + location.port : '');
//hostname = location.protocol.replace(/https?/, 'https') + '//' + hostname + location.pathname;
return location.hostname;
};
common.apiDirectory = '_apis/telebit.cloud/index.json';
common.otp = function getOtp() {
return Math.round(Math.random() * 9999).toString().padStart(4, '0');
};
common.signToken = function (state) {
var jwt = require('jsonwebtoken');
var tokenData = {
domains: Object.keys(state.config.servernames || {}).filter(function (name) {
return /\./.test(name);
})
, ports: Object.keys(state.config.ports || {}).filter(function (port) {
port = parseInt(port, 10);
return port > 0 && port <= 65535;
})
, aud: state._relayUrl
, iss: Math.round(Date.now() / 1000)
};
return jwt.sign(tokenData, state.config.secret);
};
common.api = {};
common.api.directory = function (state, next) {
state._relayUrl = common.parseUrl(state.relay);
urequest({ url: state._relayUrl + common.apiDirectory, json: true }, function (err, resp, dir) {
if (!dir) { dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } }; }
state._apiDirectory = dir;
next(err, dir);
});
};
common.api._parseWss = function (state, dir) {
if (!dir || !dir.api_host) {
dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } };
}
state._relayHostname = common.parseHostname(state.relay);
return dir.tunnel.method + '://' + dir.api_host.replace(/:hostname/g, state._relayHostname) + dir.tunnel.pathname;
};
common.api.wss = function (state, cb) {
common.api.directory(state, function (err, dir) {
cb(err, common.api._parseWss(state, dir));
});
};
common.api.token = function (state, handlers) {
common.api.directory(state, function (err, dir) {
// directory, requested, connect, tunnelUrl, offer, granted, end
function afterDir() {
if (common.debug) { console.log('[debug] after dir'); }
state.wss = common.api._parseWss(state, dir);
handlers.tunnelUrl(state.wss, function () {
if (common.debug) { console.log('[debug] after tunnelUrl'); }
if (state.config.secret /* && !state.config.token */) {
state.config._token = common.signToken(state);
}
state.token = state.token || state.config.token || state.config._token;
if (state.token) {
if (common.debug) { console.log('[debug] token via token or secret'); }
// { token, pretoken }
handlers.connect(state.token, function () {
handlers.end(null, function () {});
});
return;
}
// backwards compat (TODO remove)
if (err || !dir || !dir.pair_request) {
if (common.debug) { console.log('[debug] no dir, connect'); }
handlers.error(new Error("No token found or generated, and no pair_request api found."));
return;
}
// TODO sign token with own private key, including public key and thumbprint
// (much like ACME JOSE account)
var otp = state.config._otp; // common.otp();
var authReq = {
subject: state.config.email
, subject_scheme: 'mailto'
// TODO create domains list earlier
, scope: (state.config._servernames || Object.keys(state.config.servernames || {}))
.concat(state.config._ports || Object.keys(state.config.ports || {})).join(',')
, otp: otp
, hostname: os.hostname()
// Used for User-Agent
, os_type: os.type()
, os_platform: os.platform()
, os_release: os.release()
, os_arch: os.arch()
};
var pairRequestUrl = url.resolve('https://' + dir.api_host.replace(/:hostname/g, state._relayHostname), dir.pair_request.pathname);
var req = {
url: pairRequestUrl
, method: dir.pair_request.method
, json: authReq
};
var firstReq = true;
var firstReady = true;
function gotoNext(req) {
if (common.debug) { console.log('[debug] gotoNext called'); }
if (common.debug) { console.log(req); }
urequest(req, function (err, resp, body) {
if (err) {
if (common.debug) { console.log('[debug] gotoNext error'); }
err._request = req;
err._hint = '[telebitd.js] pair request';
handlers.error(err, function () {});
return;
}
function checkLocation() {
if (common.debug) { console.log('[debug] checkLocation'); }
if (common.debug) { console.log(body); }
// pending, try again
if ('pending' === body.status && resp.headers.location) {
if (common.debug) { console.log('[debug] pending'); }
setTimeout(gotoNext, 2 * 1000, { url: resp.headers.location, json: true });
return;
}
if ('ready' === body.status) {
if (common.debug) { console.log('[debug] ready'); }
if (firstReady) {
if (common.debug) { console.log('[debug] first ready'); }
firstReady = false;
state.token = body.access_token;
state.config.token = state.token;
handlers.offer(body.access_token, function () {
/*ignore*/
});
}
setTimeout(gotoNext, 2 * 1000, req);
return;
}
if ('complete' === body.status) {
if (common.debug) { console.log('[debug] complete'); }
handlers.granted(null, function () {
handlers.end(null, function () {});
});
return;
}
if (common.debug) { console.log('[debug] bad status'); }
var err = new Error("Bad State:" + body.status);
err._request = req;
handlers.error(err, function () {});
}
if (firstReq) {
if (common.debug) { console.log('[debug] first req'); }
handlers.requested(authReq, function () {
handlers.connect(body.access_token || body.jwt, function () {
var err;
if (!resp.headers.location) {
err = new Error("bad authentication request response");
err._resp = resp.toJSON();
handlers.error(err, function () {});
return;
}
setTimeout(gotoNext, 2 * 1000, { url: resp.headers.location, json: true });
});
});
firstReq = false;
return;
} else {
if (common.debug) { console.log('[debug] other req'); }
checkLocation();
}
});
}
gotoNext(req);
});
}
if (dir && dir.api_host) {
handlers.directory(dir, afterDir);
} else {
// backwards compat
dir = { api_host: ':hostname', tunnel: { method: "wss", pathname: "" } };
afterDir();
}
});
};
common._init = function (rootpath, confpath) {
try {
mkdirp.sync(path.join(rootpath, 'var', 'log'));
mkdirp.sync(path.join(rootpath, 'var', 'run'));
mkdirp.sync(path.join(confpath));
} catch(e) {
console.error(e);
}
};

View File

@ -1,493 +0,0 @@
[help]
remote = "telebit remote v{version}
Telebit Remote is the T-Rex long-arm of the Internet. UNSTOPPABLE!
Using reliable HTTPS tunneling to establishing peer-to-peer connections,
Telebit is empowering the next generation of tinkerers. Access your devices.
Share your stuff. Be UNSTOPPABLE! (Join us at https://rootprojects.org)
Usage:
telebit [flags] <command> [arguments]
ex: telebit http ~/Public
The flags are:
--config <path> specify config file (default is ~/.config/telebit/telebit.yml)
--json output json instead of text, if available
-h,--help display this menu (or sub-command menus)
The commands are:
status show status and configuration info
http access files, folders, and local apps via https (secure)
ssh enable remote access to this device with ssh-over-https
ssh (client) access devices via ssh-over-https (telebit, stunnel, openssl, etc)
tcp forward tcp locally
enable turn on remote access and sharing
disable turn off remote access and sharing
activate start and register the telebit service
disable stop and unregister the telebit service
config (doc) config file format and settings
client (doc) vpn, ftp, rsync, scp, ssh-proxy, sclient
Use \"telebit help [command]\" for more information about a command, including flags.
Additional help topics:
daemon telebit daemon secure background service
relay telebit secure relay, hosted, and self-hosting options
Copyright 2015-2018 AJ ONeal https://telebit.cloud MPL-2.0 Licensed (RAWR!)"
client = "telebit client v{version}
ftp secure ftp file transfer between devices
rsync rsync over https and proxy commands
scp scp over https and proxy commands
sclient use the sclient emebbed within telebit
ssh-proxy ssh over https and proxy commands
vpn (client) home network access and private web browsing via socks5
Use \"telebit help [command]\" for more information about a command, including flags.
Copyright 2015-2018 AJ ONeal https://telebit.cloud MPL-2.0 Licensed (RAWR!)"
status = "usage: telebit status
'telebit status' shows details about the current connections (or lack thereof).
Example:
Status: RAWR! (uptime: 45 minutes)
Forwarding ssh+https://jon.telebit.io => localhost:22
Forwarding https://client.jon.telebit.io => localhost:3000
Serving https://public.jon.telebit.io from ~/Public
Syncing ~/shared => home.jon.telebit.io:shared
Relay: https://telebit.cloud
Launcher: user
Additional help topics: enable, disable"
enable = "Enable Telebit - Re-enable and accept incoming connections
usage: telebit enable
enable Re-enable incoming connections for https, ssh, etc"
disable = "Disable Telebit - Reject https, ssh, and tcp connections
usage: telebit disable
disable (Temporarily) reject incoming connections for https,
ssh, etc without deleting the current configuration.
Perists on restart, but can be re-enabled remotely
(with your authorization only)."
activate = "Activate Telebit - Start telebit (if not running) and register a launcher
Usage:
telebit activate [flags]
ex: telebit activate --launcher none
The flags may be exactly one of:
--no-launcher uregister any launchers (start manually)
--user-launcher (default) register an unprivileged launcher (start on login)
--system-launcher register with the system launcher (start on boot)
Note: telebit relies on the system launcher to recover from certain error conditions"
deactivate = "Deactivate Telebit - Unregister userspace (or system) launcher and stop
Usage:
telebit deactivate [flags]
ex: telebit deactivate --keep alive
The flags are:
--keep-launcher stop telebit without unregistering the launcher
--keep-alive unregister launcher without stopping"
http = "Telebit HTTP - The UNSTOPPABLE way to share files, folders, and local apps.
usage: telebit http <path/port/none> [subdomain]
http <DIR> [subdomain] serve a file, folder, or node express app
ex: telebit http ~/Public pub ex: securely host ~/Public as pub.johndoe.telebit.io
http <PORT> [subdomain] forward all https traffic to a local app
ex: telebit http 3000 app ex: publicize localhost:3000 as app.johndoe.telebit.io
http none [subdomain] remove secure http access for (any or all) subdomain(s)
ex: telebit http none ex: remove all https access
Use cases:
- Lazy man's AirDrop (works for lazy women too!)
- Testing dev sites on a phone
- Sharing indie music and movies with friends"
ssh = "Telebit SSH - The UNSTOPPABLE way to remote into your devices.
usage: telebit ssh <auto|port|none>
All https traffic will be inspected to see if it looks like ssh Once enabled all traffic that looks
ssh auto Make ssh Just Work (on port 22)
ssh <port> forward ssh traffic to non-standard port
ex: telebit ssh 22 ex: explicitly forward ssh-looking packets to localhost:22
ssh none Disables ssh tunneling
Telebit SSH Client
usage: telebit ssh <remote> [ssh flags and options]
This is just a shortcut for \"ssh\", with all ssh-over-https options turned on.
ssh <remote> Make ssh Just Work (over https)
ex: telebit ssh jon.telebit.io ex:
\"telebit help ssh-proxy\" for more info
Use cases:
- Access your home computer from work.
- Access your work computer from home.
- Good ol' fashioned screen/tmux style pair programming"
ssh-proxy = "Proxying SSH over HTTPS
Wrapping SSH in HTTPS makes it accessible anywhere and also makes it routable.
Whether inside a harsh network environment or even if hindered by a poorly
configured firewall, once wrapped in tls, ssh becomes UNSTOPPABLE.
Usage:
telebit ssh <remote> [ssh flags and options]
Example:
telebit ssh jon.telebit.io
It is NOT at all neccessary to use \"telebit ssh\", it's just a convenience.
Wanna know why, and the alternatives? Keep reading!
## History
When TLS sends an encrypted packet over the network it begins with a handshake
which shows the things like the tls version and the host SERVERNAME unencrypted
so that the remote server can respond with the correct certificate.
SSH was created well before TLS and has a completely different header. The good
news is that, unlike some other early internet protocols, it does have a header
with its name and version, but it doesn't have anything to identify the server.
## Telebit + SSH
Here's why:
When you're running ssh through an https tunnel (as telebit does) you
can't just use \"ssh me.example.com\" to get in. You have to tell ssh that you
want to use an https tunnel. Using \"telebit ssh\" as a client will specify
all of the correct ssh options.
However, when you want to connect to ssh over https, you either have to pass
the correct arguments or modify your ~/.ssh/config to use \"openssl s_client\".
We explain the different configurations below:
## SSH + openssl
The configuration that's most likely to work with what's already installed on
your machine is this:
Host jon.telebit.io
ProxyCommand openssl s_client -quiet -connect %h:443 -servername %h
Or you would call ssh directly, like this:
ssh jon.telebit.io -o ProxyCommand=\"openssl s_client -quiet -connect %h:443 -servername %h\"
It's rather simple, but it looks quite daunting.
## SSH + sclient
Because that looks a little hairy, we created \"sclient\", so that the example
could look a bit more digestible:
Host jon.telebit.io
ProxyCommand sclient %h
Or
ssh jon.telebit.io -o ProxyCommand=\"sclient %h\"
## Inverse SSH Tunnel (same as stunnel)
The commands above instruct ssh to open a pipe into openssl or sclient. If we
instead want to connect ssh to a local tunnel, it looks like this:
Host jon.telebit.io
Hostname localhost
Port 3000
HostKeyAlias jon.telebit.io
CheckHostIP no
RequestTTY force
Or
ssh localhost -p 3000 -t -o CheckHostIP=no -o HostKeyAlias=jon.telebit.io
## See also
telebit ftp
telebit vpn"
tcp = "Telebit TCP - Seemless connectivity to LEGACY apps.
Use 'telebit http' instead, where possible (including for ssh).
usage: telebit tcp <path/port/none>
tcp <local> [remote] forward tcp to <local> from <remote>
ex: telebit tcp 5050 6565 ex: forward tcp port 6565 locally to port 5050
tcp <path> [remote] show ftp-style directory listing
ex: telebit tcp ~/Public ex: show listing of ~/Public
tcp none [remote] disable tcp access for [remote] port
ex: telebit tcp none 6565 ex: remove access to port 6565
Use cases:
- Debugging plain TCP when troubleshooting a legacy app
- You can't install a secure client (like telebit, sclient, openssl, or stunnel)
See also sclient <https://telebit.cloud/sclient> for connecting to legacy apps
with telebit-upscaled secure https access."
scp = "Telebit (Client) scp
See \"telebit rsync\"."
rsync = "Telebit (Client) rsync - Sync files to or from another computer
Sync files and directories from one computer to another.
Usage:
telebit rsync [flags] <src> <dst> [arguments]
ex: telebit rsync -av home.jon.telebit.cloud:shared/ ~/shared/ --exclude=tmp
This is not a full implementation of rsync, but rather a convenience wrapper
around rsync which passes the correct options to ssh for https tunneling.
Due to the way telebit wraps rsync, all flags which take an argumnt must
go after the source and destination paths / addresses.
See also: telebit help ssh-proxy"
vpn = "Telebit (Client) vpn - Use with Firefox for UNSTOPPABLE web browsing
This provides a very easy-to-use, lightweight VPN known as Socks5 that can be
used directly by Firefox and Chrome without requiring administrator privileges.
Usage:
telebit vpn --socks5 <port> <remote>
ex: telebit vpn --socks5 6789 home.jon.telebit.io
The flags are:
--socks5 <port> You MUST specify the socks5 port
Firefox Configuration:
Firefox -> Preferences
Advanced -> Network
Connection -> Settings
Manual proxy configuration:
SOCKS Host: localhost
Port: 6789
SOCKS v5
Just like a full vpn client, it routes your IP traffic places through the VPN
server (which in this case is another one of your telebit devices), but only
for traffic in the configured browser. You can still access school and office
resources in the other browser (and other applications) the need to switch a
full VPN on and off.
As will all other telebit functionality, this use https tunneling and will not
be disrupted by unfavorable network conditions.
Use cases:
- Watch your US Netflix using your home IP while traveling abroad.
- Log into your router as if from inside your home network.
- Disregard poorly configured web proxies at school or work.
See also: telebit help ssh-proxy"
ftp = "Telebit (Client) Secure FTP
Alias of \"telebit rsync\"
The original FTP was superseded by sftp and then rsync a few decades ago,
however, sometimes we refer to its successors, generically, as \"FTP\"
(just like you might say \"hang up\" the phone).
## History
FTP is a legacy of the 1970s. It served its purpose well on local networks, but
was extremely dangerous on the Internet due to its lack of security and various
vulnerabilities. On some legacy systems it remains an easy target to steal
passwords and load viruses onto computers.
Although very few systems have ftp installed today (thank goodness), almost every
computer comes with rsync already installed and ready to go.
Use \"telebit rsync\" instead."
daemon = "telebit daemon v{version}
Usage:
telebit daemon --config <path>
ex: telebit daemon --config ~/.config/telebit/telebitd.yml
Additional help topics:
config config file format and settings
remote telebit cli remote control
Copyright 2015-2018 https://telebit.cloud MPL-2.0 Licensed"
config = "Telebit Config (docs)
There are TWO config files:
remote ~/.config/telebit/telebit.yml
daemon ~/.config/telebit/telebitd.yml
### Remote Config
This only specifies the ipc - socket path (dir), address, or pipe name.
All other options are handled by the daemon.
ipc: /Users/aj/.local/share/telebit/var/run/
### Daemon Config
relay: telebit.cloud the relay to use
secret: null HMAC secret for self-hosted relay
email: jon@example.com the email to authenticate
agree_tos: true agree to Telebit, Greenlock, & Let's Encrypt, ToS
community_member: true get rare but relevant community updates
telemetry: true contribute to project telemetry
servernames:
example.com: don't reject https traffic for example.com
wildcard: true allow assignment to subdomains
handler: ~/Public whether to use a static server by path or app by port
home.example.com:
wildcard: true
handler: 3000
ssh_auto: 22 forward ssh-ish traffic to port 22
See also: telebit help relay"
sclient = "sclient
Usage:
sclient [flags] <remote> [local]
ex: sclient whatever.com:443 localhost:3000
ex: sclient whatever.com -
ex: printf \"GET / HTTP/1.1\\n\\n\" | sclient whatever.com
sclient is a standalane tls unwrapper. For convenience it's bundled with telebit
as the passthru subcommand \"telebit sclient\" and functions exactly the name.
telebit sclient [flags] <remote> [local]
ex: printf \"GET / HTTP/1.1\\n\\n\" | telebit sclient whatever.com
See https://telebit.cloud/sclient/"
relay = "Telebit Relay
We envision a future with better routers capable of providing reliable Internet
connectivity, and trusted peers bridging the gaps between unfavorable network
conditions.
We plan to always run telebit.cloud as a relay-as-a-service for convenience,
but it is our hope that, if your network conditions permit, you will also run
your own telebit relay for your friends, family, and yourself.
See https://git.coolaj86.com/coolaj86/telebit-relay.js"
in-n-out = "Telebit Secret Menu
The secret flags are:
--profile <name> Use config files, sockets, and pipes with this name.
For debugging and development. (default: telbit, telebitd)
--set-profile <name> Switch from the default profile
--address <path|host:port> Use explicit socket path (or address) or pipe name
Overrides \"--profile\""
[remote]
version = "telebit remote v{version}"
code = "
==============================================
Hey, Listen!
==============================================
GO CHECK YOUR EMAIL!
DEVICE PAIR CODE: 0000
==============================================
"
waiting = "waiting for you to check your email..."
success = "Success"
next_steps = "Some fun things to try first:
~/telebit http ~/Public
~/telebit tcp 5050
~/telebit ssh auto
Press any key to continue...
"
[remote.setup]
email = "Welcome!
By using Telebit you agree to:
[x] Accept the Telebit terms of service
[x] Accept the Let's Encrypt terms of service
Enter your email to agree and login/create your account:
"
[daemon]
version = "telebit daemon v{version}"

View File

@ -1,19 +0,0 @@
'use strict';
module.exports = function (opts) {
console.log("Could not connect");
var socket = opts.socket;
var handler = opts.handler;
var http = require('http');
var server = http.createServer(function (req, res) {
console.log('responding to thing');
res.statusCode = 500;
res.setHeader('Content-Type', 'text/html');
res.end("<html>"
+ "<head><title>Couldn't Connect</title></head>"
+ "<body>Could not connect to localhost:" + handler + "</body>"
+ "</html>");
});
//server.emit('connection', socket);
socket.end("Could not connect to localhost:" + handler);
};

Binary file not shown.

Before

Width:  |  Height:  |  Size: 43 B

View File

@ -1,31 +0,0 @@
body {
font-family: Source Sans Pro, sans-serif;
font-size: 18px;
color: #1a1a1a;
letter-spacing: -0.022222222em;
line-height: 1.33;
margin: 0;
text-align: center;
padding: 2em 0 2em 0;
}
code {}
code, pre {
font-family: Source Code Pro, monospace;
}
.code-block {
text-align: left;
display: inline-block;
}
span.logo {
font-size: 1.666em;
font-weight: bold;
}
p {margin-bottom: 0.5em;margin-top: 1.5em;}

View File

@ -1,95 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Telebit</title>
<meta charset="utf-8">
<link href="./css/main.css" rel="stylesheet">
<style>
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-display: block;
font-weight: 400;
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(./fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Source Sans Pro';
font-style: normal;
font-weight: 700;
font-display: block;
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(./fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 400;
src: local('Source Code Pro'), local('SourceCodePro-Regular'), url(./fonts/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
</style>
<link rel="preload" href="./fonts/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdu.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="./fonts/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7l.woff2" as="font" crossorigin="anonymous">
<link rel="preload" href="./fonts/HI_SiYsKILxRpg3hIP6sJ7fM7PqlPevW.woff2" as="font" crossorigin="anonymous">
</head>
<body>
<script>document.body.hidden = true;</script>
<!-- let's define our SVG that we will use later -->
<svg width="0" height="0" viewBox="0 0 24 24">
<defs>
<g id="svg-lock">
<path d="M0 0h24v24H0z" fill="none"/>
<path d="M18 8h-1V6c0-2.76-2.24-5-5-5S7 3.24 7 6v2H6c-1.1 0-2 .9-2 2v10c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V10c0-1.1-.9-2-2-2zm-6 9c-1.1 0-2-.9-2-2s.9-2 2-2 2 .9 2 2-.9 2-2 2zm3.1-9H8.9V6c0-1.71 1.39-3.1 3.1-3.1 1.71 0 3.1 1.39 3.1 3.1v2z"/>
</g>
</defs>
</svg>
<span class="logo">Telebit</span>
<h1>Welcome Home <!-- as in 127.0.0.1, y'know ;) --></h1>
<div>Go ahead and bookmark this page. It's yours now.</div>
<div>
<h2>You've claimed <span class="js-servername">{{servername}}</span></h2>
<p>Here are some ways you can use Telebit via Terminal or other Command Line Interface:</p>
<div class="code-block">
<br />
<pre><code>~/telebit ssh auto # allows you to connect to your computer with <br /> ssh-over-https from a different computer</span></code></pre>
<pre><code>~/telebit http ~/Public # serve a public folder
~/telebit http 3000 # forward all https traffic to localhost:3000
~/telebit http none # remove all https handlers</code></pre>
</div>
</div>
<p>And remember you can <em>always</em> tunnel <strong>SSH over HTTPS</strong>,
even while you're using it for something else:</p>
<p>&nbsp;</p>
<details>
<p><summary><strong>Here are some examples for those of you that want to access files and folders remotely. </strong></summary></p>
<p><strong>This function allows you to connect one computer to another computer you also have SSH on.</strong></p>
<div class="code-block"><pre><code>~/telebit ssh <span class="js-servername">{{servername}}</span></code></pre>
<br>
- or -
<pre><code>ssh -o ProxyCommand='<a href="https://telebit.cloud/sclient">sclient</a> %h' <span class="js-servername">{{servername}}</span></code></pre>
- or -
<pre><code>proxy_cmd='openssl s_client -connect %h:443 -servername %h -quiet'
ssh -o ProxyCommand="$proxy_cmd" <span class="js-servername">{{servername}}</span></code></pre>
</div>
<pre><code>ssh -o ProxyCommand='openssl s_client -connect %h:443 -servername %h -quiet' <span class="js-servername">{{servername}}</span></code></pre>
</details>
<!--div class="js-port" hidden>
<h2>You've claimed port <span class="js-serviceport">{{serviceport}}</span></h2>
<p>Here's some ways you can use it:</p>
<div class="code-block"><pre><code>telebit tcp 3000 # forward all tcp traffic to localhost:3000
telebit tcp /path/to/module # handle incoming tcp traffic with a node module
telebit tcp none # remove all tcp handlers</code></pre>
</div>
<p>You can <em>always</em> use this port for <strong>SSH</strong>, even while you're using it for something else:</p>
<div class="code-block"><pre><code>telebit ssh 22
ssh <span class="js-servername">{{servername}}</span> -p <span class="js-serviceport">{{serviceport}}</span></code></pre></div>
</div -->
<script src="js/app.js"></script>
</body>
</html>

View File

@ -1,49 +0,0 @@
(function () {
'use strict';
document.body.hidden = false;
var hash = window.location.hash.replace(/^[\/#?]+/, '');
var query = window.location.search;
function parseQuery(search) {
var args = search.substring(1).split('&');
var argsParsed = {};
var i, arg, kvp, key, value;
for (i=0; i < args.length; i++) {
arg = args[i];
if (-1 === arg.indexOf('=')) {
argsParsed[decodeURIComponent(arg).trim()] = true;
} else {
kvp = arg.split('=');
key = decodeURIComponent(kvp[0]).trim();
value = decodeURIComponent(kvp[1]).trim();
argsParsed[key] = value;
}
}
return argsParsed;
}
document.querySelectorAll('.js-servername').forEach(function ($el) {
$el.innerText = window.location.host;
});
console.log(parseQuery(hash));
console.log(parseQuery(query));
var port = parseQuery(hash).serviceport || parseQuery(query).serviceport;
if (port) {
document.querySelector('.js-port').hidden = false;
document.querySelectorAll('.js-serviceport').forEach(function ($el) {
$el.innerText = port;
});
}
}());

View File

@ -1,17 +1,11 @@
(function () { (function () {
'use strict'; 'use strict';
var PromiseA;
try {
PromiseA = require('bluebird');
} catch(e) {
PromiseA = global.Promise;
}
var WebSocket = require('ws'); var WebSocket = require('ws');
var PromiseA = require('bluebird');
var sni = require('sni'); var sni = require('sni');
var Packer = require('proxy-packer'); var Packer = require('proxy-packer');
var os = require('os'); var os = require('os');
var EventEmitter = require('events').EventEmitter;
function timeoutPromise(duration) { function timeoutPromise(duration) {
return new PromiseA(function (resolve) { return new PromiseA(function (resolve) {
@ -19,58 +13,37 @@ function timeoutPromise(duration) {
}); });
} }
function TelebitRemote(state) { function _connect(state) {
// jshint latedef:false // jshint latedef:false
var defaultHttpTimeout = (2 * 60);
if (!(this instanceof TelebitRemote)) { var activityTimeout = state.activityTimeout || (defaultHttpTimeout - 5) * 1000;
return new TelebitRemote(state);
}
EventEmitter.call(this);
var me = this;
var priv = {};
//var defaultHttpTimeout = (2 * 60);
//var activityTimeout = state.activityTimeout || (defaultHttpTimeout - 5) * 1000;
var activityTimeout = 6 * 1000;
var pongTimeout = state.pongTimeout || 10*1000; var pongTimeout = state.pongTimeout || 10*1000;
// Allow the tunnel client to be created with no token. This will prevent the connection from // Allow the tunnel client to be created with no token. This will prevent the connection from
// being established initialy and allows the caller to use `.append` for the first token so // being established initialy and allows the caller to use `.append` for the first token so
// they can get a promise that will provide feedback about invalid tokens. // they can get a promise that will provide feedback about invalid tokens.
priv.tokens = []; var tokens = [];
var auth; var auth;
if(!state.sortingHat) {
state.sortingHat = "./sorting-hat.js";
}
if (state.token) { if (state.token) {
if ('undefined' === state.token) { tokens.push(state.token);
throw new Error("passed string 'undefined' as token");
}
priv.tokens.push(state.token);
} }
var wstunneler; var wstunneler;
var authenticated = false; var authenticated = false;
var authsent = false; var authsent = false;
var initialConnect = true;
priv.localclients = {}; var localclients = {};
var pausedClients = []; var pausedClients = [];
var clientHandlers = { var clientHandlers = {
add: function (conn, cid, tun) { add: function (conn, cid, tun) {
priv.localclients[cid] = conn; localclients[cid] = conn;
console.info("[connect] new client '" + tun.name + ":" + tun.serviceport + "' for '" + cid + "'" console.info("[connect] new client '" + cid + "' for '" + tun.name + ":" + tun.serviceport + "' "
+ "(" + clientHandlers.count() + " clients)"); + "(" + clientHandlers.count() + " clients)");
conn.tunnelCid = cid; conn.tunnelCid = cid;
if (tun.data) {
conn.tunnelRead = tun.data.byteLength; conn.tunnelRead = tun.data.byteLength;
} else {
conn.tunnelRead = 0;
}
conn.tunnelWritten = 0; conn.tunnelWritten = 0;
conn.on('data', function onLocalData(chunk) { conn.on('data', function onLocalData(chunk) {
//var chunk = conn.read();
if (conn.tunnelClosing) { if (conn.tunnelClosing) {
console.warn("[onLocalData] received data for '"+cid+"' over socket after connection was ended"); console.warn("[onLocalData] received data for '"+cid+"' over socket after connection was ended");
return; return;
@ -82,10 +55,8 @@ function TelebitRemote(state) {
// down the data we are getting to send over. We also want to pause all active connections // down the data we are getting to send over. We also want to pause all active connections
// if any connections are paused to make things more fair so one connection doesn't get // if any connections are paused to make things more fair so one connection doesn't get
// stuff waiting for all other connections to finish because it tried writing near the border. // stuff waiting for all other connections to finish because it tried writing near the border.
var bufSize = sendMessage(Packer.packHeader(tun, chunk)); var bufSize = wsHandlers.sendMessage(Packer.pack(tun, chunk));
// Sending 2 messages instead of copying the buffer if (pausedClients.length || bufSize > 1024*1024) {
var bufSize2 = sendMessage(chunk);
if (pausedClients.length || (bufSize + bufSize2) > 1024*1024) {
// console.log('[onLocalData] paused connection', cid, 'to allow websocket to catch up'); // console.log('[onLocalData] paused connection', cid, 'to allow websocket to catch up');
conn.pause(); conn.pause();
pausedClients.push(conn); pausedClients.push(conn);
@ -94,33 +65,32 @@ function TelebitRemote(state) {
var sentEnd = false; var sentEnd = false;
conn.on('end', function onLocalEnd() { conn.on('end', function onLocalEnd() {
//console.info("[onLocalEnd] connection '" + cid + "' ended, will probably close soon"); console.info("[onLocalEnd] connection '" + cid + "' ended, will probably close soon");
conn.tunnelClosing = true; conn.tunnelClosing = true;
if (!sentEnd) { if (!sentEnd) {
sendMessage(Packer.packHeader(tun, null, 'end')); wsHandlers.sendMessage(Packer.pack(tun, null, 'end'));
sentEnd = true; sentEnd = true;
} }
}); });
conn.on('error', function onLocalError(err) { conn.on('error', function onLocalError(err) {
console.info("[onLocalError] connection '" + cid + "' errored:", err); console.info("[onLocalError] connection '" + cid + "' errored:", err);
if (!sentEnd) { if (!sentEnd) {
var packBody = true; wsHandlers.sendMessage(Packer.pack(tun, {message: err.message, code: err.code}, 'error'));
sendMessage(Packer.packHeader(tun, {message: err.message, code: err.code}, 'error', packBody));
sentEnd = true; sentEnd = true;
} }
}); });
conn.on('close', function onLocalClose(hadErr) { conn.on('close', function onLocalClose(hadErr) {
delete priv.localclients[cid]; delete localclients[cid];
console.log('[onLocalClose] closed "' + cid + '" read:'+conn.tunnelRead+', wrote:'+conn.tunnelWritten+' (' + clientHandlers.count() + ' clients)'); console.log('[onLocalClose] closed "' + cid + '" read:'+conn.tunnelRead+', wrote:'+conn.tunnelWritten+' (' + clientHandlers.count() + ' clients)');
if (!sentEnd) { if (!sentEnd) {
sendMessage(Packer.packHeader(tun, null, hadErr && 'error' || 'end')); wsHandlers.sendMessage(Packer.pack(tun, null, hadErr && 'error' || 'end'));
sentEnd = true; sentEnd = true;
} }
}); });
} }
, write: function (cid, opts) { , write: function (cid, opts) {
var conn = priv.localclients[cid]; var conn = localclients[cid];
if (!conn) { if (!conn) {
return false; return false;
} }
@ -137,13 +107,11 @@ function TelebitRemote(state) {
conn.tunnelRead += opts.data.byteLength; conn.tunnelRead += opts.data.byteLength;
if (!conn.remotePaused && conn.bufferSize > 1024*1024) { if (!conn.remotePaused && conn.bufferSize > 1024*1024) {
var packBody = true; wsHandlers.sendMessage(Packer.pack(opts, conn.tunnelRead, 'pause'));
sendMessage(Packer.packHeader(opts, conn.tunnelRead, 'pause', packBody));
conn.remotePaused = true; conn.remotePaused = true;
conn.once('drain', function () { conn.once('drain', function () {
var packBody = true; wsHandlers.sendMessage(Packer.pack(opts, conn.tunnelRead, 'resume'));
sendMessage(Packer.packHeader(opts, conn.tunnelRead, 'resume', packBody));
conn.remotePaused = false; conn.remotePaused = false;
}); });
} }
@ -151,13 +119,13 @@ function TelebitRemote(state) {
} }
, closeSingle: function (cid) { , closeSingle: function (cid) {
if (!priv.localclients[cid]) { if (!localclients[cid]) {
return; return;
} }
//console.log('[closeSingle]', cid); console.log('[closeSingle]', cid);
PromiseA.resolve().then(function () { PromiseA.resolve().then(function () {
var conn = priv.localclients[cid]; var conn = localclients[cid];
conn.tunnelClosing = true; conn.tunnelClosing = true;
conn.end(); conn.end();
@ -175,49 +143,40 @@ function TelebitRemote(state) {
}); });
}); });
}).then(function () { }).then(function () {
if (priv.localclients[cid]) { if (localclients[cid]) {
console.warn('[closeSingle]', cid, 'connection still present after calling `end`'); console.warn('[closeSingle]', cid, 'connection still present after calling `end`');
priv.localclients[cid].destroy(); localclients[cid].destroy();
return timeoutPromise(500); return timeoutPromise(500);
} }
}).then(function () { }).then(function () {
if (priv.localclients[cid]) { if (localclients[cid]) {
console.error('[closeSingle]', cid, 'connection still present after calling `destroy`'); console.error('[closeSingle]', cid, 'connection still present after calling `destroy`');
delete priv.localclients[cid]; delete localclients[cid];
} }
}).catch(function (err) { }).catch(function (err) {
console.error('[closeSingle] failed to close connection', cid, err.toString()); console.error('[closeSingle] failed to close connection', cid, err.toString());
delete priv.localclients[cid]; delete localclients[cid];
}); });
} }
, closeAll: function () { , closeAll: function () {
console.log('[closeAll]'); console.log('[closeAll]');
Object.keys(priv.localclients).forEach(function (cid) { Object.keys(localclients).forEach(function (cid) {
clientHandlers.closeSingle(cid); clientHandlers.closeSingle(cid);
}); });
} }
, count: function () { , count: function () {
return Object.keys(priv.localclients).length; return Object.keys(localclients).length;
} }
}; };
var pendingCommands = {}; var pendingCommands = {};
function sendMessage(msg) {
// There is a chance that this occurred after the websocket was told to close
// and before it finished, in which case we don't need to log the error.
if (wstunneler.readyState !== wstunneler.CLOSING) {
wstunneler.send(msg, {binary: true});
return wstunneler.bufferedAmount;
}
}
function sendCommand(name) { function sendCommand(name) {
var id = Math.ceil(1e9 * Math.random()); var id = Math.ceil(1e9 * Math.random());
var cmd = [id, name].concat(Array.prototype.slice.call(arguments, 1)); var cmd = [id, name].concat(Array.prototype.slice.call(arguments, 1));
if (state.debug) { console.log('[DEBUG] command sending', cmd); } if (state.debug) { console.log('[DEBUG] command sending', cmd); }
var packBody = true; wsHandlers.sendMessage(Packer.pack(null, cmd, 'control'));
sendMessage(Packer.packHeader(null, cmd, 'control', packBody));
setTimeout(function () { setTimeout(function () {
if (pendingCommands[id]) { if (pendingCommands[id]) {
console.warn('command', name, id, 'timed out'); console.warn('command', name, id, 'timed out');
@ -240,6 +199,24 @@ function TelebitRemote(state) {
}); });
} }
function sendAllTokens() {
if (auth) {
authsent = true;
sendCommand('auth', auth).catch(function (err) { console.error('1', err); });
}
tokens.forEach(function (jwtoken) {
if (state.debug) { console.log('[DEBUG] send token'); }
authsent = true;
sendCommand('add_token', jwtoken)
.catch(function (err) {
console.error('failed re-adding token', jwtoken, 'after reconnect', err);
// Not sure if we should do something like remove the token here. It worked
// once or it shouldn't have stayed in the list, so it's less certain why
// it would have failed here.
});
});
}
function noHandler(cmd) { function noHandler(cmd) {
console.warn("[telebit] state.handlers['" + cmd[1] + "'] not set"); console.warn("[telebit] state.handlers['" + cmd[1] + "'] not set");
console.warn(cmd[2]); console.warn(cmd[2]);
@ -247,23 +224,6 @@ function TelebitRemote(state) {
var connCallback; var connCallback;
function hyperPeek(tun) {
var m;
var str;
if (tun.data) {
if ('http' === tun.service) {
str = tun.data.toString();
m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
tun._name = tun._hostname = (m && m[1].toLowerCase() || '').split(':')[0];
}
else if ('https' === tun.service || 'tls' === tun.service) {
tun._name = tun._servername = sni(tun.data);
} else {
tun._name = '';
}
}
}
var packerHandlers = { var packerHandlers = {
oncontrol: function (opts) { oncontrol: function (opts) {
var cmd, err; var cmd, err;
@ -295,21 +255,7 @@ function TelebitRemote(state) {
if (cmd[1] === 'hello') { if (cmd[1] === 'hello') {
if (state.debug) { console.log('[DEBUG] hello received'); } if (state.debug) { console.log('[DEBUG] hello received'); }
if (auth) { sendAllTokens();
authsent = true;
sendCommand('auth', auth).catch(function (err) { console.error('1', err); });
}
priv.tokens.forEach(function (jwtoken) {
if (state.debug) { console.log('[DEBUG] send token'); }
authsent = true;
sendCommand('add_token', jwtoken)
.catch(function (err) {
console.error('failed re-adding token', jwtoken, 'after reconnect', err);
// Not sure if we should do something like remove the token here. It worked
// once or it shouldn't have stayed in the list, so it's less certain why
// it would have failed here.
});
});
if (connCallback) { if (connCallback) {
connCallback(); connCallback();
} }
@ -336,19 +282,28 @@ function TelebitRemote(state) {
err = { message: 'unknown command "'+cmd[1]+'"', code: 'E_UNKNOWN_COMMAND' }; err = { message: 'unknown command "'+cmd[1]+'"', code: 'E_UNKNOWN_COMMAND' };
} }
var packBody = true; wsHandlers.sendMessage(Packer.pack(null, [-cmd[0], err], 'control'));
sendMessage(Packer.packHeader(null, [-cmd[0], err], 'control', packBody));
} }
, onconnection: function (tun) { , onmessage: function (tun) {
var cid = tun._id = Packer.addrToId(tun); var cid = tun._id = Packer.addrToId(tun);
var str;
var m;
// this data should have been gathered already as part of the proxy protocol if ('http' === tun.service) {
// but if it's available again here we can double check str = tun.data.toString();
hyperPeek(tun); m = str.match(/(?:^|[\r\n])Host: ([^\r\n]+)[\r\n]*/im);
tun._name = tun._hostname = (m && m[1].toLowerCase() || '').split(':')[0];
}
else if ('https' === tun.service || 'tls' === tun.service) {
tun._name = tun._servername = sni(tun.data);
} else {
tun._name = '';
}
// TODO use readable streams instead if (clientHandlers.write(cid, tun)) { return; }
wstunneler._socket.pause();
wstunneler.pause();
require(state.sortingHat).assign(state, tun, function (err, conn) { require(state.sortingHat).assign(state, tun, function (err, conn) {
if (err) { if (err) {
err.message = err.message.replace(/:tun_id/, tun._id); err.message = err.message.replace(/:tun_id/, tun._id);
@ -357,46 +312,32 @@ function TelebitRemote(state) {
} }
clientHandlers.add(conn, cid, tun); clientHandlers.add(conn, cid, tun);
if (tun.data) { conn.write(tun.data); } if (tun.data) { conn.write(tun.data); }
wstunneler._socket.resume(); wstunneler.resume();
}); });
} }
, onmessage: function (tun) {
var cid = tun._id = Packer.addrToId(tun);
var handled;
hyperPeek(tun);
handled = clientHandlers.write(cid, tun);
// quasi backwards compat
if (!handled) { console.log("[debug] did not get 'connection' event"); packerHandlers.onconnection(tun); }
}
, onpause: function (opts) { , onpause: function (opts) {
var cid = Packer.addrToId(opts); var cid = Packer.addrToId(opts);
if (priv.localclients[cid]) { if (localclients[cid]) {
console.log("[TunnelPause] pausing '"+cid+"', remote received", opts.data.toString(), 'of', priv.localclients[cid].tunnelWritten, 'sent'); console.log("[TunnelPause] pausing '"+cid+"', remote received", opts.data.toString(), 'of', localclients[cid].tunnelWritten, 'sent');
priv.localclients[cid].manualPause = true; localclients[cid].manualPause = true;
priv.localclients[cid].pause(); localclients[cid].pause();
} else { } else {
console.log('[TunnelPause] remote tried pausing finished connection', cid); console.log('[TunnelPause] remote tried pausing finished connection', cid);
// Often we have enough latency that we've finished sending before we're told to pause, so // Often we have enough latency that we've finished sending before we're told to pause, so
// don't worry about sending back errors, since we won't be sending data over anyway. // don't worry about sending back errors, since we won't be sending data over anyway.
// var packBody = true; // wsHandlers.sendMessage(Packer.pack(opts, {message: 'no matching connection', code: 'E_NO_CONN'}, 'error'));
// sendMessage(Packer.packHeader(opts, {message: 'no matching connection', code: 'E_NO_CONN'}, 'error', packBody));
} }
} }
, onresume: function (opts) { , onresume: function (opts) {
var cid = Packer.addrToId(opts); var cid = Packer.addrToId(opts);
if (priv.localclients[cid]) { if (localclients[cid]) {
console.log("[TunnelResume] resuming '"+cid+"', remote received", opts.data.toString(), 'of', priv.localclients[cid].tunnelWritten, 'sent'); console.log("[TunnelResume] resuming '"+cid+"', remote received", opts.data.toString(), 'of', localclients[cid].tunnelWritten, 'sent');
priv.localclients[cid].manualPause = false; localclients[cid].manualPause = false;
priv.localclients[cid].resume(); localclients[cid].resume();
} else { } else {
console.log('[TunnelResume] remote tried resuming finished connection', cid); console.log('[TunnelResume] remote tried resuming finished connection', cid);
// var packBody = true; // wsHandlers.sendMessage(Packer.pack(opts, {message: 'no matching connection', code: 'E_NO_CONN'}, 'error'));
// sendMessage(Packer.packHeader(opts, {message: 'no matching connection', code: 'E_NO_CONN'}, 'error', packBody));
} }
} }
@ -413,74 +354,55 @@ function TelebitRemote(state) {
, _onConnectError: function (cid, opts, err) { , _onConnectError: function (cid, opts, err) {
console.info("[_onConnectError] opening '" + cid + "' failed because " + err.message); console.info("[_onConnectError] opening '" + cid + "' failed because " + err.message);
sendMessage(Packer.packHeader(opts, null, 'error')); wsHandlers.sendMessage(Packer.pack(opts, null, 'error'));
} }
}; };
priv.timeoutId = null; var lastActivity;
priv.lastActivity = Date.now(); var timeoutId;
priv.refreshTimeout = function refreshTimeout() { var wsHandlers = {
priv.lastActivity = Date.now(); refreshTimeout: function () {
}; lastActivity = Date.now();
priv.checkTimeout = function checkTimeout() { }
, checkTimeout: function () {
if (!wstunneler) { if (!wstunneler) {
console.warn('checkTimeout called when websocket already closed'); console.warn('checkTimeout called when websocket already closed');
return; return;
} }
// Determine how long the connection has been "silent", ie no activity. // Determine how long the connection has been "silent", ie no activity.
var silent = Date.now() - priv.lastActivity; var silent = Date.now() - lastActivity;
// If we have had activity within the last activityTimeout then all we need to do is // If we have had activity within the last activityTimeout then all we need to do is
// call this function again at the soonest time when the connection could be timed out. // call this function again at the soonest time when the connection could be timed out.
if (silent < activityTimeout) { if (silent < activityTimeout) {
priv.timeoutId = setTimeout(priv.checkTimeout, activityTimeout-silent); timeoutId = setTimeout(wsHandlers.checkTimeout, activityTimeout-silent);
} }
// Otherwise we check to see if the pong has also timed out, and if not we send a ping // Otherwise we check to see if the pong has also timed out, and if not we send a ping
// and call this function again when the pong will have timed out. // and call this function again when the pong will have timed out.
else if (silent < activityTimeout + pongTimeout) { else if (silent < activityTimeout + pongTimeout) {
//console.log('DEBUG: pinging tunnel server'); console.log('pinging tunnel server');
try { try {
wstunneler.ping(); wstunneler.ping();
} catch (err) { } catch (err) {
console.warn('failed to ping tunnel server', err); console.warn('failed to ping tunnel server', err);
} }
priv.timeoutId = setTimeout(priv.checkTimeout, pongTimeout); timeoutId = setTimeout(wsHandlers.checkTimeout, pongTimeout);
} }
// Last case means the ping we sent before didn't get a response soon enough, so we // Last case means the ping we sent before didn't get a response soon enough, so we
// need to close the websocket connection. // need to close the websocket connection.
else { else {
console.info('[info] closing due to connection timeout'); console.log('connection timed out');
wstunneler.close(1000, 'connection timeout'); wstunneler.close(1000, 'connection timeout');
} }
};
me.destroy = function destroy() {
console.info('[info] destroy()');
try {
//wstunneler.close(1000, 're-connect');
wstunneler._socket.destroy();
} catch(e) {
// ignore
} }
};
me.connect = function connect() {
if (!priv.tokens.length && state.config.email) {
auth = TelebitRemote._tokenFromState(state);
}
priv.timeoutId = null;
var machine = Packer.create(packerHandlers);
console.info("[telebit:lib/remote.js] [connect] '" + (state.wss || state.relay) + "'"); , onOpen: function () {
var tunnelUrl = (state.wss || state.relay).replace(/\/$/, '') + '/'; // + auth; console.info("[open] connected to '" + state.relay + "'");
wstunneler = new WebSocket(tunnelUrl, { rejectUnauthorized: !state.insecure }); wsHandlers.refreshTimeout();
// XXXXXX timeoutId = setTimeout(wsHandlers.checkTimeout, activityTimeout);
wstunneler.on('open', function () {
console.info("[telebit:lib/remote.js] [open] connected to '" + (state.wss || state.relay) + "'");
me.emit('connect');
priv.refreshTimeout();
priv.timeoutId = setTimeout(priv.checkTimeout, activityTimeout);
wstunneler._socket.on('drain', function () { wstunneler._socket.on('drain', function () {
// the websocket library has it's own buffer apart from node's socket buffer, but that one // the websocket library has it's own buffer apart from node's socket buffer, but that one
// is much more difficult to watch, so we watch for the lower level buffer to drain and // is much more difficult to watch, so we watch for the lower level buffer to drain and
@ -497,13 +419,15 @@ function TelebitRemote(state) {
conn.resume(); conn.resume();
} }
}); });
pausedClients.length = 0; pausedClients.length = 0;
}); });
initialConnect = false; }
});
wstunneler.on('close', function () { , onClose: function () {
console.info("[info] [closing] received close signal from relay"); console.log('ON CLOSE');
clearTimeout(priv.timeoutId); clearTimeout(timeoutId);
wstunneler = null;
clientHandlers.closeAll(); clientHandlers.closeAll();
var error = new Error('websocket connection closed before response'); var error = new Error('websocket connection closed before response');
@ -515,49 +439,52 @@ function TelebitRemote(state) {
connCallback(error); connCallback(error);
} }
me.emit('close'); if (!authenticated) {
}); console.info('[close] failed on first attempt... check authentication.');
wstunneler.on('error', function (err) { timeoutId = null;
me.emit('error', err); }
}); else if (tokens.length) {
console.info('[retry] disconnected and waiting...');
timeoutId = setTimeout(connect, 5000);
}
}
// Our library will automatically handle sending the pong respose to ping requests. , onError: function (err) {
wstunneler.on('ping', priv.refreshTimeout); console.error("[tunnel error] " + err.message);
wstunneler.on('pong', function () { console.error(err);
//console.log('DEBUG received pong'); if (connCallback) {
priv.refreshTimeout(); connCallback(err);
}); }
wstunneler.on('message', function (data, flags) { }
priv.refreshTimeout();
if (data.error || '{' === data[0]) { , sendMessage: function (msg) {
console.log(data); if (wstunneler) {
try {
wstunneler.send(msg, {binary: true});
return wstunneler.bufferedAmount;
} catch (err) {
// There is a chance that this occurred after the websocket was told to close
// and before it finished, in which case we don't need to log the error.
if (wstunneler.readyState !== wstunneler.CLOSING) {
console.warn('[sendMessage] error sending websocket message', err);
}
}
}
}
};
function connect() {
if (wstunneler) {
console.warn('attempted to connect with connection already active');
return; return;
} }
machine.fns.addChunk(data, flags); if (!tokens.length) {
}); if (state.config.email) {
}; auth = {
me.end = function() {
priv.tokens.length = 0;
if (priv.timeoutId) {
clearTimeout(priv.timeoutId);
priv.timeoutId = null;
}
console.info('[info] closing due to tr.end()');
wstunneler.close(1000, 're-connect');
wstunneler.on('close', function () {
me.emit('end');
});
};
}
TelebitRemote.prototype = EventEmitter.prototype;
TelebitRemote._tokenFromState = function (state) {
return {
subject: state.config.email subject: state.config.email
, subject_scheme: 'mailto' , subject_scheme: 'mailto'
// TODO create domains list earlier // TODO create domains list earlier
, scope: Object.keys(state.config.servernames || {}).join(',') , scope: Object.keys(state.config.servernames || {}).join(',')
, otp: state.otp
, hostname: os.hostname() , hostname: os.hostname()
// Used for User-Agent // Used for User-Agent
, os_type: os.type() , os_type: os.type()
@ -565,19 +492,125 @@ TelebitRemote._tokenFromState = function (state) {
, os_release: os.release() , os_release: os.release()
, os_arch: os.arch() , os_arch: os.arch()
}; };
}; }
}
timeoutId = null;
var machine = Packer.create(packerHandlers);
TelebitRemote.create = function (opts) { console.info("[connect] '" + state.relay + "'");
return new TelebitRemote(opts); var tunnelUrl = state.relay.replace(/\/$/, '') + '/'; // + auth;
}; wstunneler = new WebSocket(tunnelUrl, { rejectUnauthorized: !state.insecure });
TelebitRemote.createConnection = function (opts, cb) { wstunneler.on('open', wsHandlers.onOpen);
var tunnel = TelebitRemote.create(opts); wstunneler.on('close', wsHandlers.onClose);
tunnel.connect(opts); wstunneler.on('error', wsHandlers.onError);
tunnel.once('connect', cb);
return tunnel;
};
TelebitRemote.connect = TelebitRemote.createConnection;
module.exports.TelebitRemote = TelebitRemote; // Our library will automatically handle sending the pong respose to ping requests.
wstunneler.on('ping', wsHandlers.refreshTimeout);
wstunneler.on('pong', wsHandlers.refreshTimeout);
wstunneler.on('message', function (data, flags) {
wsHandlers.refreshTimeout();
if (data.error || '{' === data[0]) {
console.log(data);
return;
}
machine.fns.addChunk(data, flags);
});
}
connect();
var connPromise;
return {
end: function() {
tokens.length = 0;
if (timeoutId) {
clearTimeout(timeoutId);
timeoutId = null;
}
if (wstunneler) {
try {
wstunneler.close();
} catch(e) {
console.error("[error] wstunneler.close()");
console.error(e);
}
}
}
, append: function (token) {
if (tokens.indexOf(token) >= 0) {
return PromiseA.resolve();
}
tokens.push(token);
var prom;
if (tokens.length === 1 && !wstunneler) {
// We just added the only token in the list, and the websocket connection isn't up
// so we need to restart the connection.
if (timeoutId) {
// Handle the case were the last token was removed and this token added between
// reconnect attempts to make sure we don't try openning multiple connections.
clearTimeout(timeoutId);
timeoutId = null;
}
// We want this case to behave as much like the other case as we can, but we don't have
// the same kind of reponses when we open brand new connections, so we have to rely on
// the 'hello' and the 'un-associated' error commands to determine if the token is good.
prom = connPromise = new PromiseA(function (resolve, reject) {
connCallback = function (err) {
connCallback = null;
connPromise = null;
if (err) {
reject(err);
} else {
resolve();
}
};
});
connect();
}
else if (connPromise) {
prom = connPromise.then(function () {
return sendCommand('add_token', token);
});
}
else {
prom = sendCommand('add_token', token);
}
prom.catch(function (err) {
console.error('adding token', token, 'failed:', err);
// Most probably an invalid token of some kind, so we don't really want to keep it.
tokens.splice(tokens.indexOf(token), 1);
});
return prom;
}
, clear: function (token) {
if (typeof token === 'undefined') {
token = '*';
}
if (token === '*') {
tokens.length = 0;
} else {
var index = tokens.indexOf(token);
if (index < 0) {
return PromiseA.resolve();
}
tokens.splice(index);
}
var prom = sendCommand('delete_token', token);
prom.catch(function (err) {
console.error('clearing token', token, 'failed:', err);
});
return prom;
}
};
}
module.exports.connect = _connect;
module.exports.createConnection = _connect;
}()); }());

View File

@ -1,9 +1,3 @@
'use strict';
var os = require('os');
var path = require('path');
var fs = require('fs');
module.exports.print = function (config) { module.exports.print = function (config) {
var services = { https: {}, http: {}, tcp: {} }; var services = { https: {}, http: {}, tcp: {} };
// Note: the remote needs to know: // Note: the remote needs to know:
@ -59,23 +53,17 @@ module.exports.print = function (config) {
}; };
module.exports.assign = function (state, tun, cb) { module.exports.assign = function (state, tun, cb) {
//console.log('first message from', tun); console.log('first message from', tun);
var net = state.net || require('net'); var net = state.net || require('net');
function trySsh(tun, cb) { function trySsh(tun, cb) {
// https://security.stackexchange.com/questions/43231/plausibly-deniable-ssh-does-it-make-sense?rq=1 // https://security.stackexchange.com/questions/43231/plausibly-deniable-ssh-does-it-make-sense?rq=1
// https://tools.ietf.org/html/rfc4253#section-4.2 // https://tools.ietf.org/html/rfc4253#section-4.2
var sshPort; if (false === state.config.ssh_auto || 'SSH-2.0-' !== tun.data.slice(0, 8).toString()) {
if (-1 !== ['true', 'enable', 'auto', 'on'].indexOf(state.config.sshAuto)) {
sshPort = 22;
} else {
sshPort = parseInt(state.config.sshAuto, 10);
}
if (!sshPort || 'SSH-2.0-' !== tun.data.slice(0, 8).toString()) {
cb(null, false); cb(null, false);
return; return;
} }
getNetConn(sshPort, cb); cb(null, getNetConn(state.config.sshPort || 22));
} }
var handlers = {}; var handlers = {};
@ -88,24 +76,19 @@ module.exports.assign = function (state, tun, cb) {
state.httpRedirectServer = require('http').createServer(state.greenlock.middleware(state.redirectHttps)); state.httpRedirectServer = require('http').createServer(state.greenlock.middleware(state.redirectHttps));
} }
state.httpRedirectServer.emit('connection', socket); state.httpRedirectServer.emit('connection', socket);
process.nextTick(function () { socket.resume(); });
}; };
handlers.https = function (tlsSocket) { handlers.https = function (tlsSocket) {
console.log('Encrypted', tlsSocket.encrypted, tlsSocket.remoteAddress, tlsSocket.remotePort); console.log('Enccrypted', tlsSocket.encrypted, tlsSocket.remoteAddress, tlsSocket.remotePort);
if (!state.defaultHttpServer) { if (!state.defaultHttpServer) {
state._finalHandler = require('finalhandler');
state._serveStatic = require('serve-static');
state._defaultServe = state._serveStatic(path.join(__dirname, 'html'));
state.defaultHttpServer = require('http').createServer(function (req, res) { state.defaultHttpServer = require('http').createServer(function (req, res) {
// TODO serve api console.log('[hit http/s server]');
state._defaultServe(req, res, state._finalHandler(req, res)); res.end('Hello, Encrypted Tunnel World!');
}); });
} }
state.defaultHttpServer.emit('connection', tlsSocket); state.defaultHttpServer.emit('connection', tlsSocket);
process.nextTick(function () { tlsSocket.resume(); });
}; };
function getNetConn(port, cb) { function getNetConn(port) {
var netOpts = { var netOpts = {
port: port port: port
, host: '127.0.0.1' , host: '127.0.0.1'
@ -122,12 +105,8 @@ module.exports.assign = function (state, tun, cb) {
// this will happen before 'data' or 'readable' is triggered // this will happen before 'data' or 'readable' is triggered
// We use the data from the netOpts object so that the createConnection function has // We use the data from the netOpts object so that the createConnection function has
// the oppurtunity of removing/changing it if it wants/needs to handle it differently. // the oppurtunity of removing/changing it if it wants/needs to handle it differently.
cb(null, conn);
cb = function () {}; // for error events
});
conn.on('error', function (err) {
cb(err);
}); });
return conn;
} }
function redirectHttp(cb) { function redirectHttp(cb) {
@ -141,40 +120,6 @@ module.exports.assign = function (state, tun, cb) {
return conn; return conn;
} }
function errorTcp(conf, cb) {
var socketPair = require('socket-pair');
var conn = socketPair.create(function (err, other) {
if (err) { cb(err); return; }
cb(null, conn);
other.write("\n" +
[ "[Telebit Error Server]"
, "Could not load '" + conf.handler + "' as a module, file, or directory."
].join("\n") + "\n\n");
other.end();
});
//if (tun.data) { conn.write(tun.data); }
return conn;
}
function fileDirTcp(opts, cb) {
var socketPair = require('socket-pair');
var conn = socketPair.create(function (err, other) {
if (err) { cb(err); return; }
if (opts.stat.isFile()) {
fs.createReadStream(opts.config.handler).pipe(other);
} else {
fs.readdir(opts.config.handler, function (err, nodes) {
other.write('\n' + nodes.join('\n') + '\n\n');
other.end();
});
}
cb(null, conn);
});
//if (tun.data) { conn.write(tun.data); }
return conn;
}
function echoTcp(cb) { function echoTcp(cb) {
var socketPair = require('socket-pair'); var socketPair = require('socket-pair');
var conn = socketPair.create(function (err, other) { var conn = socketPair.create(function (err, other) {
@ -190,14 +135,7 @@ module.exports.assign = function (state, tun, cb) {
cb(null, conn); cb(null, conn);
other.write("\n" + other.write("[Telebit Echo Server] v1.0\nPlease edit your config file to port forward somewhere more useful.\n\n");
[ "[Telebit Echo Server] v1.0"
, "To configure tcp run the following:"
, "\ttelebit tcp <port number or module name>"
, "\tex: telebit tcp 5050"
, "\tex: telebit tcp /path/to/module"
, "\tex: telebit tcp none"
].join("\n") + "\n\n");
}); });
//if (tun.data) { conn.write(tun.data); } //if (tun.data) { conn.write(tun.data); }
return conn; return conn;
@ -234,181 +172,41 @@ module.exports.assign = function (state, tun, cb) {
}); });
} }
function invokeTcpHandler(conf, socket, tun, id, cb) {
var conn;
if (parseInt(conf.handler, 10)) {
getNetConn(conf.handler, cb);
return conn;
}
var handle = tun.port;
var handler;
var handlerpath = conf.handler;
var homedir = os.homedir();
var localshare = path.join(homedir, '.local/share/telebit/apps');
if (/^~/.test(handlerpath)) {
handlerpath = path.join(homedir, handlerpath.replace(/^~(\/?)/, ''));
}
try {
handler = require(handlerpath);
console.info("Handling '" + handle + ":" + id + "' with '" + handlerpath + "'");
} catch(e1) {
try {
handler = require(path.join(localshare, handlerpath));
console.info("Handling '" + handle + ":" + id + "' with '" + handlerpath + "'");
} catch(e2) {
console.error("Failed to require('" + handlerpath + "'):", e1.message);
console.error("Failed to require('" + path.join(localshare, handlerpath) + "'):", e2.message);
console.warn("Trying static and index handlers for '" + handle + ":" + id + "'");
handler = null;
// fallthru
}
}
if (handler) {
var socketPair = require('socket-pair');
conn = socketPair.create(function (err, other) {
handler(other, tun, id);
cb(null, conn);
});
return conn;
}
fs.access(conf.handler, fs.constants.R_OK, function (err1) {
fs.stat(conf.handler, function (err2, stat) {
if ((err1 || err2) || !(stat.isFile() || stat.isDirectory())) {
errorTcp(conf, cb);
return;
}
fileDirTcp({ config: conf, stat: stat }, cb);
});
});
}
var handlerservers = {};
function invokeHandler(conf, tlsSocket, tun, id) { function invokeHandler(conf, tlsSocket, tun, id) {
if (parseInt(conf.handler, 10)) { if (parseInt(conf.handler, 10)) {
// TODO http-proxy with proper headers and ws support // TODO http-proxy with proper headers and ws support
getNetConn(conf.handler, function (err, conn) { var conn = getNetConn(conf.handler);
process.nextTick(function () { tlsSocket.resume(); });
if (err) {
require('./handlers/local-app-error.js')({ handler: conf.handler, socket: tlsSocket });
return;
}
console.info("Port-Forwarding '" + (tun.name || tun.serviceport) + "' to '" + conf.handler + "'"); console.info("Port-Forwarding '" + (tun.name || tun.serviceport) + "' to '" + conf.handler + "'");
conn.pipe(tlsSocket); conn.pipe(tlsSocket);
tlsSocket.pipe(conn); tlsSocket.pipe(conn);
});
return; return;
} }
var handle = tun.name || tun.port; var handle = tun.name || tun.port;
var handler; var handler;
var handlerpath = conf.handler; var path = require('path');
var homedir = os.homedir(); var homedir = require('os').homedir();
var localshare = path.join(homedir, '.local/share/telebit/apps'); var localshare = path.join(homedir, '.local/share/telebit/apps');
var http = require('http');
// 1. No modification handlerpath may be an aboslute path if (/^~/.test(conf.handler)) {
// 2. it may be relative to a user home directory conf.handler = require('path').join(require('os').homedir(), conf.handler.replace(/^~(\/?)/, ''));
// 3. it may be relative to a user ~/local/share
tlsSocket._tun = tun;
tlsSocket._id = id;
if (handlerservers[conf.handler]) {
handlerservers[conf.handler].emit('connection', tlsSocket);
process.nextTick(function () { tlsSocket.resume(); });
return;
}
if (/^~/.test(handlerpath)) {
// TODO have the telebit remote tell which user is running
handlerpath = path.join(homedir, handlerpath.replace(/^~(\/?)/, ''));
} }
try { try {
handler = require(handlerpath); handler = require(conf.handler);
console.info("Trying to handle '" + handle + ":" + id + "' with '" + handlerpath + "'"); console.info("Handling '" + handle + ":" + id + "' with '" + conf.handler + "'");
handler(tlsSocket, tun, id);
} catch(e1) { } catch(e1) {
try { try {
handler = require(path.join(localshare, handlerpath)); handler = require(path.join(localshare, conf.handler));
console.info("Skip. (couldn't require('" + handlerpath + "'):", e1.message + ")"); console.info("Handling '" + handle + ":" + id + "' with '" + conf.handler + "'");
console.info("Trying to handle '" + handle + ":" + id + "' with '" + handlerpath + "'"); handler(tlsSocket, tun, id);
} catch(e2) { } catch(e2) {
console.info("Skip. (couldn't require('" + path.join(localshare, handlerpath) + "'):", e2.message + ")"); console.error("Failed to load '" + conf.handler + "':", e1.message);
console.info("Last chance! (using static and index handlers for '" + handle + ":" + id + "')"); console.error("Failed to load '" + path.join(localshare, conf.handler) + "':", e2.message);
handler = null; console.warn("Using default handler for '" + handle + ":" + id + "'");
// fallthru
}
}
if (handler) {
handlerservers[conf.handler] = http.createServer(handler);
handlerservers[conf.handler].emit('connection', tlsSocket);
process.nextTick(function () { tlsSocket.resume(); });
return;
}
fs.access(conf.handler, fs.constants.R_OK, function (err1) {
fs.stat(conf.handler, function (err2, stat) {
if (err1 || err2) {
// TODO handle errors
handlers.https(tlsSocket, tun, id); handlers.https(tlsSocket, tun, id);
return;
} }
var isFile = stat.isFile();
state._finalHandler = require('finalhandler');
state._serveStatic = require('serve-static');
state._serveIndex = require('serve-index');
var serveIndex;
var serveStatic;
var dlStatic;
if (isFile) {
serveStatic = state._serveStatic(path.dirname(conf.handler), { dotfiles: 'allow', index: [ 'index.html' ] });
dlStatic = state._serveStatic(path.dirname(conf.handler), { acceptRanges: false, dotfiles: 'allow', index: [ 'index.html' ] });
serveIndex = function (req, res, next) { next(); };
isFile = path.basename(conf.handler);
} else {
serveStatic = state._serveStatic(conf.handler, { dotfiles: 'allow', index: [ 'index.html' ] });
dlStatic = state._serveStatic(conf.handler, { acceptRanges: false, dotfiles: 'allow', index: [ 'index.html' ] });
serveIndex = state._serveIndex(conf.handler, {
hidden: true, icons: true
, template: require('serve-tpl-attachment')({ privatefiles: 'ignore' })
});
} }
handler = function (req, res) {
var qIndex = req.url.indexOf('?');
var fIndex;
var fname;
if (-1 === qIndex) {
qIndex = req.url.length;
}
req.querystring = req.url.substr(qIndex);
req.url = req.url.substr(0, qIndex);
req.query = require('querystring').parse(req.querystring.substr(1));
if (isFile) {
req.url = '/' + isFile;
}
//console.log('[req.query]', req.url, req.query);
if (req.query.download) {
fIndex = req.url.lastIndexOf('/');
fname = req.url.substr(fIndex + 1);
res.setHeader('Content-Disposition', 'attachment; filename="'+decodeURIComponent(fname)+'"');
res.setHeader('Content-Type', 'application/octet-stream');
dlStatic(req, res, function () {
serveIndex(req, res, state._finalHandler(req, res));
});
} else {
serveStatic(req, res, function () {
serveIndex(req, res, state._finalHandler(req, res));
});
}
};
handlerservers[conf.handler] = http.createServer(handler);
handlerservers[conf.handler].emit('connection', tlsSocket);
process.nextTick(function () { tlsSocket.resume(); });
});
});
} }
function terminateTls(tun, cb) { function terminateTls(tun, cb) {
@ -433,26 +231,28 @@ module.exports.assign = function (state, tun, cb) {
defineProps(tlsSocket, addr); defineProps(tlsSocket, addr);
//console.log('[hit tls server]', tlsSocket.remoteFamily, tlsSocket.remoteAddress, tlsSocket.remotePort, tlsSocket.localPort); //console.log('[hit tls server]', tlsSocket.remoteFamily, tlsSocket.remoteAddress, tlsSocket.remotePort, tlsSocket.localPort);
//console.log(addr); //console.log(addr);
var conf = state.servernames[tlsSocket.servername]; var conf = state.config.servernames[tlsSocket.servername];
tlsSocket.once('data', function (firstChunk) { tlsSocket.once('data', function (firstChunk) {
tlsSocket.pause(); tlsSocket.pause();
//tlsSocket.unshift(firstChunk); //tlsSocket.unshift(firstChunk);
tlsSocket._handle.onread(firstChunk.length, firstChunk); tlsSocket._handle.onread(firstChunk.length, firstChunk);
trySsh({ data: firstChunk }, function (err, conn) { trySsh({ data: firstChunk }, function (err, conn) {
process.nextTick(function () { tlsSocket.resume(); });
if (conn) { if (conn) {
conn.pipe(tlsSocket); conn.pipe(tlsSocket);
tlsSocket.pipe(conn); tlsSocket.pipe(conn);
return; return;
} }
if (!conf || !conf.handler || 'none' === conf.handler) { if (!conf || !conf.handler) {
console.log('https default handler'); console.log('https default handler');
handlers.https(tlsSocket); handlers.https(tlsSocket);
return; return;
} }
//console.log('https invokeHandler'); console.log('https invokeHandler');
invokeHandler(conf, tlsSocket, tun, id); invokeHandler(conf, tlsSocket, tun, id);
}); });
}); });
@ -476,6 +276,11 @@ module.exports.assign = function (state, tun, cb) {
return; return;
} }
if (!state.config.servernames) {
state.config.servernames = {};
}
if ('http' === tun.service || 'https' === tun.service) { if ('http' === tun.service || 'https' === tun.service) {
if (!tun.name) { if (!tun.name) {
cb(new Error("No routing information for ':tun_id'. Service '" + tun.service + "' is missing 'name'.")); cb(new Error("No routing information for ':tun_id'. Service '" + tun.service + "' is missing 'name'."));
@ -485,13 +290,13 @@ module.exports.assign = function (state, tun, cb) {
if ('http' === tun.service) { if ('http' === tun.service) {
// TODO match *.example.com // TODO match *.example.com
handled = Object.keys(state.servernames).some(function (sn) { handled = Object.keys(state.config.servernames).some(function (sn) {
if (sn !== tun.name) { return; } if (sn !== tun.name) { return; }
console.log('Found config match for PLAIN', tun.name); console.log('Found config match for PLAIN', tun.name);
if (!state.servernames[sn]) { return; } if (!state.config.servernames[sn]) { return; }
if (false === state.servernames[sn].terminate) { if (false === state.config.servernames[sn].terminate) {
cb(new Error("insecure http not supported yet")); cb(new Error("insecure http not supported yet"));
return true; return true;
} }
@ -508,13 +313,13 @@ module.exports.assign = function (state, tun, cb) {
if ('https' === tun.service) { if ('https' === tun.service) {
// TODO match *.example.com // TODO match *.example.com
handled = Object.keys(state.servernames).some(function (sn) { handled = Object.keys(state.config.servernames).some(function (sn) {
if (sn !== tun.name) { return; } if (sn !== tun.name) { return; }
console.log('Found config match for TLS', tun.name); console.log('Found config match for TLS', tun.name);
if (!state.servernames[sn]) { return; } if (!state.config.servernames[sn]) { return; }
if (false === state.servernames[sn].terminate) { if (false === state.config.servernames[sn].terminate) {
cb(new Error("insecure http not supported yet")); cb(new Error("insecure http not supported yet"));
return true; return true;
} }
@ -533,17 +338,8 @@ module.exports.assign = function (state, tun, cb) {
trySsh(tun, function (err, conn) { trySsh(tun, function (err, conn) {
if (conn) { cb(null, conn); return; } if (conn) { cb(null, conn); return; }
// TODO add TCP handlers // TODO add TCP handlers
var conf = state.ports[tun.serviceport];
if (!conf || !conf.handler || 'none' === conf.handler) {
console.log('Using echo server for tcp'); console.log('Using echo server for tcp');
echoTcp(cb); echoTcp(cb);
return;
}
var Packer = require('proxy-packer');
//var addr = Packer.socketToAddr(conn);
var id = Packer.addrToId(tun);
invokeTcpHandler(conf, conn, tun, id, cb);
}); });
return; return;
} }

View File

@ -1,141 +0,0 @@
'use strict';
module.exports = function (pkg) {
function checkUpgrade() {
var https = require('https');
function getFile(url, cb) {
https.get(url, function (resp) {
var str = '';
resp.on('data', function (chunk) {
//var chunk = conn.read();
str += chunk.toString('utf8');
});
resp.on('end', function () {
cb(null, str);
});
resp.on('error', function (err) {
// ignore
cb(err);
});
}).on('error', function (err) {
// ignore
cb(err);
});
}
function isNewer(latest, myPkg) {
//console.log('sort result:', sortLatest(latest, myPkg));
return sortLatest(latest, myPkg) < 0;
}
function sortLatest(latest, myPkg) {
var m = /^(v)?(\d+)\.(\d+)\.(\d+)(.*)/.exec(latest);
var n = /^(v)?(\d+)\.(\d+)\.(\d+)(.*)/.exec(myPkg);
//console.log('m', m);
//console.log('n', n);
if (!m) {
if (!n) {
return 0;
}
return 1;
} else if (!n) {
return -1;
}
if (parseInt(m[2], 10) > parseInt(n[2], 10)) {
return -1;
} else if (parseInt(m[2], 10) === parseInt(n[2], 10)) {
if (parseInt(m[3], 10) > parseInt(n[3], 10)) {
return -1;
} else if (parseInt(m[3], 10) === parseInt(n[3], 10)) {
if (parseInt(m[4], 10) > parseInt(n[4], 10)) {
return -1;
} else if (parseInt(m[4], 10) === parseInt(n[4], 10)) {
// lex sorting
if (m[5] > n[5]) {
return -1;
} else if (m[5] === n[5]) {
return 0;
} else {
return 1;
}
} else {
return 1;
}
} else {
return 1;
}
} else {
return 1;
}
}
getFile("https://telebit.cloud/dist/index.tab", function (err, tab) {
if (err) { /*ignore*/ return; }
if (tab) { tab = tab && tab.toString() || ''; }
var versions = [];
var lines = tab.split(/[\r\n]/g);
var headers = lines.shift().split(/\t/g);
var chan = 'prod';
var next;
lines.forEach(function (line) {
var tsv = {};
var fields = line.split(/\t/g);
fields.forEach(function (value, i) {
tsv[headers[i]] = value;
});
versions.push(tsv);
});
// find matching version
versions.some(function (v) {
if (('v' + pkg.version) === v.version) {
chan = v.channel;
return true;
}
});
// find first (most recent) version in channel
versions.some(function (v) {
if (chan === v.channel) {
next = v;
return true;
}
});
if (!next || !isNewer(next.version, pkg.version)) {
//console.log('DEBUG can\'t upgrade from', pkg.version, 'in channel', chan);
return;
}
console.log('Upgrade Available: ' + next.version + ' in \'' + next.channel + '\'channel');
getFile("https://telebit.cloud/dist/upgrade.js", function (err, script) {
if (err) { /*ignore*/ return; }
var os = require('os');
var fs = require('fs');
var path = require('path');
var scriptname = 'telebit-upgrade-' + Math.round(Math.random() * 99999) + '.js';
var pathname = path.join(os.tmpdir(), scriptname);
fs.writeFile(pathname, script, function (err) {
if (err) { /*ignore*/ return; }
// console.log('DEBUG wrote', pathname);
//var str =
require(pathname)({
package: pkg
, root: path.resolve(__dirname, '..')
, latest: next
, channel: chan
}, function () {
// console.log('upgrade complete');
});
//console.log(str);
});
});
});
}
var _interval = setInterval(checkUpgrade, 2 * 60 * 60 * 1000);
process.nextTick(function () {
checkUpgrade();
});
return function cancel() {
clearInterval(_interval);
};
};

View File

@ -1,16 +1,10 @@
{ {
"name": "telebit", "name": "telebit",
"version": "0.20.8", "version": "0.12.0",
"description": "Break out of localhost. Connect to any device from anywhere over any tcp port or securely in a browser. A secure tunnel. A poor man's reverse VPN.", "description": "Break out of localhost. Connect to any device from anywhere over any tcp port or securely in a browser. A secure tunnel. A poor man's reverse VPN.",
"main": "lib/remote.js", "main": "lib/remote.js",
"files": [
"bin",
"lib",
"usr"
],
"bin": { "bin": {
"telebit": "bin/telebit.js", "telebit": "bin/telebit.js"
"telebitd": "bin/telebitd.js"
}, },
"scripts": { "scripts": {
"test": "echo \"Error: no test specified\" && exit 1" "test": "echo \"Error: no test specified\" && exit 1"
@ -53,30 +47,16 @@
}, },
"homepage": "https://git.coolaj86.com/coolaj86/telebit.js#readme", "homepage": "https://git.coolaj86.com/coolaj86/telebit.js#readme",
"dependencies": { "dependencies": {
"@coolaj86/urequest": "^1.3.5", "bluebird": "^3.5.1",
"finalhandler": "^1.1.1", "commander": "^2.9.0",
"greenlock": "^2.3.1", "greenlock": "^2.2.19",
"js-yaml": "^3.11.0", "js-yaml": "^3.11.0",
"jsonwebtoken": "^7.1.9", "jsonwebtoken": "^7.1.9",
"mkdirp": "^0.5.1", "proxy-packer": "^1.4.3",
"proxy-packer": "^2.0.2",
"ps-list": "^5.0.0",
"recase": "^1.0.4", "recase": "^1.0.4",
"redirect-https": "^1.1.5", "redirect-https": "^1.1.5",
"sclient": "^1.4.1",
"serve-index": "^1.9.1",
"serve-static": "^1.13.2",
"serve-tpl-attachment": "^1.0.4",
"sni": "^1.0.0", "sni": "^1.0.0",
"socket-pair": "^1.0.3", "socket-pair": "^1.0.3",
"toml": "^0.4.1", "ws": "^2.2.3"
"ws": "^6.0.0"
},
"trulyOptionalDependencies": {
"bluebird": "^3.5.1"
},
"enginesStrict": true,
"engines": {
"node": "10.2.1 10.4 10.6"
} }
} }

View File

@ -1,21 +0,0 @@
There are a number of conditions and whatnot that must be tested in more-or-less real-world conditions.
telebit init // fresh install
telebit init // after install complete
telebit http 3000 // have an app listening on localhost:3000
telebit http 4545 // do not have an app listening
telebit http ./path/to/site
telebit http ./path/to/dir
telebit http ./path/to/file
telebit http ./doesnt/exist
telebit ssh auto // do have ssh listening on localhost:22
telebit ssh 4545 // do have ssh listenening
telebit tcp 3000 // have an echo server listening on localhost:3000
telebit tcp 4545 // no server listening
telebit tcp ./path/to/file
telebit tcp ./path/to/dir

View File

@ -1,27 +0,0 @@
'use strict';
var net = require('net');
var server = net.createServer(function (conn) {
function echo(chunk) {
conn.write(chunk);
if (chunk.length <= 10 && /\b(q|quit|end|cancel)\b/i.test(chunk.toString('utf8'))) {
conn.end();
conn.removeListener('data', echo);
}
}
conn.on('data', echo);
// NOTE: early versions of telebit do not support a 'connection' event
// and therefore will say hello after the first message from the client
conn.write(
"[Echo Server] Hello! I'm an echo server.\n"
+ "[Echo Server] I try to be your friend but when I see things like q|quit|end|cancel, I give up.\n"
);
});
server.on('error', function (err) {
console.error("[echo server]");
console.error(err);
});
server.listen(process.argv[2] || 3000, function () {
console.info("Listening on", this.address());
console.info('ctrl+c to cancel');
});

View File

@ -1,37 +0,0 @@
'use strict';
var email = 'jon@example.com';
var pin = Math.round(Math.random() * 999999).toString().padStart(6, '0'); // '321654'
console.log('Pair Code:', pin);
var urequest = require('@coolaj86/urequest');
var req = {
url: 'https://api.telebit.ppl.family/api/telebit.cloud/pair_request'
, method: 'POST'
, headers: { 'cOntEnt-tYpE': 'application/json;charset=utf-8' }
, json: {
subject: email
, subject_scheme: 'mailto'
, scope: ''
, otp: pin
, hostname: "User's Macbook Pro"
, os_type: 'Linux'
, os_platform: 'linux'
, os_release: '4.4.0-116-generic'
, os_arch: 'x64'
}
};
urequest(req, function (err, resp, body) {
if (err) {
console.error(err);
return;
}
console.log('Location:', resp.headers.location);
console.log('Body:');
console.log(body);
/*
{ jwt: '...'
}
*/
});

View File

@ -1,22 +0,0 @@
'use strict';
var stateUrl = 'https://api.telebit.ppl.family/api/telebit.cloud/pair_state/bca27428719e9c67805359f1';
var urequest = require('@coolaj86/urequest');
var req = {
url: stateUrl
, method: 'GET'
, json: true
};
urequest(req, function (err, resp, body) {
if (err) {
console.error(err);
return;
}
console.log('Done:');
console.log(body);
/*
body.status = 'ready' | 'pending' | 'complete' | 'invalid'
body.access_token // only in 'ready' state
*/
});

View File

@ -1,20 +0,0 @@
'use strict';
var os = require('os');
var net = require('net');
var ipc = {
path: /^win/.test(os.platform()) ? '\\\\.\\pipe\\X:/name/of/pipe' : (__dirname + '/tmp.sock')
};
var oldUmask = process.umask(0x0000);
var server = net.createServer();
server.listen({
path: ipc.path || null
, host: 'localhost'
, port: ipc.port || null
, writeableAll: true
, readableAll: true
}, function () {
process.umask(oldUmask);
console.log("Listening on", this.address());
});

View File

@ -1,22 +0,0 @@
'use strict';
var path = require('path');
var spawn = require('child_process').spawn;
var args = [
path.join(__dirname, 'windows-pipe.js')
];
var subprocess = spawn(
'node'
, args
, { detached: true
, stdio: [ 'ignore', process.stdout, process.stderr ]
}
);
//console.log('[debug]', vars.telebitNode, args.join(' '));
subprocess.unref();
subprocess.on('error', function (_err) {
console.error(_err);
});
subprocess.on('exit', function (code, signal) {
console.error('' + code + ' ' + signal + ' failure to launch');
});

View File

@ -6,41 +6,37 @@
<string>Telebit Remote</string> <string>Telebit Remote</string>
<key>ProgramArguments</key> <key>ProgramArguments</key>
<array> <array>
<string>{TELEBIT_NODE}</string> <string>/opt/telebit/bin/node</string>
<string>{TELEBITD_JS}</string> <string>/opt/telebit/bin/telebit.js</string>
<string>daemon</string>
<string>--config</string> <string>--config</string>
<string>{TELEBITD_CONFIG}</string> <string>/opt/telebit/etc/telebit.yml</string>
</array> </array>
<key>EnvironmentVariables</key> <key>EnvironmentVariables</key>
<dict> <dict>
<key>TELEBIT_PATH</key> <key>TELEBIT_PATH</key>
<string>{TELEBIT_PATH}</string> <string>/opt/telebit</string>
<key>NODE_PATH</key> <key>NODE_PATH</key>
<string>{NODE_PATH}</string> <string>/opt/telebit/lib/node_modules</string>
<key>NPM_CONFIG_PREFIX</key> <key>NPM_CONFIG_PREFIX</key>
<string>{NPM_CONFIG_PREFIX}</string> <string>/opt/telebit</string>
</dict> </dict>
<key>UserName</key> <key>UserName</key>
<string>{TELEBIT_USER}</string> <string>root</string>
<key>GroupName</key> <key>GroupName</key>
<string>{TELEBIT_GROUP}</string> <string>wheel</string>
<key>InitGroups</key> <key>InitGroups</key>
<true/> <true/>
<key>RunAtLoad</key> <key>RunAtLoad</key>
<true/> <true/>
<key>KeepAlive</key> <key>KeepAlive</key>
<true/> <dict>
<!--dict>
<key>Crashed</key> <key>Crashed</key>
<true/> <true/>
<key>NetworkState</key>
<true/>
<key>SuccessfulExit</key> <key>SuccessfulExit</key>
<false/> <false/>
</dict--> </dict>
<key>SoftResourceLimits</key> <key>SoftResourceLimits</key>
<dict> <dict>
@ -51,11 +47,11 @@
<dict/> <dict/>
<key>WorkingDirectory</key> <key>WorkingDirectory</key>
<string>{TELEBIT_PATH}</string> <string>/opt/telebit</string>
<key>StandardErrorPath</key> <key>StandardErrorPath</key>
<string>{TELEBIT_LOG_DIR}/telebit.log</string> <string>/opt/telebit/var/log/error.log</string>
<key>StandardOutPath</key> <key>StandardOutPath</key>
<string>{TELEBIT_LOG_DIR}/telebit.log</string> <string>/opt/telebit/var/log/info.log</string>
</dict> </dict>
</plist> </plist>

View File

@ -1,2 +0,0 @@
#!/bin/bash
{TELEBIT_NODE} {TELEBIT_JS} "$@"

View File

@ -1,2 +0,0 @@
#!/bin/bash
{TELEBIT_NODE} {TELEBITD_JS} daemon "$@"

View File

@ -1,57 +1,54 @@
# Pre-req # Pre-req
# sudo adduser telebit --home {TELEBIT_PATH} # sudo adduser telebit --home /opt/telebit
# sudo mkdir -p {TELEBIT_PATH}/ # sudo mkdir -p /opt/telebit/
# sudo chown -R {TELEBIT_USER}:{TELEBIT_GROUP} {TELEBIT_PATH}/ # sudo chown -R telebit:telebit /opt/telebit/
[Unit] [Unit]
Description=Telebit Remote Description=Telebit Remote
Documentation=https://git.coolaj86.com/coolaj86/telebit.js/ Documentation=https://git.coolaj86.com/coolaj86/telebit.js/
; After=network-online.target
; Wants=network-online.target systemd-networkd-wait-online.service
[Service] [Service]
# Restart on crash (bad signal), and also on 'clean' failure (error exit code) # Restart on crash (bad signal), but not on 'clean' failure (error exit code)
# Allow up to 3 restarts within 10 seconds # Allow up to 3 restarts within 10 seconds
# (it's unlikely that a user or properly-running script will do this) # (it's unlikely that a user or properly-running script will do this)
Restart=always Restart=always
StartLimitInterval=10 StartLimitInterval=10
StartLimitBurst=3 StartLimitBurst=3
# User and group the process will run as # https://wiki.archlinux.org/index.php/Systemd/User
;User={TELEBIT_USER} # ~/.local/share/systemd/user/
;Group={TELEBIT_GROUP} WorkingDirectory=%h/.config/telebit
# custom directory cannot be set and will be the place where gitea exists, not the working directory
WorkingDirectory={TELEBIT_PATH} ExecStart=/opt/telebit/bin/node /opt/telebit/bin/telebit.js --config /etc/telebit/telebit.yml
# custom directory cannot be set and will be the place where this exists, not the working directory
ExecStart={TELEBIT_NODE} {TELEBITD_JS} daemon --config {TELEBITD_CONFIG}
ExecReload=/bin/kill -USR1 $MAINPID ExecReload=/bin/kill -USR1 $MAINPID
# Limit the number of file descriptors and processes; see `man systemd.exec` for more limit settings. # Limit the number of file descriptors and processes; see `man systemd.exec` for more limit settings.
# Unmodified, this is not expected to use more than this. # Unmodified gitea is not expected to use more than this.
;LimitNOFILE=1048576 # no issues yet, but disabled just in case LimitNOFILE=1048576
;LimitNPROC=64 # doesn't work on some systems LimitNPROC=64
# Use private /tmp and /var/tmp, which are discarded after this stops. # Use private /tmp and /var/tmp, which are discarded after gitea stops.
PrivateTmp=true PrivateTmp=true
# Use a minimal /dev # Use a minimal /dev
;PrivateDevices=true PrivateDevices=true
# Hide /home, /root, and /run/user. Nobody will steal your SSH-keys. # Hide /home, /root, and /run/user. Nobody will steal your SSH-keys.
ProtectHome=true ProtectHome=true
# Make /usr, /boot, /etc and possibly some more folders read-only. # Make /usr, /boot, /etc and possibly some more folders read-only.
ProtectSystem=full ProtectSystem=full
# ... except for a few because we want a place for config, logs, etc # ... except /opt/gitea because we want a place for the database
# and /var/log/gitea because we want a place where logs can go.
# This merely retains r/w access rights, it does not add any new. # This merely retains r/w access rights, it does not add any new.
# Must still be writable on the host! # Must still be writable on the host!
ReadWriteDirectories={TELEBIT_RW_DIRS} ReadWriteDirectories=/opt/telebit /etc/telebit
# Note: in v231 and above ReadWritePaths has been renamed to ReadWriteDirectories # Note: in v231 and above ReadWritePaths has been renamed to ReadWriteDirectories
; ReadWritePaths={TELEBIT_RW_DIRS} ; ReadWritePaths=/opt/telebit /etc/telebit
# The following additional security directives only work with systemd v229 or later. # The following additional security directives only work with systemd v229 or later.
# They further retrict privileges that can be gained. # They further retrict privileges that can be gained by gitea.
# Note that you may have to add capabilities required by any plugins in use. # Note that you may have to add capabilities required by any plugins in use.
;CapabilityBoundingSet=CAP_NET_BIND_SERVICE CapabilityBoundingSet=CAP_NET_BIND_SERVICE
;AmbientCapabilities=CAP_NET_BIND_SERVICE AmbientCapabilities=CAP_NET_BIND_SERVICE
NoNewPrivileges=true NoNewPrivileges=true
# Caveat: Some features may need additional capabilities. # Caveat: Some features may need additional capabilities.

View File

@ -1,70 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Label</key>
<string>Telebit Remote</string>
<key>ProgramArguments</key>
<array>
<string>{TELEBIT_NODE}</string>
<string>{TELEBITD_JS}</string>
<string>daemon</string>
<string>--config</string>
<string>{TELEBITD_CONFIG}</string>
</array>
<key>EnvironmentVariables</key>
<dict>
<key>TELEBIT_PATH</key>
<string>{TELEBIT_PATH}</string>
<key>NODE_PATH</key>
<string>{NODE_PATH}</string>
<key>NPM_CONFIG_PREFIX</key>
<string>{NPM_CONFIG_PREFIX}</string>
</dict>
<!--
:: LaunchDaemon Only ::
<key>UserName</key>
<string>{TELEBIT_USER}</string>
<key>GroupName</key>
<string>{TELEBIT_GROUP}</string>
<key>InitGroups</key>
<true/>
-->
<key>RunAtLoad</key>
<true/>
<key>KeepAlive</key>
<true/>
<!--
<dict>
<key>Crashed</key>
<true/>
<key>NetworkState</key>
<true/>
<key>SuccessfulExit</key>
<false/>
</dict>
-->
<!--
<key>SoftResourceLimits</key>
<dict>
<key>NumberOfFiles</key>
<integer>8192</integer>
</dict>
<key>HardResourceLimits</key>
<dict/>
-->
<key>WorkingDirectory</key>
<string>{TELEBIT_PATH}</string>
<key>StandardErrorPath</key>
<string>{TELEBIT_LOG_DIR}/telebit.log</string>
<key>StandardOutPath</key>
<string>{TELEBIT_LOG_DIR}/telebit.log</string>
</dict>
</plist>

View File

@ -1,7 +1,7 @@
# Pre-req # Pre-req
# sudo adduser telebit --home {TELEBIT_PATH} # sudo adduser telebit --home /opt/telebit
# sudo mkdir -p {TELEBIT_PATH}/ # sudo mkdir -p /opt/telebit/
# sudo chown -R {TELEBIT_USER}:{TELEBIT_GROUP} {TELEBIT_PATH}/ # sudo chown -R telebit:telebit /opt/telebit/
[Unit] [Unit]
Description=Telebit Remote Description=Telebit Remote
@ -10,7 +10,7 @@ After=network-online.target
Wants=network-online.target systemd-networkd-wait-online.service Wants=network-online.target systemd-networkd-wait-online.service
[Service] [Service]
# Restart on crash (bad signal), and also on 'clean' failure (error exit code) # Restart on crash (bad signal), but not on 'clean' failure (error exit code)
# Allow up to 3 restarts within 10 seconds # Allow up to 3 restarts within 10 seconds
# (it's unlikely that a user or properly-running script will do this) # (it's unlikely that a user or properly-running script will do this)
Restart=always Restart=always
@ -18,12 +18,13 @@ StartLimitInterval=10
StartLimitBurst=3 StartLimitBurst=3
# User and group the process will run as # User and group the process will run as
User={TELEBIT_USER} # (git is the de facto standard on most systems)
Group={TELEBIT_GROUP} User=telebit
Group=telebit
WorkingDirectory={TELEBIT_PATH} WorkingDirectory=/opt/telebit
# custom directory cannot be set and will be the place where this exists, not the working directory # custom directory cannot be set and will be the place where this exists, not the working directory
ExecStart={TELEBIT_NODE} {TELEBITD_JS} daemon --config {TELEBITD_CONFIG} ExecStart=/opt/telebit/bin/node /opt/telebit/bin/telebit.js --config /opt/telebit/etc/telebit.yml
ExecReload=/bin/kill -USR1 $MAINPID ExecReload=/bin/kill -USR1 $MAINPID
# Limit the number of file descriptors and processes; see `man systemd.exec` for more limit settings. # Limit the number of file descriptors and processes; see `man systemd.exec` for more limit settings.
@ -39,13 +40,13 @@ PrivateDevices=true
ProtectHome=true ProtectHome=true
# Make /usr, /boot, /etc and possibly some more folders read-only. # Make /usr, /boot, /etc and possibly some more folders read-only.
ProtectSystem=full ProtectSystem=full
# ... except for a few because we want a place for config, logs, etc # ... except /opt/telebit because we want a place for config, logs, etc
# This merely retains r/w access rights, it does not add any new. # This merely retains r/w access rights, it does not add any new.
# Must still be writable on the host! # Must still be writable on the host!
ReadWriteDirectories={TELEBIT_RW_DIRS} ReadWriteDirectories=/opt/telebit
# Note: in v231 and above ReadWritePaths has been renamed to ReadWriteDirectories # Note: in v231 and above ReadWritePaths has been renamed to ReadWriteDirectories
; ReadWritePaths={TELEBIT_RW_DIRS} ; ReadWritePaths=/opt/telebit
# The following additional security directives only work with systemd v229 or later. # The following additional security directives only work with systemd v229 or later.
# They further retrict privileges that can be gained. # They further retrict privileges that can be gained.
@ -61,7 +62,4 @@ NoNewPrivileges=true
; NoNewPrivileges=true ; NoNewPrivileges=true
[Install] [Install]
# For system-level service WantedBy=multi-user.target
;WantedBy=multi-user.target
# For userspace service
WantedBy=default.target

Binary file not shown.

Before

Width:  |  Height:  |  Size: 36 KiB

View File

@ -1,448 +0,0 @@
'use strict';
//var fs = require('fs');
var os = require('os');
var mkdirp = require('mkdirp');
var exec = require('child_process').exec;
var path = require('path');
var Launcher = module.exports;
Launcher._killAll = function (fn) {
var psList = require('ps-list');
psList().then(function (procs) {
procs.forEach(function (proc) {
if ('node' === proc.name && /\btelebitd\b/i.test(proc.cmd)) {
console.log(proc);
process.kill(proc.pid);
return true;
}
});
// Two things:
// 1) wait to see if the process dies
// 2) wait to give time for the socket to connect
setTimeout(function () {
if (fn) { fn(null); return; }
}, 1.75 * 1000);
});
};
Launcher._getError = function getError(err, stderr) {
if (err) { return err; }
if (stderr) {
err = new Error(stderr);
err.code = 'ELAUNCHER';
return err;
}
};
Launcher._detect = function (things, fn) {
if (things.launcher) {
if ('string' === typeof things.launcher) {
fn(null, things.launcher);
return;
}
if ('function' === typeof things.launcher) {
things.launcher(things);
return;
}
}
// could have used "command-exists" but I'm trying to stay low-dependency
// os.platform(), os.type()
if (!/^win/i.test(os.platform())) {
if (/^darwin/i.test(os.platform())) {
exec('command -v launchctl', things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
fn(err, 'launchctl');
});
} else {
exec('command -v systemctl', things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
fn(err, 'systemctl');
});
}
} else {
// https://stackoverflow.com/questions/17908789/how-to-add-an-item-to-registry-to-run-at-startup-without-uac
// wininit? regedit? SCM?
// REG ADD "HKCU\SOFTWARE\Microsoft\Windows\CurrentVersion\Run" /V "My App" /t REG_SZ /F /D "C:\MyAppPath\MyApp.exe"
// https://www.microsoft.com/developerblog/2015/11/09/reading-and-writing-to-the-windows-registry-in-process-from-node-js/
// https://docs.microsoft.com/en-us/windows-server/administration/windows-commands/reg-add
// https://social.msdn.microsoft.com/Forums/en-US/5b318f44-281e-4098-8dee-3ba8435fa391/add-registry-key-for-autostart-of-app-in-ice?forum=quebectools
// utils.elevate
// https://github.com/CatalystCode/windows-registry-node
exec('where reg.exe', things._execOpts, function (err, stdout, stderr) {
//console.log((stdout||'').trim());
if (stderr) {
console.error(stderr);
}
fn(err, 'reg.exe');
});
}
};
Launcher.install = function (things, fn) {
if (!fn) { fn = function (err) { if (err) { console.error(err); } }; }
things = things || {};
// in some future version we can take this file out
// and accept process.env from things
var installLauncher = require('./template-launcher');
// Right now this is just for npm install -g and npx
if (things.env) {
things.env.PATH = things.env.PATH || process.env.PATH;
} else {
things.env = process.env;
}
things.argv = things.argv || process.argv;
things._execOpts = { windowsHide: true, env: things.env };
var telebitRoot = path.join(__dirname, '../..');
var vars = {
telebitPath: telebitRoot
, telebitUser: os.userInfo().username
, telebitGroup: (/^darwin/i.test(os.platform()) ? 'staff' : os.userInfo().username)
, telebitRwDirs: [
telebitRoot
, path.join(os.homedir(), '.config/telebit')
, path.join(os.homedir(), '.local/share/telebit')
]
, telebitNode: (things.argv[0]||'').replace(/\.exe/i, '') // path.join(telebitRoot, 'bin/node')
, telebitBin: path.join(telebitRoot, 'bin/telebit')
, telebitdBin: path.join(telebitRoot, 'bin/telebitd')
, telebitJs: path.join(telebitRoot, 'bin/telebit.js')
, telebitdJs: path.join(telebitRoot, 'bin/telebitd.js')
, telebitConfig: path.join(os.homedir(), '.config/telebit/telebit.yml')
, telebitdConfig: path.join(os.homedir(), '.config/telebit/telebitd.yml')
, TELEBIT_LOG_DIR: path.join(os.homedir(), '.local/share/telebit/var/log')
, TELEBIT_SOCK_DIR: path.join(os.homedir(), '.local/share/telebit/var/run')
};
vars.telebitBinTpl = path.join(telebitRoot, 'usr/share/dist/bin/telebit.tpl');
vars.telebitNpm = path.resolve(vars.telebitNode, '../npm');
vars.nodePath = path.resolve(vars.telebitNode, '../../lib/node_modules');
vars.npmConfigPrefix = path.resolve(vars.telebitNode, '..', '..');
vars.userspace = (!things.telebitUser || (things.telebitUser === os.userInfo().username)) ? true : false;
if (-1 === vars.telebitRwDirs.indexOf(vars.npmConfigPrefix)) {
vars.telebitRwDirs.push(vars.npmConfigPrefix);
}
vars.telebitRwDirs = vars.telebitRwDirs.join(' ');
var launchers = {
'node': function () {
var fs = require('fs');
var spawn = require('child_process').spawn;
var logpath = path.join(os.homedir(), '.local/share/telebit/var/log');
try {
mkdirp.sync(logpath);
} catch(e) {
if (fn) { fn(e); return; }
return;
}
var stdout = fs.openSync(path.join(logpath, 'info.log'), 'a');
var stderr = fs.openSync(path.join(logpath, 'error.log'), 'a');
var killed = 0;
var err;
var args = [
path.join(telebitRoot, 'bin/telebitd.js')
, 'daemon'
, '--config'
, vars.telebitdConfig
];
var subprocess = spawn(
vars.telebitNode
, args
, { detached: true
, stdio: [ 'ignore', stdout, stderr ]
}
);
//console.log('[debug]', vars.telebitNode, args.join(' '));
subprocess.unref();
subprocess.on('error', function (_err) {
err = _err;
killed += 1;
});
subprocess.on('exit', function (code, signal) {
if (!err) { err = new Error('' + code + ' ' + signal + ' failure to launch'); }
killed += 1;
});
// Two things:
// 1) wait to see if the process dies
// 2) wait to give time for the socket to connect
setTimeout(function () {
if (fn) { fn(err); return; }
}, 1.75 * 1000);
return;
}
, 'launchctl': function () {
var launcher = path.join(os.homedir(), 'Library/LaunchAgents/cloud.telebit.remote.plist');
try {
mkdirp.sync(path.join(os.homedir(), 'Library/LaunchAgents'));
installLauncher.sync({
file: {
tpl: vars.telebitBinTpl
, launcher: path.join(vars.telebitPath, 'bin/telebit')
, executable: true
}
, vars: vars
});
installLauncher({
file: {
tpl: path.join(vars.telebitPath, 'usr/share/dist/etc/skel/Library/LaunchAgents/cloud.telebit.remote.plist.tpl')
, launcher: launcher
}
, vars: vars
});
var launcherstr = (vars.userspace ? "" : "sudo ") + "launchctl ";
var execstr = launcherstr + "unload -w " + launcher;
exec(execstr, things._execOpts, function (/*err, stdout, stderr*/) {
// we probably only need to skip the stderr (saying that it can't stop something that isn't started)
//err = Launcher._getError(err, stderr);
//if (err) { fn(err); return; }
//console.log((stdout||'').trim());
//console.log('unload worked?');
execstr = launcherstr + "load -w " + launcher;
exec(execstr, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
//console.log('load worked?');
setTimeout(function () {
fn(null);
}, 1.25 * 1000);
});
});
} catch(e) {
console.error("'" + launcher + "' error:");
console.error(e);
if (fn) { fn(e); return; }
}
}
, 'systemctl': function () {
var launcher = path.join(os.homedir(), '.config/systemd/user/telebit.service');
var launchername = 'telebit.service';
try {
mkdirp.sync(path.join(os.homedir(), '.config/systemd/user'));
installLauncher({
file: {
tpl: path.join(vars.telebitPath, 'usr/share/dist/etc/skel/.config/systemd/user/telebit.service.tpl')
, launcher: launcher
}
, vars: vars
}, function () {
// IMPORTANT
// It's a dangerous to go alone, take this:
// SYSTEMD_LOG_LEVEL=debug journalctl -xef --user-unit=telebit
// (makes debugging systemd issues not "easy" per se, but possible)
var launcherstr = (vars.userspace ? "" : "sudo ") + "systemctl " + (vars.userspace ? "--user " : "");
var execstr = launcherstr + "daemon-reload";
exec(execstr, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
var execstr = launcherstr + "enable " + launchername;
exec(execstr, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr && !/Created symlink/i.test(stderr) && stderr || '');
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
var execstr = launcherstr + "restart " + launchername;
exec(execstr, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
setTimeout(function () {
var execstr = launcherstr + "status " + launchername;
exec(execstr, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
if (!/active.*running/i.test(stdout)) {
err = new Error("systemd failed to start '" + launchername + "'");
}
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
fn(null);
});
}, 1.25 * 1000);
});
});
});
});
} catch(e) {
console.error("'" + launcher + "' error:");
console.error(e);
if (fn) { fn(e); return; }
}
}
, 'reg.exe': function () {
if (!vars.userspace) {
console.warn("sysetm-level, privileged services are not yet supported on windows");
}
vars.telebitNode += '.exe';
var cmd = 'reg.exe add "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"'
+ ' /V "Telebit" /t REG_SZ /D '
+ '"' + things.argv[0] + ' /c ' // something like C:\\Program Files (x64)\nodejs\node.exe
+ [ path.join(__dirname, 'bin/telebitd.js')
, 'daemon'
, '--config'
, path.join(os.homedir(), '.config/telebit/telebitd.yml')
].join(' ')
+ '" /F'
;
exec(cmd, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
// need to start it for the first time ourselves
run(null, 'node');
});
}
};
function run(err, launcher) {
if (err) {
console.error("No luck with '" + launcher + "', trying a child process instead...");
console.error(err);
launcher = 'node';
}
if (launchers[launcher]) {
// console.log('Launching with launcher ' + launcher);
mkdirp.sync(path.join(vars.telebitPath, 'bin'));
mkdirp.sync(vars.TELEBIT_LOG_DIR);
mkdirp.sync(vars.TELEBIT_SOCK_DIR);
launchers[launcher]();
return;
} else {
console.error("No launcher handler for '" + launcher+ "'");
}
}
things._vars = vars;
things._userspace = vars.userspace;
Launcher._detect(things, run);
};
Launcher.uninstall = function (things, fn) {
if (!fn) { fn = function (err) { if (err) { console.error(err); } }; }
things = things || {};
// Right now this is just for npm install -g and npx
if (things.env) {
things.env.PATH = things.env.PATH || process.env.PATH;
} else {
things.env = process.env;
}
things.argv = things.argv || process.argv;
things._execOpts = { windowsHide: true, env: things.env };
var vars = {
telebitUser: os.userInfo().username
};
vars.userspace = (!things.telebitUser || (things.telebitUser === os.userInfo().username)) ? true : false;
var launchers = {
'node': function () {
Launcher._killAll(fn);
}
, 'launchctl': function () {
var launcher = path.join(os.homedir(), 'Library/LaunchAgents/cloud.telebit.remote.plist');
try {
var launcherstr = (vars.userspace ? "" : "sudo ") + "launchctl ";
var execstr = launcherstr + "unload -w " + launcher;
exec(execstr, things._execOpts, function (err, stdout, stderr) {
// we probably only need to skip the stderr (saying that it can't stop something that isn't started)
//err = Launcher._getError(err, stderr);
//if (err) { fn(err); return; }
//console.log((stdout||'').trim());
//console.log('unload worked?');
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
//console.log('load worked?');
setTimeout(function () {
fn(null);
}, 1.25 * 1000);
});
} catch(e) {
console.error("'" + launcher + "' error (uninstall):");
console.error(e);
if (fn) { fn(e); return; }
}
}
, 'systemctl': function () {
var launcher = path.join(os.homedir(), '.config/systemd/user/telebit.service');
var launchername = 'telebit.service';
try {
mkdirp.sync(path.join(os.homedir(), '.config/systemd/user'));
// IMPORTANT
// It's a dangerous to go alone, take this:
// SYSTEMD_LOG_LEVEL=debug journalctl -xef --user-unit=telebit
// (makes debugging systemd issues not "easy" per se, but possible)
var launcherstr = (vars.userspace ? "" : "sudo ") + "systemctl " + (vars.userspace ? "--user " : "");
var execstr = launcherstr + "disable " + launchername;
exec(execstr, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr && !/Removed symlink/i.test(stderr) && stderr || '');
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
var execstr = launcherstr + "stop " + launchername;
exec(execstr, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
setTimeout(function () {
var execstr = launcherstr + "status " + launchername;
exec(execstr, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
if (!/inactive.*dead/i.test(stdout)) {
err = new Error("systemd failed to stop '" + launchername + "'");
}
if (err) { fn(err); return; }
//console.log((stdout||'').trim());
fn(null);
});
}, 1.25 * 1000);
});
});
} catch(e) {
console.error("'" + launcher + "' error:");
console.error(e);
if (fn) { fn(e); return; }
}
}
, 'reg.exe': function () {
if (!vars.userspace) {
console.warn("sysetm-level, privileged services are not yet supported on windows");
}
var cmd = 'reg.exe add "HKCU\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Run"'
+ ' /V "Telebit" /F'
;
exec(cmd, things._execOpts, function (err, stdout, stderr) {
err = Launcher._getError(err, stderr);
if (err) { fn(err); return; }
// need to start it for the first time ourselves
kill(null, 'node');
});
}
};
function kill(err, launcher) {
if (err) {
console.error("No luck with '" + launcher + "', trying a process.kill() instead...");
console.error(err);
launcher = 'node';
}
if (launchers[launcher]) {
launchers[launcher]();
return;
} else {
console.error("No launcher handler (uninstall) for '" + launcher + "'");
}
}
things._vars = vars;
things._userspace = vars.userspace;
Launcher._detect(things, kill);
};
if (module === require.main) {
Launcher.install({
argv: process.argv
, env: process.env
}, function (err) {
if (err) { console.error(err); return; }
console.log("Telebit launched, or so it seems.");
});
}

View File

@ -59,11 +59,10 @@ http_get()
http_bash() http_bash()
{ {
local _http_bash_url=$1 _http_url=$1
local _http_bash_args=${2:-} my_args=${2:-}
local _http_bash_tmp=$(mktemp) my_tmp=$(mktemp)
$_my_http_get $_my_http_opts $_my_http_out "$_http_bash_tmp" "$_http_bash_url" $_my_http_get $_my_http_opts $_my_http_out "$my_tmp" "$_http_url"; bash "$my_tmp" $my_args; rm "$my_tmp"
bash "$_http_bash_tmp" $_http_bash_args; rm "$_http_bash_tmp"
} }
detect_http_get detect_http_get
@ -74,12 +73,9 @@ export -f http_bash
## END HTTP_GET ## ## END HTTP_GET ##
############################### ###############################
if [ -n "${TELEBIT_VERSION:-}" ]; then my_branch=master
echo 'TELEBIT_VERSION='${TELEBIT_VERSION}
fi
export TELEBIT_VERSION=${TELEBIT_VERSION:-master}
if [ -e "usr/share/install_helper.sh" ]; then if [ -e "usr/share/install_helper.sh" ]; then
bash usr/share/install_helper.sh "$@" bash usr/share/install_helper.sh "$@"
else else
http_bash https://git.coolaj86.com/coolaj86/telebit.js/raw/branch/$TELEBIT_VERSION/usr/share/install_helper.sh "$@" http_bash https://git.coolaj86.com/coolaj86/telebit.js/raw/branch/$my_branch/usr/share/install_helper.sh "$@"
fi fi

View File

@ -26,61 +26,27 @@
# system daemon launcher, etc. Also, this is designed to be # system daemon launcher, etc. Also, this is designed to be
# reusable with many apps and services, so it's very variabled... # reusable with many apps and services, so it's very variabled...
# hack to allow calling script to finish before this executes
sleep 0.1
set -e set -e
set -u set -u
### http_bash exported by get.sh ### http_bash exported by get.sh
TELEBIT_DEBUG=${TELEBIT_DEBUG:-}
# NOTE: On OS X logname works from a pipe, but on Linux it does not
my_logname=$(who am i </dev/tty | awk '{print $1}')
#my_logname=${my_logname:-$(logname)}
#my_logname=${my_logname:-$SUDO_USER}
if [ -n "$my_logname" ] && [ "$my_logname" != "$(id -u -n)" ]; then
echo "WARNING:"
echo " You are logged in as '$(logname)' but acting as '$(id -u -n)'."
echo " If the installation is not successful please log in as '$(id -u -n)' directly."
sleep 3
fi
if [ -n "${TELEBIT_DEBUG:-}" ]; then
echo 'TELEBIT_DEBUG='${TELEBIT_DEBUG}
fi
if [ -n "${TELEBIT_PATH:-}" ]; then
echo 'TELEBIT_PATH='${TELEBIT_PATH}
fi
if [ -n "${TELEBIT_USERSPACE:-}" ]; then
echo 'TELEBIT_USERSPACE='${TELEBIT_USERSPACE}
fi
if [ -n "${TELEBIT_USER:-}" ]; then
echo 'TELEBIT_USER='${TELEBIT_USER}
fi
if [ -n "${TELEBIT_GROUP:-}" ]; then
echo 'TELEBIT_GROUP='${TELEBIT_GROUP}
fi
TELEBIT_VERSION=${TELEBIT_VERSION:-master}
TELEBIT_USERSPACE=${TELEBIT_USERSPACE:-no}
my_email=${1:-} my_email=${1:-}
my_relay=${2:-} my_relay=${2:-}
my_servernames=${3:-} my_servernames=${3:-}
my_secret=${4:-} my_secret=${4:-}
my_user="telebit"
cur_user="$(id -u -n)"
TELEBIT_USER="${TELEBIT_USER:-$cur_user}"
cur_group="$(id -g -n)"
TELEBIT_GROUP="${TELEBIT_GROUP:-$cur_group}"
my_app_pkg_name="cloud.telebit.remote" my_app_pkg_name="cloud.telebit.remote"
my_app="telebit" my_app="telebit"
my_daemon="telebitd"
my_bin="telebit.js" my_bin="telebit.js"
my_name="Telebit Remote" my_name="Telebit Remote"
my_repo="telebit.js" my_repo="telebit.js"
my_root=${my_root:-} # todo better install script my_root=${my_root:-} # todo better install script
soft_sudo_cmd="sudo" sudo_cmd="sudo"
soft_sudo_cmde="sudo " sudo_cmde="sudo "
exec 3<>/dev/tty exec 3<>/dev/tty
read_cmd="read -u 3" read_cmd="read -u 3"
# TODO detect if rsync is available and use rsync -a (more portable) # TODO detect if rsync is available and use rsync -a (more portable)
@ -106,79 +72,92 @@ fi
set -e set -e
if [ "root" == $(whoami) ] || [ 0 == $(id -u) ]; then if [ "root" == $(whoami) ] || [ 0 == $(id -u) ]; then
soft_sudo_cmd=" " sudo_cmd=" "
soft_sudo_cmde="" sudo_cmde=""
fi
if [ -z "${my_email}" ]; then
echo ""
echo ""
echo "Telebit uses Greenlock for free automated ssl through Let's Encrypt."
echo ""
echo "To accept the Terms of Service for Telebit, Greenlock and Let's Encrypt,"
echo "please enter your email."
echo ""
$read_cmd -p "email: " my_email
echo ""
# UX - just want a smooth transition
sleep 0.5
fi
if [ -z "${my_relay}" ]; then
echo "What self-hosted relay will you be using?"
#echo "What relay will you be using? (press enter for default)"
echo ""
#$read_cmd -p "relay [default: wss://telebit.cloud]: " my_relay
$read_cmd -p "relay: " my_relay
echo ""
my_relay=${my_relay:-wss://telebit.cloud}
# UX - just want a smooth transition
sleep 0.5
fi
if [ -z "${my_servernames}" ]; then
#echo "What servername(s) will you be relaying here? (press enter for default)"
echo "What servername(s) will you be relaying here?"
echo ""
#$read_cmd -p "domain [default: <random>.telebit.cloud]: " my_servernames
$read_cmd -p "domain: " my_servernames
echo ""
# UX - just want a smooth transition
sleep 0.5
fi
if [ -z "${my_secret}" ]; then
#echo "What's your authorization for the relay server? (press enter for default)"
echo "What's your authorization for the relay server?"
echo ""
#$read_cmd -p "auth [default: new account]: " my_secret
$read_cmd -p "secret: " my_secret
echo ""
# UX - just want a smooth transition
sleep 0.5
fi fi
echo "" echo ""
TELEBIT_REAL_PATH=${TELEBIT_PATH:-} if [ -z "${TELEBIT_PATH:-}" ]; then
echo 'TELEBIT_PATH="'${TELEBIT_PATH:-}'"'
if [ $(id -u) -ne 0 ] && [ "$TELEBIT_USER" == "$cur_user" ]; then TELEBIT_PATH=/opt/$my_app
TELEBIT_USERSPACE="yes"
if [ -z "${TELEBIT_REAL_PATH:-}" ]; then
TELEBIT_REAL_PATH=$HOME/Applications/$my_app
fi
else
TELEBIT_USERSPACE="no"
if [ -z "${TELEBIT_REAL_PATH:-}" ]; then
TELEBIT_REAL_PATH=/opt/$my_app
fi
fi fi
TELEBIT_PATH="$TELEBIT_REAL_PATH"
TELEBIT_TMP="$TELEBIT_REAL_PATH"
# this works slightly differently between bsd (macOS) and gnu mktemp
# bsd requires the Xes for templates while GNU uses them literally
my_tmp="$(mktemp -d -t telebit.XXXXXXXX)"
#TELEBIT_TMP="$my_tmp/telebit"
echo "Installing $my_name to '$TELEBIT_REAL_PATH'" echo "Installing $my_name to '$TELEBIT_PATH'"
# v10.2+ has much needed networking fixes, but breaks ursa.
# v9.x has severe networking bugs. echo "Installing node.js dependencies into '$TELEBIT_PATH'"
# v8.x has working ursa, but requires tls workarounds" # v10.2+ has much needed networking fixes, but breaks ursa. v9.x has severe networking bugs. v8.x has working ursa, but requires tls workarounds"
# v10.13 seems to work for me locally (new greenlock) NODEJS_VER="${NODEJS_VER:-v10}"
NODEJS_VER="${NODEJS_VER:-v10.13}"
export NODEJS_VER export NODEJS_VER
export NODE_PATH="$TELEBIT_TMP/lib/node_modules" export NODE_PATH="$TELEBIT_PATH/lib/node_modules"
export NPM_CONFIG_PREFIX="$TELEBIT_TMP" export NPM_CONFIG_PREFIX="$TELEBIT_PATH"
# this comes last for security export PATH="$TELEBIT_PATH/bin:$PATH"
export PATH="$PATH:$TELEBIT_REAL_PATH/bin" sleep 0.5
sleep 0.25 echo "(your password may be required to complete installation)"
real_sudo_cmd=$soft_sudo_cmd http_bash https://git.coolaj86.com/coolaj86/node-installer.sh/raw/branch/master/install.sh --no-dev-deps >/dev/null 2>/dev/null
real_sudo_cmde=$soft_sudo_cmde
set +e my_tree="telebit" # my_branch
mkdir -p $my_tmp "$TELEBIT_REAL_PATH" "$TELEBIT_REAL_PATH/etc" "$TELEBIT_REAL_PATH/var/log" 2>/dev/null && \ my_node="$TELEBIT_PATH/bin/node"
chown -R $(id -u -n):$(id -g -n) $my_tmp "$TELEBIT_REAL_PATH" 2>/dev/null my_npm="$my_node $TELEBIT_PATH/bin/npm"
if [ $? -eq 0 ]; then my_tmp="$(mktemp -d)"
soft_sudo_cmd=" " mkdir -p $my_tmp
soft_sudo_cmde=""
else
$soft_sudo_cmd mkdir -p $my_tmp "$TELEBIT_REAL_PATH" "$TELEBIT_REAL_PATH/etc" "$TELEBIT_REAL_PATH/var/log"
$soft_sudo_cmd chown -R $(id -u -n):$(id -g -n) $my_tmp "$TELEBIT_REAL_PATH"
fi
set -e
echo "${sudo_cmde}mkdir -p '$TELEBIT_PATH'"
if [ -n "${TELEBIT_DEBUG}" ]; then $sudo_cmd mkdir -p "$TELEBIT_PATH"
echo " - installing node.js runtime to '$TELEBIT_REAL_PATH'..." $sudo_cmd mkdir -p "$TELEBIT_PATH/etc"
http_bash https://git.coolaj86.com/coolaj86/node-installer.sh/raw/branch/master/install.sh --no-dev-deps $sudo_cmd mkdir -p "$TELEBIT_PATH/var/log"
else $sudo_cmd chown -R $(id -u -n):$(id -g -n) "$TELEBIT_PATH"
echo -n "." #echo "${sudo_cmde}mkdir -p '/etc/$my_app/'"
#bash -c 'while true; do echo -n "."; sleep 2; done' 2>/dev/null & #$sudo_cmd mkdir -p "/etc/$my_app/"
#_my_pid=$! #$sudo_cmd chown $(id -u -n):$(id -g -n) "/etc/$my_app/"
http_bash https://git.coolaj86.com/coolaj86/node-installer.sh/raw/branch/master/install.sh --no-dev-deps >/dev/null 2>/dev/null
#kill $_my_pid >/dev/null 2>/dev/null
fi
#
# TODO create "upgrade" script and run that instead
#
my_node="$TELEBIT_REAL_PATH/bin/node"
tmp_node="$TELEBIT_TMP/bin/node"
my_npm="$my_node $TELEBIT_TMP/bin/npm"
tmp_npm="$tmp_node $TELEBIT_TMP/bin/npm"
#https://git.coolaj86.com/coolaj86/telebit.js.git #https://git.coolaj86.com/coolaj86/telebit.js.git
#https://git.coolaj86.com/coolaj86/telebit.js/archive/:tree:.tar.gz #https://git.coolaj86.com/coolaj86/telebit.js/archive/:tree:.tar.gz
@ -186,389 +165,227 @@ tmp_npm="$tmp_node $TELEBIT_TMP/bin/npm"
set +e set +e
my_unzip=$(type -p unzip) my_unzip=$(type -p unzip)
my_tar=$(type -p tar) my_tar=$(type -p tar)
# TODO extract to temporary directory, configure, copy etc, replace
if [ -n "$my_unzip" ]; then if [ -n "$my_unzip" ]; then
rm -f $my_tmp/$my_app-$TELEBIT_VERSION.zip rm -f $my_tmp/$my_app-$my_tree.zip
if [ -n "${TELEBIT_DEBUG}" ]; then http_get https://git.coolaj86.com/coolaj86/$my_repo/archive/$my_tree.zip $my_tmp/$my_app-$my_tree.zip
echo " - installing telebit zip to '$TELEBIT_REAL_PATH'"
fi
echo -n "."
#bash -c 'while true; do echo -n "."; sleep 2; done' 2>/dev/null &
#_my_pid=$!
http_get https://git.coolaj86.com/coolaj86/$my_repo/archive/$TELEBIT_VERSION.zip $my_tmp/$my_app-$TELEBIT_VERSION.zip
#kill $_my_pid >/dev/null 2>/dev/null
# -o means overwrite, and there is no option to strip # -o means overwrite, and there is no option to strip
$my_unzip -o $my_tmp/$my_app-$TELEBIT_VERSION.zip -d $my_tmp/ >/dev/null $my_unzip -o $my_tmp/$my_app-$my_tree.zip -d $TELEBIT_PATH/ > /dev/null 2>&1
$rsync_cmd $my_tmp/$my_repo/* $TELEBIT_TMP/ > /dev/null $rsync_cmd $TELEBIT_PATH/$my_repo/* $TELEBIT_PATH/ > /dev/null
rm -rf $my_tmp/$my_repo rm -rf $TELEBIT_PATH/$my_bin
elif [ -n "$my_tar" ]; then elif [ -n "$my_tar" ]; then
rm -f $my_tmp/$my_app-$TELEBIT_VERSION.tar.gz rm -f $my_tmp/$my_app-$my_tree.tar.gz
if [ -n "${TELEBIT_DEBUG}" ]; then http_get https://git.coolaj86.com/coolaj86/$my_repo/archive/$my_tree.tar.gz $my_tmp/$my_app-$my_tree.tar.gz
echo " - installing telebit tar.gz to '$TELEBIT_REAL_PATH'" ls -lah $my_tmp/$my_app-$my_tree.tar.gz
fi $my_tar -xzf $my_tmp/$my_app-$my_tree.tar.gz --strip 1 -C $TELEBIT_PATH/
echo -n "."
#bash -c 'while true; do echo -n "."; sleep 2; done' 2>/dev/null &
#_my_pid=$!
http_get https://git.coolaj86.com/coolaj86/$my_repo/archive/$TELEBIT_VERSION.tar.gz $my_tmp/$my_app-$TELEBIT_VERSION.tar.gz
#kill $_my_pid >/dev/null 2>/dev/null
$my_tar -xzf $my_tmp/$my_app-$TELEBIT_VERSION.tar.gz --strip 1 -C $TELEBIT_TMP/ >/dev/null
else else
echo "Neither tar nor unzip found. Abort." echo "Neither tar nor unzip found. Abort."
exit 13 exit 13
fi fi
set -e set -e
# pushd $TELEBIT_PATH >/dev/null
# TODO create slim packages that contain all the deps on each os and cpu $my_npm install >/dev/null 2>/dev/null
#
pushd $TELEBIT_TMP >/dev/null
if [ -n "${TELEBIT_DEBUG}" ]; then
echo " - installing telebit npm dependencies to '$TELEBIT_REAL_PATH'..."
else
echo -n "."
fi
set +e
$tmp_npm install >/dev/null 2>/dev/null &
tmp_npm_pid=$!
while [ -n "$tmp_npm_pid" ]; do
sleep 2
echo -n "."
kill -s 0 $tmp_npm_pid >/dev/null 2>/dev/null || tmp_npm_pid=""
done
set -e
echo -n "."
$tmp_npm install >/dev/null 2>/dev/null
# ursa is now an entirely optional dependency for key generation
# but very much needed on ARM devices
$tmp_npm install ursa >/dev/null 2>/dev/null || true
popd >/dev/null popd >/dev/null
if [ -n "${TELEBIT_DEBUG}" ]; then cat << EOF > $TELEBIT_PATH/bin/$my_app
echo " - configuring telebit..." #!/bin/bash
echo "" $my_node $TELEBIT_PATH/bin/$my_bin
fi EOF
chmod a+x $TELEBIT_PATH/bin/$my_app
###############################################
#
# TODO convert to node script
#
# Now that node is installed and the telebit
# packeage is downloaded, everything can be
# run from node, except things requiring sudo
#
###############################################
# telebit remote
echo '#!/bin/bash' > "$TELEBIT_TMP/bin/$my_app"
echo "$my_node $TELEBIT_REAL_PATH/bin/$my_bin "'"$@"' >> "$TELEBIT_TMP/bin/$my_app"
chmod a+x "$TELEBIT_TMP/bin/$my_app"
# telebit daemon
echo '#!/bin/bash' > "$TELEBIT_TMP/bin/$my_daemon"
echo "$my_node $TELEBIT_REAL_PATH/bin/$my_daemon.js daemon "'"$@"' >> "$TELEBIT_TMP/bin/$my_daemon"
chmod a+x "$TELEBIT_TMP/bin/$my_daemon"
# Create uninstall script based on the install script variables # Create uninstall script based on the install script variables
cat << EOF > $TELEBIT_TMP/bin/${my_app}_uninstall cat << EOF > $TELEBIT_PATH/bin/${my_app}_uninstall
#!/bin/bash #!/bin/bash
set -x
if [ "$(type -p launchctl)" ]; then if [ "$(type -p launchctl)" ]; then
sudo launchctl unload -w /Library/LaunchDaemons/${my_app_pkg_name}.plist sudo launchctl unload -w /Library/LaunchDaemons/${my_app_pkg_name}.plist
sudo rm -f /Library/LaunchDaemons/${my_app_pkg_name}.plist sudo rm -rf /Library/LaunchDaemons/cloud.telebit.remote.plist
launchctl unload -w $HOME/Library/LaunchAgents/${my_app_pkg_name}.plist
rm -f $HOME/Library/LaunchAgents/${my_app_pkg_name}.plist
fi fi
if [ "$(type -p systemctl)" ]; then if [ "$(type -p systemctl)" ]; then
systemctl --user disable $my_app >/dev/null sudo systemctl disable telebit; sudo systemctl stop telebit
systemctl --user stop $my_app sudo rm -rf /etc/systemd/system/$my_app.service
rm -f $HOME/.config/systemd/user/$my_app.service
sudo systemctl disable $my_app >/dev/null
sudo systemctl stop $my_app
sudo rm -f /etc/systemd/system/$my_app.service
fi fi
sudo rm -rf $TELEBIT_REAL_PATH /usr/local/bin/$my_app sudo rm -rf $TELEBIT_PATH /usr/local/bin/$my_app
sudo rm -rf $TELEBIT_REAL_PATH /usr/local/bin/$my_daemon rm -rf ~/.config/$my_app ~/.local/share/$my_app
rm -rf $HOME/.config/$my_app $HOME/.local/share/$my_app
EOF EOF
chmod a+x $TELEBIT_TMP/bin/${my_app}_uninstall chmod a+x $TELEBIT_PATH/bin/${my_app}_uninstall
#set +e echo "${sudo_cmde}ln -sf $TELEBIT_PATH/bin/$my_app /usr/local/bin/$my_app"
#if type -p setcap >/dev/null 2>&1; then $sudo_cmd ln -sf $TELEBIT_PATH/bin/$my_app /usr/local/bin/$my_app
# #echo "Setting permissions to allow $my_app to run on port 80 and port 443 without sudo or root"
# echo " > ${real_sudo_cmde}setcap cap_net_bind_service=+ep $TELEBIT_REAL_PATH/bin/node"
# $real_sudo_cmd setcap cap_net_bind_service=+ep $TELEBIT_REAL_PATH/bin/node
#fi
#set -e
my_skip=""
set +e set +e
# TODO for macOS https://apple.stackexchange.com/questions/286749/how-to-add-a-user-from-the-command-line-in-macos if type -p setcap >/dev/null 2>&1; then
# TODO do stuff for groups too #echo "Setting permissions to allow $my_app to run on port 80 and port 443 without sudo or root"
# TODO add ending $ echo "${sudo_cmde}setcap cap_net_bind_service=+ep $TELEBIT_PATH/bin/node"
if type -p dscl >/dev/null 2>/dev/null; then $sudo_cmd setcap cap_net_bind_service=+ep $TELEBIT_PATH/bin/node
if [ -n "$(dscl . list /users | grep ^$TELEBIT_USER)" ] && [ -n "$(dscl . list /groups | grep ^$TELEBIT_GROUP)" ]; then
my_skip="yes"
fi
elif [ -n "$(cat $my_root/etc/passwd | grep $TELEBIT_USER)" ] && [ -n "$(cat $my_root/etc/group | grep $TELEBIT_GROUP)" ]; then
my_skip="yes"
fi
if [ -z "$my_skip" ]; then
if type -p adduser >/dev/null 2>/dev/null; then
$real_sudo_cmd adduser --home $TELEBIT_REAL_PATH --gecos '' --disabled-password $TELEBIT_USER >/dev/null 2>&1
#TELEBIT_USER=$my_app_name
TELEBIT_GROUP=$TELEBIT_USER
elif [ -n "$(cat /etc/passwd | grep www-data:)" ]; then
# Linux (Ubuntu)
TELEBIT_USER=www-data
TELEBIT_GROUP=www-data
elif [ -n "$(cat /etc/passwd | grep _www:)" ]; then
# Mac
TELEBIT_USER=_www
TELEBIT_GROUP=_www
else
# Unsure
TELEBIT_USER=$(id -u -n) # $(whoami)
TELEBIT_GROUP=$(id -g -n)
fi
fi fi
set -e set -e
export TELEBIT_USER set +e
export TELEBIT_GROUP # TODO for macOS https://apple.stackexchange.com/questions/286749/how-to-add-a-user-from-the-command-line-in-macos
export TELEBIT_PATH if type -p adduser >/dev/null 2>/dev/null; then
export TELEBIT_CONFIG=$HOME/.config/$my_app/$my_app.yml if [ -z "$(cat $my_root/etc/passwd | grep $my_user)" ]; then
# TODO check both expected sock paths in client by default $sudo_cmd adduser --home $TELEBIT_PATH --gecos '' --disabled-password $my_user >/dev/null 2>&1
if [ "yes" == "$TELEBIT_USERSPACE" ]; then fi
TELEBIT_TMP_CONFIGD=$HOME/.config/$my_app/$my_daemon.yml #my_user=$my_app_name
TELEBITD_CONFIG=$HOME/.config/$my_app/$my_daemon.yml my_group=$my_user
TELEBIT_LOG_DIR=${TELEBIT_LOG_DIR:-$HOME/.local/share/$my_app/var/log/} elif [ -n "$(cat /etc/passwd | grep www-data:)" ]; then
TELEBIT_SOCK_DIR=${TELEBIT_SOCK_DIR:-$HOME/.local/share/$my_app/var/run/} # Linux (Ubuntu)
TELEBIT_SOCK=${TELEBIT_SOCK:-$HOME/.local/share/$my_app/var/run/$my_app.sock} my_user=www-data
my_group=www-data
elif [ -n "$(cat /etc/passwd | grep _www:)" ]; then
# Mac
my_user=_www
my_group=_www
else else
TELEBIT_TMP_CONFIGD=$TELEBIT_TMP/etc/$my_daemon.yml # Unsure
TELEBITD_CONFIG=$TELEBIT_REAL_PATH/etc/$my_daemon.yml my_user=$(id -u -n) # $(whoami)
TELEBIT_LOG_DIR=${TELEBIT_LOG_DIR:-$TELEBIT_REAL_PATH/var/log/} my_group=$(id -g -n)
TELEBIT_SOCK_DIR=${TELEBIT_SOCK_DIR:-$TELEBIT_REAL_PATH/var/run/}
TELEBIT_SOCK=${TELEBIT_SOCK:-$TELEBIT_REAL_PATH/var/run/$my_app.sock}
fi fi
export TELEBITD_CONFIG set -e
export TELEBIT_SOCK
export TELEBIT_NODE=$TELEBIT_REAL_PATH/bin/node
export TELEBIT_NPM=$TELEBIT_REAL_PATH/bin/npm
export TELEBIT_BIN=$TELEBIT_REAL_PATH/bin/telebit
export TELEBITD_BIN=$TELEBIT_REAL_PATH/bin/telebitd
export TELEBIT_JS=$TELEBIT_REAL_PATH/bin/telebit.js
export TELEBITD_JS=$TELEBIT_REAL_PATH/bin/telebitd.js
export TELEBIT_LOG_DIR
export TELEBIT_SOCK_DIR
export NODE_PATH="$TELEBIT_REAL_PATH/lib/node_modules"
export NPM_CONFIG_PREFIX="$TELEBIT_REAL_PATH"
$my_node $TELEBIT_TMP/usr/share/template-launcher.js
# TODO don't create this in TMP_PATH if it exists in TELEBIT_REAL_PATH
mkdir -p "$(dirname $TELEBIT_TMP_CONFIGD)"
if [ ! -e "$TELEBITD_CONFIG" ]; then
echo "sock: $TELEBIT_SOCK" >> "$TELEBIT_TMP_CONFIGD"
echo "root: $TELEBIT_REAL_PATH" >> "$TELEBIT_TMP_CONFIGD"
cat $TELEBIT_REAL_PATH/usr/share/$my_daemon.tpl.yml >> "$TELEBIT_TMP_CONFIGD"
# TODO don't create this in TMP_PATH if it exists in TELEBIT_PATH
my_config="$TELEBIT_PATH/etc/$my_app.yml"
mkdir -p "$(dirname $my_config)"
if [ ! -e "$my_config" ]; then
#$rsync_cmd examples/$my_app.yml "$my_config"
if [ -n "$my_email" ]; then
echo "email: $my_email" >> "$my_config"
echo "agree_tos: true" >> "$my_config"
fi
if [ -n "$my_relay" ]; then
echo "relay: $my_relay" >> "$my_config"
fi
if [ -n "$my_secret" ]; then
echo "secret: $my_secret" >> "$my_config"
fi
if [ -n "$my_servernames" ]; then
# TODO could use printf or echo -e,
# just not sure how portable they are
echo "servernames:" >> "$my_config"
echo " $my_servernames: {}" >> "$my_config"
fi
#echo "dynamic_ports:\n []" >> "$my_config"
cat $TELEBIT_PATH/usr/share/$my_app.tpl.yml >> "$my_config"
fi fi
mkdir -p "$(dirname $TELEBIT_CONFIG)" #my_config_link="/etc/$my_app/$my_app.yml"
if [ ! -e "$TELEBIT_CONFIG" ]; then #if [ ! -e "$my_config_link" ]; then
# echo "${sudo_cmde}ln -sf '$my_config' '$my_config_link'"
echo "sock: $TELEBIT_SOCK" >> "$TELEBIT_CONFIG" # #$sudo_cmd mkdir -p /etc/$my_app
# $sudo_cmd ln -sf "$my_config" "$my_config_link"
#fi
my_config="$HOME/.config/$my_app/$my_app.yml"
mkdir -p "$(dirname $my_config)"
if [ ! -e "$my_config" ]; then
echo "cli: true" >> "$my_config"
if [ -n "$my_email" ]; then
echo "email: $my_email" >> "$my_config"
echo "agree_tos: true" >> "$my_config"
fi
if [ -n "$my_relay" ]; then
echo "relay: $my_relay" >> "$my_config"
fi
if [ -n "$my_secret" ]; then
echo "secret: $my_secret" >> "$my_config"
fi
cat $TELEBIT_PATH/usr/share/$my_app.tpl.yml >> "$my_config"
fi fi
echo "${sudo_cmde}chown -R $my_user '$TELEBIT_PATH' # '/etc/$my_app'"
$sudo_cmd chown -R $my_user "$TELEBIT_PATH" # "/etc/$my_app"
# ~/.config/systemd/user/
# TODO
# Backup final directory, if it exists
# Move everything over to final directory
# Restore config files, if they exist
# rewrite system service file with real variables
# This should only affect non-USERSPACE installs
#echo "${soft_sudo_cmde}chown -R $TELEBIT_USER '$TELEBIT_REAL_PATH'
$soft_sudo_cmd mkdir -p $TELEBIT_LOG_DIR
$soft_sudo_cmd mkdir -p $TELEBIT_SOCK_DIR
$soft_sudo_cmd chown -R $TELEBIT_USER "$TELEBIT_REAL_PATH"
# $HOME/.config/systemd/user/
# %h/.config/telebit/telebit.yml # %h/.config/telebit/telebit.yml
if [ -n "${TELEBIT_DEBUG}" ]; then echo "### Adding $my_app is a system service"
echo " - adding $my_app as a system service"
fi
# TODO detect with type -p # TODO detect with type -p
my_system_launcher="" my_system_launcher=""
my_app_launchd_service=""
if [ -d "/Library/LaunchDaemons" ]; then if [ -d "/Library/LaunchDaemons" ]; then
my_system_launcher="launchd" my_system_launcher="launchd"
my_sudo_cmde="$real_sudo_cmde" my_app_launchd_service="Library/LaunchDaemons/${my_app_pkg_name}.plist"
my_sudo_cmd="$real_sudo_cmd" echo "${sudo_cmde}$rsync_cmd $TELEBIT_PATH/usr/share/dist/$my_app_launchd_service /$my_app_launchd_service"
$sudo_cmd $rsync_cmd "$TELEBIT_PATH/usr/share/dist/$my_app_launchd_service" "/$my_app_launchd_service"
echo "${sudo_cmde}chown root:wheel $my_root/$my_app_launchd_service"
if [ "yes" == "$TELEBIT_USERSPACE" ]; then $sudo_cmd chown root:wheel "$my_root/$my_app_launchd_service"
my_app_launchd_service_skel="etc/skel/Library/LaunchAgents/${my_app_pkg_name}.plist" echo "${sudo_cmde}launchctl unload -w $my_root/$my_app_launchd_service >/dev/null 2>/dev/null"
my_app_launchd_service="$HOME/Library/LaunchAgents/${my_app_pkg_name}.plist" $sudo_cmd launchctl unload -w "$my_root/$my_app_launchd_service" >/dev/null 2>/dev/null
if [ -n "${TELEBIT_DEBUG}" ]; then echo "${sudo_cmde}launchctl load -w $my_root/$my_app_launchd_service"
echo " > $rsync_cmd $TELEBIT_REAL_PATH/usr/share/dist/$my_app_launchd_service $my_app_launchd_service" $sudo_cmd launchctl load -w "$my_root/$my_app_launchd_service"
fi
mkdir -p $HOME/Library/LaunchAgents
$rsync_cmd "$TELEBIT_REAL_PATH/usr/share/dist/$my_app_launchd_service_skel" "$my_app_launchd_service"
if [ -n "${TELEBIT_DEBUG}" ]; then
echo " > chown $(id -u -n):$(id -g -n) $my_app_launchd_service"
fi
chown $(id -u -n):$(id -g -n) "$my_app_launchd_service"
my_sudo_cmd=""
my_sudo_cmde=""
if [ -n "${TELEBIT_DEBUG}" ]; then
echo " > launchctl unload -w $my_app_launchd_service >/dev/null 2>/dev/null"
fi
launchctl unload -w "$my_app_launchd_service" >/dev/null 2>/dev/null
else
my_app_launchd_service_skel="usr/share/dist/Library/LaunchDaemons/${my_app_pkg_name}.plist"
my_app_launchd_service="$my_root/Library/LaunchDaemons/${my_app_pkg_name}.plist"
echo " > ${real_sudo_cmde}$rsync_cmd $TELEBIT_REAL_PATH/usr/share/dist/$my_app_launchd_service $my_app_launchd_service"
$real_sudo_cmd $rsync_cmd "$TELEBIT_REAL_PATH/usr/share/dist/$my_app_launchd_service_skel" "$my_app_launchd_service"
echo " > ${real_sudo_cmde}chown root:wheel $my_app_launchd_service"
$real_sudo_cmd chown root:wheel "$my_app_launchd_service"
echo " > ${real_sudo_cmde}launchctl unload -w $my_app_launchd_service >/dev/null 2>/dev/null"
$real_sudo_cmd launchctl unload -w "$my_app_launchd_service" >/dev/null 2>/dev/null
fi
elif [ -d "$my_root/etc/systemd/system" ]; then elif [ -d "$my_root/etc/systemd/system" ]; then
my_system_launcher="systemd" my_system_launcher="systemd"
echo "${sudo_cmde}$rsync_cmd $TELEBIT_PATH/usr/share/dist/etc/systemd/system/$my_app.service /etc/systemd/system/$my_app.service"
$sudo_cmd $rsync_cmd "$TELEBIT_PATH/usr/share/dist/etc/systemd/system/$my_app.service" "/etc/systemd/system/$my_app.service"
if [ "yes" == "$TELEBIT_USERSPACE" ]; then $sudo_cmd systemctl daemon-reload
if [ -n "${TELEBIT_DEBUG}" ]; then echo "${sudo_cmde}systemctl enable $my_app"
echo " > $rsync_cmd $TELEBIT_REAL_PATH/usr/share/dist/etc/skel/.config/systemd/user/$my_app.service $HOME/.config/systemd/user/$my_app.service" $sudo_cmd systemctl enable $my_app
fi echo "${sudo_cmde}systemctl start $my_app"
mkdir -p $HOME/.config/systemd/user $sudo_cmd systemctl restart $my_app
$rsync_cmd "$TELEBIT_REAL_PATH/usr/share/dist/etc/skel/.config/systemd/user/$my_app.service" "$HOME/.config/systemd/user/$my_app.service"
else
echo " > ${real_sudo_cmde}$rsync_cmd $TELEBIT_REAL_PATH/usr/share/dist/etc/systemd/system/$my_app.service /etc/systemd/system/$my_app.service"
$real_sudo_cmd $rsync_cmd "$TELEBIT_REAL_PATH/usr/share/dist/etc/systemd/system/$my_app.service" "/etc/systemd/system/$my_app.service"
fi
fi fi
sleep 1 sleep 1
echo ""
############################### echo ""
# Actually Launch the Service # echo ""
############################### echo "=============================================="
if [ -n "${TELEBIT_DEBUG}" ]; then echo " Privacy Settings in Config"
echo "" echo "=============================================="
fi echo ""
if [ "launchd" == "$my_system_launcher" ]; then echo "The default config file $TELEBIT_PATH/etc/$my_app.yml opts-in to"
echo "contributing telemetrics and receiving infrequent relevant updates"
if [ "yes" == "$TELEBIT_USERSPACE" ]; then echo "(probably once per quarter or less) such as important notes on"
if [ -n "${TELEBIT_DEBUG}" ]; then echo "a new release, an important API change, etc. No spam."
echo " > launchctl load -w $my_app_launchd_service" echo ""
else echo "Please edit the config file to meet your needs before starting."
echo -n "." echo ""
fi sleep 2
launchctl load -w "$my_app_launchd_service"
else
echo " > ${real_sudo_cmde}launchctl load -w $my_app_launchd_service"
$real_sudo_cmd launchctl load -w "$my_app_launchd_service"
fi
sleep 2; # give it time to start
elif [ "systemd" == "$my_system_launcher" ]; then
if [ "yes" == "$TELEBIT_USERSPACE" ]; then
# https://wiki.archlinux.org/index.php/Systemd/User
# sudo loginctl enable-linger username
if [ -n "${TELEBIT_DEBUG}" ]; then
echo " > systemctl --user enable $my_app"
else
echo -n "."
fi
set +e
if systemctl --user daemon-reload; then
# enable also puts success output to stderr... why?
systemctl --user enable $my_app >/dev/null 2>/dev/null
#echo " > systemctl --user enable systemd-tmpfiles-setup.service systemd-tmpfiles-clean.timer"
#systemctl --user enable systemd-tmpfiles-setup.service systemd-tmpfiles-clean.timer
if [ -n "${TELEBIT_DEBUG}" ]; then
echo " > systemctl --user start $my_app"
fi
systemctl --user stop $my_app >/dev/null 2>/dev/null
systemctl --user start $my_app >/dev/null
sleep 2; # give it time to start
_is_running=$(systemctl --user status --no-pager $my_app 2>/dev/null | grep "active.*running")
if [ -z "$_is_running" ]; then
echo "Something went wrong:"
systemctl --user status --no-pager $my_app
fi
else
echo "libpam-systemd is missing, which is required on Linux to register Telebit with the user launcher."
echo "sudo apt-get install -y libpam-systemd"
sudo apt-get install -y libpam-systemd
fi
set -e
echo -n "."
else
$real_sudo_cmd systemctl daemon-reload
echo " > ${real_sudo_cmde}systemctl enable $my_app"
$real_sudo_cmd systemctl enable $my_app >/dev/null
echo " > ${real_sudo_cmde}systemctl start $my_app"
$real_sudo_cmd systemctl daemon-reload
$real_sudo_cmd systemctl restart $my_app
sleep 2; # give it time to start
$real_sudo_cmd systemctl status --no-pager $my_app
fi
else
echo "Run the service manually (we couldn't detect your system service to do that automatically):"
echo ""
echo " $TELEBITD_BIN --config $TELEBITD_CONFIG"
echo " ~/$my_app --config $TELEBIT_CONFIG"
fi
# NOTE: ln -sf *should* replace an existing link... but sometimes it doesn't, hence rm -f
if [ "yes" == "$TELEBIT_USERSPACE" ]; then
if [ -n "${TELEBIT_DEBUG}" ]; then
echo " > ${real_sudo_cmde}ln -sf $TELEBIT_REAL_PATH/bin/$my_app /usr/local/bin/$my_app"
fi
rm -f /usr/local/bin/$my_app 2>/dev/null || true
ln -sf $TELEBIT_REAL_PATH/bin/$my_app /usr/local/bin/$my_app 2>/dev/null || true
else
echo " > ${real_sudo_cmde}ln -sf $TELEBIT_REAL_PATH/bin/$my_app /usr/local/bin/$my_app"
rm -f /usr/local/bin/$my_app 2>/dev/null || \
$real_sudo_cmd rm -f /usr/local/bin/$my_app
ln -sf $TELEBIT_REAL_PATH/bin/$my_app /usr/local/bin/$my_app 2>/dev/null || \
$real_sudo_cmd ln -sf $TELEBIT_REAL_PATH/bin/$my_app /usr/local/bin/$my_app
# telebitd
echo " > ${real_sudo_cmde}ln -sf $TELEBIT_REAL_PATH/bin/$my_daemon /usr/local/bin/$my_daemon"
rm -f $TELEBIT_REAL_PATH/bin/$my_daemon || $real_sudo_cmd rm -f $TELEBIT_REAL_PATH/bin/$my_daemon
ln -sf $TELEBIT_REAL_PATH/bin/$my_daemon /usr/local/bin/$my_daemon || \
$real_sudo_cmd ln -sf $TELEBIT_REAL_PATH/bin/$my_daemon /usr/local/bin/$my_daemon
fi
rm -f $HOME/$my_app; ln -s $TELEBIT_REAL_PATH/bin/$my_app $HOME/
if [ -n "${TELEBIT_DEBUG}" ]; then
echo " > telebit init --tty"
echo ""
fi
sleep 0.25
echo "" echo ""
$TELEBIT_REAL_PATH/bin/node $TELEBIT_REAL_PATH/bin/telebit.js init --tty echo ""
echo "=============================================="
echo "Installed successfully. Last steps:"
echo "=============================================="
echo ""
if [ "systemd" == "$my_system_launcher" ]; then
echo "Edit the config and restart, if desired:"
echo ""
echo " ${sudo_cmde}$my_edit $TELEBIT_PATH/etc/$my_app.yml"
echo " ${sudo_cmde}systemctl restart $my_app"
echo ""
echo "Or disabled the service and start manually:"
echo ""
echo " ${sudo_cmde}systemctl stop $my_app"
echo " ${sudo_cmde}systemctl disable $my_app"
echo " $my_app --config $TELEBIT_PATH/etc/$my_app.yml"
elif [ "launchd" == "$my_system_launcher" ]; then
echo "Edit the config and restart, if desired:"
echo ""
echo " ${sudo_cmde}$my_edit $TELEBIT_PATH/etc/$my_app.yml"
echo " ${sudo_cmde}launchctl unload $my_root/$my_app_launchd_service"
echo " ${sudo_cmde}launchctl load -w $my_root/$my_app_launchd_service"
echo ""
echo "Or disabled the service and start manually:"
echo ""
echo " ${sudo_cmde}launchctl unload -w $my_root/$my_app_launchd_service"
echo " $my_app --config $TELEBIT_PATH/etc/$my_app.yml"
else
echo "Edit the config, if desired:"
echo ""
echo " ${sudo_cmde}$my_edit $my_config"
echo ""
echo "Or disabled the service and start manually:"
echo ""
echo " $my_app --config $my_config"
fi
echo ""
sleep 1

View File

@ -1,46 +0,0 @@
echo ""
echo ""
echo "=============================================="
echo " Launcher Configuration "
echo "=============================================="
echo ""
my_stopper=""
if [ "systemd" == "$my_system_launcher" ]; then
my_stopper="${real_sudo_cmde}systemctl stop $my_app"
echo "Edit the config and restart, if desired:"
echo ""
echo " ${real_sudo_cmde}$my_edit $TELEBITD_CONFIG"
echo " ${real_sudo_cmde}systemctl restart $my_app"
echo ""
echo "Or disabled the service and start manually:"
echo ""
echo " ${real_sudo_cmde}systemctl stop $my_app"
echo " ${real_sudo_cmde}systemctl disable $my_app"
echo " $my_daemon --config $TELEBITD_CONFIG"
elif [ "launchd" == "$my_system_launcher" ]; then
my_stopper="${real_sudo_cmde}launchctl unload $my_app_launchd_service"
echo "Edit the config and restart, if desired:"
echo ""
echo " ${real_sudo_cmde}$my_edit $TELEBITD_CONFIG"
echo " ${real_sudo_cmde}launchctl unload $my_app_launchd_service"
echo " ${real_sudo_cmde}launchctl load -w $my_app_launchd_service"
echo ""
echo "Or disabled the service and start manually:"
echo ""
echo " ${real_sudo_cmde}launchctl unload -w $my_app_launchd_service"
echo " $my_daemon --config $TELEBITD_CONFIG"
else
my_stopper="not started"
echo ""
echo "Run the service manually (we couldn't detect your system service to do that automatically):"
echo ""
echo " $my_daemon --config $TELEBITD_CONFIG"
echo " $my_app --config $TELEBIT_CONFIG"
fi

View File

@ -1,5 +1,4 @@
#agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes #agree_tos: true # agree to the Telebit, Greenlock, and Let's Encrypt TOSes
community_member: true # receive infrequent relevant updates community_member: true # receive infrequent relevant updates
telemetry: true # contribute to project telemetric data telemetry: true # contribute to project telemetric data
newsletter: false # contribute to project telemetric data ssh_auto: 22 # forward ssh-looking packets, from any connection, to port 22
ssh_auto: false # forward ssh-looking packets, from any connection, to port 22

View File

@ -1,97 +0,0 @@
'use strict';
var path = require('path');
var fs = require('fs');
var os = require('os');
module.exports = function (opts, fn) {
// TODO make async version
try {
module.exports.sync(opts);
} catch(e) {
if (fn) { fn(e); }
}
if (fn) { fn(null); }
};
module.exports.sync = function (opts) {
var f = opts.file;
var vars = opts.vars;
var text = fs.readFileSync(f.tpl, 'utf8')
.replace(/{TELEBIT_PATH}/g, vars.telebitPath || '{TELEBIT_PATH}')
.replace(/{TELEBIT_NODE}/g, vars.telebitNode || '{TELEBIT_NODE}')
.replace(/{NODE_PATH}/g, vars.nodePath || '{NODE_PATH}')
.replace(/{NPM_CONFIG_PREFIX}/g, vars.npmConfigPrefix || '{NPM_CONFIG_PREFIX}')
.replace(/{TELEBIT_NPM}/g, vars.telebitNpm || '{TELEBIT_NPM}')
.replace(/{TELEBIT_BIN}/g, vars.telebitBin || '{TELEBIT_BIN}')
.replace(/{TELEBITD_BIN}/g, vars.telebitdBin || '{TELEBITD_BIN}')
.replace(/{TELEBIT_JS}/g, vars.telebitJs || '{TELEBIT_JS}')
.replace(/{TELEBITD_JS}/g, vars.telebitdJs || '{TELEBITD_JS}')
.replace(/{TELEBIT_USER}/g, vars.telebitUser || '{TELEBIT_USER}')
.replace(/{TELEBIT_GROUP}/g, vars.telebitGroup || '{TELEBIT_GROUP}')
.replace(/{TELEBIT_RW_DIRS}/g, vars.telebitRwDirs || '{TELEBIT_RW_DIRS}')
.replace(/{TELEBIT_CONFIG}/g, vars.telebitConfig || '{TELEBIT_CONFIG}')
.replace(/{TELEBITD_CONFIG}/g, vars.telebitdConfig || '{TELEBITD_CONFIG}')
.replace(/{TELEBIT_LOG_DIR}/g, vars.TELEBIT_LOG_DIR || '{TELEBIT_LOG_DIR}')
.replace(/{TELEBIT_SOCK_DIR}/g, vars.TELEBIT_LOG_DIR || '{TELEBIT_SOCK_DIR}')
;
fs.writeFileSync(f.launcher, text, 'utf8');
if (f.executable && !/^win/i.test(os.platform())) {
// TODO not sure if chmod works on windows
fs.chmodSync(f.launcher, parseInt('755', 8));
}
};
function run() {
var files = [
{ tpl: (process.env.TELEBIT_SERVICE_TPL || path.join(__dirname, 'dist/etc/systemd/system/telebit.service.tpl'))
, launcher: (process.env.TELEBIT_SERVICE || path.join(__dirname, 'dist/etc/systemd/system/telebit.service'))
}
, { tpl: (process.env.TELEBIT_USER_SERVICE_TPL || path.join(__dirname, 'dist/etc/skel/.config/systemd/user/telebit.service.tpl'))
, launcher: (process.env.TELEBIT_USER_SERVICE || path.join(__dirname, 'dist/etc/skel/.config/systemd/user/telebit.service'))
}
, { tpl: (process.env.TELEBIT_PLIST_TPL || path.join(__dirname, 'dist/Library/LaunchDaemons/cloud.telebit.remote.plist.tpl'))
, launcher: (process.env.TELEBIT_PLIST || path.join(__dirname, 'dist/Library/LaunchDaemons/cloud.telebit.remote.plist'))
}
, { tpl: (process.env.TELEBIT_USER_PLIST_TPL || path.join(__dirname, 'dist/etc/skel/Library/LaunchAgents/cloud.telebit.remote.plist.tpl'))
, launcher: (process.env.TELEBIT_USER_PLIST || path.join(__dirname, 'dist/etc/skel/Library/LaunchAgents/cloud.telebit.remote.plist'))
}
];
files.forEach(function (f) {
var telebitRoot = path.resolve(__dirname, '../..');
var vars = {
telebitPath: process.env.TELEBIT_PATH || telebitRoot
, telebitNode: process.env.TELEBIT_NODE || process.argv[0] || path.resolve(telebitRoot, 'bin/node')
, telebitBin: process.env.TELEBIT_BIN || path.resolve(telebitRoot, 'bin/telebit')
, telebitdBin: process.env.TELEBITD_BIN || path.resolve(telebitRoot, 'bin/telebitd')
, telebitJs: process.env.TELEBIT_JS || path.resolve(telebitRoot, 'bin/telebit.js')
, telebitdJs: process.env.TELEBITD_JS || path.resolve(telebitRoot, 'bin/telebitd.js')
, telebitRwDirs: [
(process.env.TELEBIT_PATH || path.resolve(__dirname, '../..'))
, path.join(os.homedir(), '.config/telebit')
, path.join(os.homedir(), '.local/share/telebit')
]
, telebitUser: process.env.TELEBIT_USER || os.userInfo().username
, telebitGroup: process.env.TELEBIT_GROUP || ('darwin' === os.platform() ? 'staff' : os.userInfo().username)
, telebitConfig: process.env.TELEBIT_CONFIG || path.join(os.homedir(), '.config/telebit/telebit.yml')
, telebitdConfig: process.env.TELEBITD_CONFIG || path.join(os.homedir(), '.config/telebit/telebitd.yml')
, TELEBIT_LOG_DIR: process.env.TELEBIT_LOG_DIR || path.join(os.homedir(), '.local/share/telebit/var/log')
};
vars.telebitNpm = process.env.TELEBIT_NPM || path.resolve(vars.telebitNode, '../npm');
vars.nodePath = process.env.NODE_PATH || path.resolve(vars.telebitNode, '../../lib/node_modules');
vars.npmConfigPrefix = process.env.NPM_CONFIG_PREFIX || path.resolve(vars.telebitNode, '..', '..');
if (-1 === vars.telebitRwDirs.indexOf(vars.npmConfigPrefix)) {
vars.telebitRwDirs.push(vars.npmConfigPrefix);
}
vars.telebitRwDirs = vars.telebitRwDirs.join(' ');
module.exports({
file: f
, vars: vars
});
});
}
if (module === require.main) {
run();
}

View File

View File