goldilocks.js/README.md

620 lines
17 KiB
Markdown
Raw Normal View History

2017-01-27 06:07:51 +00:00
Goldilocks
==========
2017-07-26 00:37:29 +00:00
The node.js netserver that's just right.
* **HTTPS Web Server** with Automatic TLS (SSL) via ACME ([Let's Encrypt](https://letsencrypt.org))
* Static Web Server
* URL Redirects
* SSL on localhost (with bundled localhost.daplie.me certificates)
2017-07-28 19:03:06 +00:00
* Uses node cluster to take advantage of multiple CPUs (in progress)
2017-07-26 00:37:29 +00:00
* **TLS** name-based (SNI) proxy
* **TCP** port-based proxy
* WS **Tunnel Server** (i.e. run on Digital Ocean and expose a home-firewalled Raspberry Pi to the Internet)
* WS **Tunnel Client** (i.e. run on a Raspberry Pi and connect to a Daplie Tunnel)
2017-07-28 19:03:29 +00:00
* UPnP / NAT-PMP forwarding and loopback testing (in progress)
2017-07-26 00:37:29 +00:00
* Configurable via API
* mDNS Discoverable (configure in home or office with mobile and desktop apps)
* OAuth3 Authentication
Install Standalone
2015-06-24 21:44:42 +00:00
-------
```bash
2017-07-26 00:37:29 +00:00
# v1 in npm
2017-02-22 22:00:14 +00:00
npm install -g goldilocks
2017-01-08 00:21:12 +00:00
# v1 in git (via ssh)
2017-05-05 07:33:36 +00:00
npm install -g git+ssh://git@git.daplie.com:Daplie/goldilocks.js#v1
2017-01-08 00:21:12 +00:00
# v1 in git (unauthenticated)
2017-05-05 07:33:36 +00:00
npm install -g git+https://git@git.daplie.com:Daplie/goldilocks.js#v1
2015-06-24 21:44:42 +00:00
```
2017-01-07 23:43:45 +00:00
```bash
2017-02-22 22:00:14 +00:00
goldilocks
2015-06-24 21:44:42 +00:00
```
2017-01-07 23:43:45 +00:00
```bash
Serving /Users/foo/ at https://localhost.daplie.me:8443
2015-06-24 21:44:42 +00:00
```
2017-07-26 00:37:29 +00:00
Install as a System Service (daemon-mode)
We have service support for
2017-05-05 07:33:36 +00:00
2017-07-26 00:37:29 +00:00
* systemd (Linux, Ubuntu)
* launchd (macOS)
2017-05-05 07:33:36 +00:00
```bash
curl https://git.daplie.com/Daplie/goldilocks.js/raw/master/install.sh | bash
```
2017-07-26 00:37:29 +00:00
Modules & Configuration
2015-06-24 21:44:42 +00:00
-----
Goldilocks has several core systems, which all have their own configuration and
some of which have modules:
* [http](#http)
- [proxy (reverse proxy)](#httpproxy-how-to-reverse-proxy-ruby-python-etc)
- [static](#httpstatic-how-to-serve-a-web-page)
- [redirect](#httpredirect-how-to-redirect-urls)
* [tls](#tls)
- [proxy (reverse proxy)](#tlsproxy)
- [acme](#tlsacme)
* [tcp](#tcp)
- [proxy](#tcpproxy)
- [forward](#tcpforward)
* [udp](#udp)
- [forward](#udpforward)
* [domains](#domains)
* [tunnel_server](#tunnel_server)
* [DDNS](#ddns)
* [tunnel_client](#tunnel)
* [mDNS](#mdns)
2017-10-13 18:39:31 +00:00
* [socks5](#socks5)
2017-07-26 00:37:29 +00:00
* api
All modules require a `type` and an `id`, and any modules not defined inside the
`domains` system also require a `domains` field (with the exception of the `forward`
modules that require the `ports` field).
2017-07-26 00:37:29 +00:00
### http
The HTTP system handles plain http (TLS / SSL is handled by the tls system)
Example config:
```yml
2017-07-26 00:37:29 +00:00
http:
trust_proxy: true # allow localhost, 192.x, 10.x, 172.x, etc to set headers
allow_insecure: false # allow non-https even without proxy https headers
primary_domain: example.com # attempts to access via IP address will redirect here
# An array of modules that define how to handle incoming HTTP requests
2017-07-26 00:37:29 +00:00
modules:
- type: static
2017-07-26 00:37:29 +00:00
domains:
- example.com
root: /srv/www/:hostname
```
### http.proxy - how to reverse proxy (ruby, python, etc)
2017-07-26 00:37:29 +00:00
The proxy module is for reverse proxying, typically to an application on the same machine.
(Though it can also reverse proxy to other devices on the local network.)
2017-07-26 00:37:29 +00:00
It has the following options:
2017-07-26 00:37:29 +00:00
```
address The DNS-resolvable hostname (or IP address) and port connected by `:` to proxy the request to.
Takes priority over host and port if they are also specified.
ex: locahost:3000
ex: 192.168.1.100:80
host The DNS-resolvable hostname (or IP address) of the system to which the request will be proxied.
Defaults to localhost if only the port is specified.
ex: localhost
ex: 192.168.1.100
port The port on said system to which the request will be proxied
ex: 3000
ex: 80
2017-07-26 00:37:29 +00:00
```
Example config:
```yml
2017-07-26 00:37:29 +00:00
http:
modules:
- type: proxy
2017-07-26 00:37:29 +00:00
domains:
- api.example.com
host: 192.168.1.100
port: 80
- type: proxy
domains:
- www.example.com
address: 192.168.1.16:80
- type: proxy
domains:
- '*'
port: 3000
2017-07-26 00:37:29 +00:00
```
### http.static - how to serve a web page
The static module is for serving static web pages and assets and has the following options:
```
root The path to serve as a string.
The template variable `:hostname` represents the HTTP Host header without port information
ex: `root: /srv/www/example.com` would load the example.com folder for any domain listed
ex: `root: /srv/www/:hostname` would load `/srv/www/example.com` if so indicated by the Host header
2017-10-24 22:04:44 +00:00
index Set to `false` to disable the default behavior of loading `index.html` in directories
ex: `false`
dotfiles Set to `allow` to load dotfiles rather than ignoring them
ex: `"allow"`
redirect Set to `false` to disable the default behavior of ensuring that directory paths end in '/'
ex: `false`
indexes An array of directories which should be have indexes served rather than blocked
ex: `[ '/' ]` will allow all directories indexes to be served
2017-07-26 00:37:29 +00:00
```
Example config:
```yml
2017-07-26 00:37:29 +00:00
http:
modules:
- type: static
2017-07-26 00:37:29 +00:00
domains:
- example.com
root: /srv/www/:hostname
```
### http.redirect - how to redirect URLs
2017-07-26 00:37:29 +00:00
The redirect module is for, you guessed it, redirecting URLs.
2017-07-26 00:37:29 +00:00
It has the following options:
2015-07-08 07:46:01 +00:00
```
2017-07-26 00:37:29 +00:00
status The HTTP status code to issue (301 is usual permanent redirect, 302 is temporary)
ex: 301
from The URL path that was used in the request.
The `*` wildcard character can be used for matching a full segment of the path
ex: /photos/
ex: /photos/*/*/
to The new URL path which should be used.
If wildcards matches were used they will be available as `:1`, `:2`, etc.
ex: /pics/
ex: /pics/:1/:2/
ex: https://mydomain.com/photos/:1/:2/
2015-07-08 07:46:01 +00:00
```
2017-07-26 00:37:29 +00:00
Example config:
```yml
2017-07-26 00:37:29 +00:00
http:
modules:
- type: proxy
2017-07-26 00:37:29 +00:00
domains:
- example.com
status: 301
from: /archives/*/*/*/
to: https://example.net/year/:1/month/:2/day/:3/
```
2015-07-08 07:46:01 +00:00
2017-07-26 00:37:29 +00:00
### tls
2015-06-30 23:11:01 +00:00
2017-07-26 00:37:29 +00:00
The tls system handles encrypted connections, including fetching certificates,
and uses ServerName Indication (SNI) to determine if the connection should be
handled by the http system, a tls system module, or rejected.
2017-07-26 00:37:29 +00:00
Example config:
```yml
tls:
modules:
- type: proxy
2017-07-26 00:37:29 +00:00
domains:
- example.com
- example.net
address: '127.0.0.1:6443'
```
2017-07-26 00:37:29 +00:00
Certificates are saved to `~/acme`, which may be `/var/www/acme` if Goldilocks is run as the www-data user.
### tls.proxy
The proxy module routes the traffic based on the ServerName Indication (SNI) **without decrypting** it.
It has the same options as the [HTTP proxy module](#httpproxy-how-to-reverse-proxy-ruby-python-etc).
2017-07-26 00:37:29 +00:00
Example config:
```yml
tls:
modules:
- type: proxy
2017-07-26 00:37:29 +00:00
domains:
- example.com
address: '127.0.0.1:5443'
```
### tls.acme
2017-07-26 00:37:29 +00:00
The acme module defines the setting used when getting new certificates.
2017-07-26 00:37:29 +00:00
It has the following options:
```
email The email address for ACME certificate issuance
ex: john.doe@example.com
server The ACME server to use
ex: https://acme-v01.api.letsencrypt.org/directory
ex: https://acme-staging.api.letsencrypt.org/directory
challenge_type The ACME challenge to request
ex: http-01, dns-01, tls-01
```
2017-07-26 00:37:29 +00:00
Example config:
```yml
tls:
modules:
- type: acme
2017-07-26 00:37:29 +00:00
domains:
- example.com
- example.net
email: 'joe.shmoe@example.com'
server: 'https://acme-staging.api.letsencrypt.org/directory'
challenge_type: 'http-01'
2017-07-26 00:37:29 +00:00
```
2017-11-08 19:05:38 +00:00
**NOTE:** If you specify `dns-01` as the challenge type there must also be a
[DDNS module](#ddns) defined for all of the relevant domains (though not all
domains handled by a single TLS module need to be handled by the same DDNS
module). The DDNS module provides all of the information needed to actually
set the DNS records needed to verify ownership.
2017-07-26 00:37:29 +00:00
### tcp
2017-10-30 17:16:20 +00:00
The tcp system handles both *raw* and *tls-terminated* tcp network traffic
(see the _Note_ section below the example). It may use port numbers
2017-07-26 00:37:29 +00:00
or traffic sniffing to determine how the connection should be handled.
It has the following options:
```
2017-07-26 00:37:29 +00:00
bind An array of numeric ports on which to bind
ex: 80
```
Example Config:
```yml
2017-07-26 00:37:29 +00:00
tcp:
bind:
- 22
- 80
- 443
modules:
- type: forward
2017-07-26 00:37:29 +00:00
ports:
- 22
address: '127.0.0.1:2222'
```
2017-10-30 17:16:20 +00:00
_Note_: When tcp traffic comes into goldilocks it will be tested against the tcp modules.
The connection may be handed to the TLS module if it appears to be a TLS/SSL/HTTPS connection
and if the tls module terminates the traffic, the connection will be sent back to the TLS module.
Due to the complexity of node.js' networking stack it is not currently possible to tell which
port tls-terminated traffic came from, so only the SNI header (serername / domain name) may be used for
modules matching terminated TLS.
### tcp.proxy
2017-10-30 17:16:20 +00:00
The proxy module routes traffic **after tls-termination** based on the servername (domain name)
contained in a SNI header. As such this only works to route TCP connections wrapped in a TLS stream.
It has the same options as the [HTTP proxy module](#httpproxy-how-to-reverse-proxy-ruby-python-etc).
2017-10-30 17:16:20 +00:00
This is particularly useful for routing ssh and vpn traffic over tcp port 443 as wrapped TLS
connections in order to access one of your servers even when connecting from a harsh or potentially
misconfigured network environment (i.e. hotspots in public libraries and shopping malls).
Example config:
```yml
tcp:
modules:
- type: proxy
domains:
- ssh.example.com # Note: this domain would also listed in tls.acme.domains
2017-10-30 17:16:20 +00:00
host: localhost
port: 22
2017-10-30 17:24:29 +00:00
- type: proxy
domains:
- vpn.example.com # Note: this domain would also listed in tls.acme.domains
2017-10-30 17:24:29 +00:00
host: localhost
port: 1194
```
2017-10-30 17:16:20 +00:00
_Note_: In same cases network administrators purposefully block ssh and vpn connections using
Application Firewalls with DPI (deep packet inspection) enabled. You should read the ToS of the
network you are connected to to ensure that you aren't subverting policies that are purposefully
in place on such networks.
#### Using with ssh
In order to use this to route SSH connections you will need to use `ssh`'s
`ProxyCommand` option. For example to use the TLS certificate for `ssh.example.com`
2017-10-30 17:16:20 +00:00
to wrap an ssh connection you could use the following command:
```bash
ssh user@example.com -o ProxyCommand='openssl s_client -quiet -connect example.com:443 -servername ssh.example.com'
```
Alternatively you could add the following lines to your ssh config file.
```
2017-10-30 17:16:20 +00:00
Host example.com
ProxyCommand openssl s_client -quiet -connect example.com:443 -servername ssh.example.com
```
2017-10-30 17:16:20 +00:00
#### Using with OpenVPN
There are two strategies that will work well for you:
2017-10-30 17:24:29 +00:00
1) [Use ssh](https://redfern.me/tunneling-openvpn-through-ssh/) with the config above to reverse proxy tcp port 1194 to you.
2017-10-30 17:16:20 +00:00
2017-10-30 17:24:29 +00:00
```bash
ssh -L 1194:localhost:1194 example.com
```
2) [Use stunnel]https://serverfault.com/questions/675553/stunnel-vpn-traffic-and-ensure-it-looks-like-ssl-traffic-on-port-443/681497)
```
[openvpn-over-goldilocks]
client = yes
accept = 127.0.0.1:1194
sni = vpn.example.com
2017-10-30 17:24:29 +00:00
connect = example.com:443
```
2017-10-30 17:16:20 +00:00
2017-10-30 17:24:29 +00:00
3) [Use stunnel.js](https://git.daplie.com/Daplie/node-tunnel-client) as described in the "tunnel_server" section below.
2017-10-30 17:16:20 +00:00
2017-07-26 00:37:29 +00:00
### tcp.forward
The forward module routes traffic based on port number **without decrypting** it.
In addition to the same options as the [HTTP proxy module](#httpproxy-how-to-reverse-proxy-ruby-python-etc),
the TCP forward modules also has the following options:
2017-07-26 00:37:29 +00:00
```
ports A numeric array of source ports
ex: 22
```
Example Config:
```yml
2017-07-26 00:37:29 +00:00
tcp:
bind:
- 22
- 80
- 443
modules:
- type: forward
2017-07-26 00:37:29 +00:00
ports:
- 22
port: 2222
```
### udp
The udp system handles all udp network traffic. It currently only supports
forwarding the messages without any examination.
It has the following options:
```
bind An array of numeric ports on which to bind
ex: 53
```
Example Config:
```yml
udp:
bind:
- 53
modules:
- type: forward
ports:
- 53
address: '127.0.0.1:8053'
```
### udp.forward
The forward module routes traffic based on port number **without decrypting** it.
It has the same options as the [TCP forward module](#tcpforward).
Example Config:
```yml
udp:
bind:
- 53
modules:
- type: forward
ports:
- 53
address: '127.0.0.1:8053'
```
### domains
To reduce repetition defining multiple modules that operate on the same domain
name the `domains` field can define multiple modules of multiple types for a
single list of names. The modules defined this way do not need to have their
own `domains` field. Note that the [tcp.forward](#tcpforward) module is not
allowed in a domains group since its routing is not based on domains.
Example Config
```yml
domains:
- names:
- example.com
- www.example.com
- api.example.com
modules:
tls:
- type: acme
email: joe.schmoe@example.com
challenge_type: 'http-01'
http:
- type: redirect
from: /deprecated/path
to: /new/path
- type: proxy
port: 3000
dns:
- type: 'dns@oauth3.org'
token_id: user_token_id
- names:
- ssh.example.com
modules:
tls:
- type: acme
email: john.smith@example.com
challenge_type: 'http-01'
tcp:
- type: proxy
port: 22
dns:
- type: 'dns@oauth3.org'
token_id: user_token_id
2017-07-26 00:37:29 +00:00
```
### tunnel\_server
The tunnel server system is meant to be run on a publicly accessible IP address to server tunnel clients
which are behind firewalls, carrier-grade NAT, or otherwise Internet-connect but inaccessible devices.
It has the following options:
```
secret A 128-bit or greater string to use for signing tokens (HMAC JWT)
ex: abc123
servernames An array of string servernames that should be captured as the
tunnel server, ignoring the TLS forward module
2017-07-26 00:37:29 +00:00
ex: api.tunnel.example.com
```
Example config:
```yml
2017-07-26 00:37:29 +00:00
tunnel_server:
secret: abc123def456ghi789
servernames:
- 'api.tunnel.example.com'
```
### DDNS
2017-07-26 17:44:08 +00:00
The DDNS module watches the network environment of the unit and makes sure the
device is always accessible on the internet using the domains listed in the
config. If the device has a public address or if it can automatically set up
port forwarding the device will periodically check its public address to ensure
the DNS records always point to it. Otherwise it will to connect to a tunnel
server and set the DNS records to point to that server.
The `loopback` setting specifies how the unit will check its public IP address
and whether connections can reach it. Currently only `tunnel@oauth3.org` is
supported. If the loopback setting is not defined it will default to using
`oauth3.org`.
The `tunnel` setting can be used to specify how to connect to the tunnel.
Currently only `tunnel@oauth3.org` is supported. The token specified in the
`tunnel` setting will be used to acquire the tokens that are used directly with
the tunnel server. If the tunnel setting is not defined it will default to try
using the tokens in the modules for the relevant domains.
If a particular DDNS module has been disabled the device will still try to set
up port forwarding (and connect to a tunnel if that doesn't work), but the DNS
records will not be updated to point to the device. This is to allow a setup to
be tested before transitioning services between devices.
```yaml
ddns:
disabled: false
loopback:
type: 'tunnel@oauth3.org'
domain: oauth3.org
tunnel:
type: 'tunnel@oauth3.org'
token_id: user_token_id
modules:
- type: 'dns@oauth3.org'
token_id: user_token_id
domains:
- www.example.com
- api.example.com
- test.example.com
```
### mDNS
2017-07-26 00:37:29 +00:00
enabled by default
Although it does not announce itself, Goldilocks is discoverable via mDNS with the special query `_cloud._tcp.local`.
This is so that it can be easily configured via Desktop and Mobile apps when run on devices such as a Raspberry Pi or
SOHO servers.
```yaml
2017-07-26 00:37:29 +00:00
mdns:
disabled: false
port: 5353
broadcast: '224.0.0.251'
ttl: 300
```
You can discover goldilocks with `mdig`.
```
npm install -g git+https://git.daplie.com/Daplie/mdig.git
mdig _cloud._tcp.local
```
2017-08-04 21:23:15 +00:00
### socks5
Run a Socks5 proxy server.
```yaml
socks5:
enable: true
port: 1080
```
2017-07-26 00:37:29 +00:00
### api
See [API.md](/API.md)
2017-07-26 00:37:29 +00:00
@tigerbot: How are the APIs used (in terms of URL, Method, Headers, etc)?
TODO
----
* [ ] http - nowww module
* [ ] http - Allow match styles of `www.*`, `*`, and `*.example.com` equally
* [ ] http - redirect based on domain name (not just path)
* [ ] tcp - bind should be able to specify localhost, uniquelocal, private, or ip
* [ ] tcp - if destination host is omitted default to localhost, if dst port is missing, default to src
* [ ] sys - `curl https://daplie.me/goldilocks | bash -s example.com`
* [ ] oauth3 - `example.com/.well-known/domains@oauth3.org/directives.json`
* [ ] oauth3 - commandline questionnaire
* [x] modules - use consistent conventions (i.e. address vs host + port)
* [x] tls - tls.acme vs tls.modules.acme
* [ ] tls - forward should be able to match on source port to reach different destination ports