bring README.md over from ziti-edge, as it is command-centric.

This commit is contained in:
Shawn Carey
2019-12-03 15:01:03 -05:00
parent c08ad74ffa
commit 01836722a4
+276
View File
@@ -0,0 +1,276 @@
# ziti-tunnel
`ziti-tunnel` intercepts outbound transport layer packets to Ziti service addresses
and proxies them through Ziti.
The services that are proxied are determined by the app-wan membership of the
identity that is defined in the configuration file.
## Configuration File
A configuration file is created when an identity is successfully enrolled with
a Ziti edge controller. Here's an example of a configuration file that
`ziti-tunnel` accepts:
{
"ztAPI": "https://ziti-dev-controller01.localhost:1080/",
"versions": {
"api": "1.0.0",
"enrollmentApi": "1.0.0"
},
"id": {
"cert": "file:///root/device01.3rdparty.client.cert.pem",
"key": "file:///root/device01.3rdparty.client.key.pem",
"ca": "file:///root/ziti-dev-ingress01.external.chain.cert.pem"
}
}
## Usage
ziti-tunnel authenticates with the edge controller in the config file, and
periodically queries the controller for services that the identity is allowed to
use. Whenever a new service is found, ziti-tunnel invokes the selected intercept
mode with the details of the service.
`ziti-tunnel` supports several intercept modes, which are specified with a sub-command.
Typically you will run `ziti-tunnel` with the _run_ sub-command, which determines
the preferred intercept mode based on the kernel drivers that are available on the
host.
$ sudo ziti-tunnel run ziti.json
[ 0.000] INFO ziti/tunnel/intercept/tproxy.New: tproxy listening on 127.0.0.1:33641
[ 0.006] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.run: using tproxy interceptor
...
When started on a host that lacks __iptables__, the tproxy intercept mode initializer
fails and `ziti-tunnel` attempts to use the tun intercept mode:
$ sudo ziti-tunnel run ziti.json
[ 0.001] INFO ziti/tunnel/intercept/tproxy.New: tproxy listening on 127.0.0.1:37313
[ 0.001] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.run: tproxy initialization failed: failed to initialize iptables handle: exec: "iptables": executable file not found in $PATH
[ 0.009] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.run: using tun interceptor
`ziti-tunnel` fails to start if no intercept modes can be successfully initialized:
$ sudo ziti-tunnel run ziti.json
[ 0.000] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.run: tproxy initialization failed: failed to initialize iptables handle: exec: "iptables": executable file not found in $PATH
[ 0.001] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.run: tun initialization failed: failed to open tun interface (name='', mtu=0): ioctl failed with 'invalid argument'
[ 0.001] FATAL ziti/tunnel/cmd/ziti-tunnel/subcmd.run: failed to initialize an interceptor
## Intercept Modes
### tproxy
tproxy is the preferred intercept mode when running on a Linux kernel that has the
__ip_tables__ kernel module installed.
$ lsmod | grep ip_tables
ip_tables 32768 5 iptable_filter,iptable_security,iptable_raw,iptable_nat,iptable_mangle
`ziti-tunnel` manipulates routing tables and firewall rules when using the tproxy
intercept mode. The NET_ADMIN Linux capability is required for these actions. The
usage example here runs ziti-tunnel with sudo as a simple way to obtain
NET_ADMIN:
$ sudo ziti-tunnel --identity ziti.json tproxy
[ 0.000] INFO ziti/tunnel/intercept/tproxy.New: tproxy listening on 127.0.0.1:33355
[ 0.010] INFO ziti/tunnel/dns.NewDnsServer: starting dns server...
[ 2.018] INFO ziti/tunnel/dns.NewDnsServer: dns server running at 127.0.0.1:53
[ 2.018] INFO ziti/tunnel/dns.(*resolver).AddHostname: adding ziti-tunnel.resolver.test = 19.65.28.94 to resolver
[ 2.033] INFO ziti/tunnel/dns.(*resolver).RemoveHostname: removing ziti-tunnel.resolver.test from resolver
[ 2.096] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: starting tunnel for newly available service wttr.in
[ 2.290] INFO ziti/tunnel/dns.(*resolver).AddHostname: adding wttr.in = 5.9.243.187 to resolver
[ 2.300] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: service wttr.in not hostable
[ 2.300] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: starting tunnel for newly available service ssh-scarey
[ 2.570] INFO ziti/tunnel/dns.(*resolver).AddHostname: adding scarey.io = 169.254.1.1 to resolver
The tproxy intercept mode creates a network listener that accepts connections at a
randomly selected port on the loopback interface. Intercepted ziti service traffic
directed to the listener by two mechanisms:
* Firewall Rules (iptables)
The TPROXY iptables target is the primary intercept mechanism used by the tproxy
intercept mode. The TPROXY target essentially sends packets to a local listener
without actually modifying the packet's destination address fields. See
https://www.kernel.org/doc/Documentation/networking/tproxy.txt and
`iptables-extensions(8)` for more details on the TPROXY target.
First, the tproxy interceptor links a new iptables chain to the PREROUTING chain:
$ sudo iptables -nt mangle -L PREROUTING | grep NF-INTERCEPT
NF-INTERCEPT all -- 0.0.0.0/0 0.0.0.0/0
Then it creates rules in the new chain for each intercepted service. You can view
the tproxy rules in play:
$ sudo iptables -nt mangle -L NF-INTERCEPT
Chain NF-INTERCEPT (1 references)
target prot opt source destination
TPROXY tcp -- 0.0.0.0/0 5.9.243.187 /* wttr.in */ tcp dpt:443 TPROXY redirect 127.0.0.1:33355 mark 0x1/0x1
TPROXY tcp -- 0.0.0.0/0 169.254.1.1 /* ssh-scarey */ tcp dpt:22 TPROXY redirect 127.0.0.1:33355 mark 0x1/0x1
TPROXY tcp -- 0.0.0.0/0 1.2.3.4 /* netcat */ tcp dpt:22169 TPROXY redirect 127.0.0.1:33355 mark 0x1/0x1
Packets with a destination address that matches the intercept address of a Ziti
service are directed to ziti-tunnel's network listener (127.0.0.1:33355 in the
examples above). This effectively enables `ziti-tunnel` to capture packets that
are destined for any address using a single listener (and a single port).
NOTE: _netfilter_ rules were considered when implementing ziti-tunnel's tproxy
intercept mode. _netfilter_ is a slightly more modern than _iptables_ and has
a supported netlink API for manipulating rules without "shelling out" to the
`iptables` command line utility. _netfilter_ was ultimately abandoned because
netfilter tproxy support requires kernel configuration options (`CONFIG_NFT_TPROXY`,
`CONFIG_NFT_SOCKET`) that are not enabled in the default kernels of many common
Linux distributions.
* Local Routes
The TPROXY target is only valid in the PREROUTING iptables chain, which is
traversed by incoming packets that were routed to the host over the network. A
_local_ route is necessary in order to get locally generated packets to traverse
the PREROUTING chain:
$ ip route show table local
local 1.2.3.4 dev lo proto kernel scope host src 1.2.3.4
local 5.9.243.187 dev lo proto kernel scope host src 5.9.243.187
local 169.254.1.1 dev lo proto kernel scope host src 169.254.1.1
### tun
The tun intercept mode creates an ephemeral tun interface and configures it with the
IP addresses of the services that are being proxied.
`ziti-tunnel` manipulates network interfaces when using the tun intercept mode,
which requires the NET_ADMIN Linux capability. The usage example here runs
ziti-tunnel with sudo as a simple way to obtain NET_ADMIN:
$ sudo ziti-tunnel --identity ziti.json tun
[ 0.010] INFO ziti/tunnel/dns.NewDnsServer: starting dns server...
[ 2.012] INFO ziti/tunnel/dns.NewDnsServer: dns server running at 127.0.0.1:53
[ 2.012] INFO ziti/tunnel/dns.(*resolver).AddHostname: adding ziti-tunnel.resolver.test = 19.65.28.94 to resolver
[ 2.031] INFO ziti/tunnel/dns.(*resolver).RemoveHostname: removing ziti-tunnel.resolver.test from resolver
[ 2.089] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: starting tunnel for newly available service wttr.in
[ 2.280] INFO ziti/tunnel/dns.(*resolver).AddHostname: adding wttr.in = 5.9.243.187 to resolver
[ 2.282] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: service wttr.in not hostable
[ 2.282] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: starting tunnel for newly available service ssh-scarey
[ 2.502] INFO ziti/tunnel/dns.(*resolver).AddHostname: adding scarey.io = 169.254.1.2 to resolver
[ 2.505] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: service ssh-scarey not hostable
[ 2.505] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: starting tunnel for newly available service netcat
[ 2.506] INFO ziti/tunnel/cmd/ziti-tunnel/subcmd.updateServices: service netcat not hostable
...
The addresses that `ziti-tunnel` adds to the tun interface are __point-to-point__
addresses:
$ ip addr show dev tun0
10: tun0: <POINTOPOINT,MULTICAST,NOARP,UP,LOWER_UP> mtu 65535 qdisc fq_codel state UNKNOWN group default qlen 500
link/none
inet 169.254.1.1/32 scope host tun0
valid_lft forever preferred_lft forever
inet 169.254.1.1 peer 5.9.243.187/32 scope host tun0
valid_lft forever preferred_lft forever
inet 169.254.1.1 peer 169.254.1.2/32 scope host tun0
valid_lft forever preferred_lft forever
inet 169.254.1.1 peer 1.2.3.4/32 scope host tun0
valid_lft forever preferred_lft forever
The tun interface itself is assigned a link-local address, 169.254.1.1 in this
case, and each intercepted service is represented by a point-to-point address with
the remote address matching the intercept IP of the Ziti service. The tun intercept
mode uses point-to-point addresses instead of local routes because local routes
would result in the Linux networking stack receiving the packets that are routed to
the tun interface. The point-to-point addresses ensure that the packets are delivered
"to the wire", which mean that, for a tun interface, the packets will be picked up
by `ziti-tunnel` when it reads data from the tun interface.
### proxy
The proxy intercept mode creates a network listener for each Ziti service that is
intercepted. The services to intercept, and the ports that they are intercepted on,
are specified on the command line (as opposed to using the service definitions that
are retrieved from the edge controller):
$ ziti-tunnel --identity ziti.json proxy wttr.in:8443 ssh-scarey:2222 netcat:22169
[ 0.004] INFO ziti/tunnel/intercept/proxy.(*proxyInterceptor).Start: starting proxy interceptor
[ 0.120] INFO ziti/tunnel/intercept.updateServices: starting tunnel for newly available service ssh-scarey
[ 0.183] INFO ziti/tunnel/intercept.updateServices: service ssh-scarey not hostable
[ 0.183] INFO ziti/tunnel/intercept.updateServices: starting tunnel for newly available service netcat
[ 0.183] INFO ziti/tunnel/intercept/proxy.proxyInterceptor.runServiceListener: {addr=[0.0.0.0:2222] service=[ssh-scarey]} service is listening
[ 0.203] INFO ziti/tunnel/intercept.updateServices: service netcat not hostable
[ 0.203] INFO ziti/tunnel/intercept/proxy.proxyInterceptor.runServiceListener: {addr=[0.0.0.0:22169] service=[netcat]} service is listening
[ 0.203] INFO ziti/tunnel/intercept.updateServices: starting tunnel for newly available service wttr.in
[ 0.226] INFO ziti/tunnel/intercept.updateServices: service wttr.in not hostable
[ 0.226] INFO ziti/tunnel/intercept/proxy.proxyInterceptor.runServiceListener: {addr=[0.0.0.0:8443] service=[wttr.in]} service is listening
All network listeners bind to local network interfaces (0.0.0.0):
$ netstat -tnl | fgrep 0.0.0.0
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State
tcp 0 0 0.0.0.0:2222 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:22169 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:8443 0.0.0.0:* LISTEN
Proxy mode is intended to be a developer tool, most useful in situations where root
privileges are not available.
## DNS Server
ziti-tunnel runs an internal DNS server by default. The DNS server must be first in the host's resolver
configuration (e.g. resolve.conf). A self-test is performed when ziti-tunnel starts to ensure that its
internal DNS server is configured in the system resolver:
INFO[0002] dns server started on 127.0.0.1:53
INFO[0002] adding ziti-tunnel.resolver.test -> 19.65.28.94 to resolver
INFO[0002] removing ziti-tunnel.resolver.test from resolver
The test involves inserting a known hostname/IP address into the internal DNS server, and using the system
resolver to retrieve the address of the hostname. *ziti-tunnel will exit if the DNS self-test fails.*
Linux distributions typically manage the contents of /etc/resolv.conf, so simply editing the file
will only work for a short time until /etc/resolv.conf is overwritten by the managing process.
Resolver configuration changes must survive restarts of the Linux name resolution manager. Linux
distrubutions use one of several name resolution managers. The simplest way to determine which name
resolution manager is being used by your Linux distrubtion is to look at /etc/resolv.conf:
$ ls -l /etc/resolv.conf
* If /etc/resolv.conf is a regular file, then it is most likely being managed by `dhclient`.
* If /etc/resolv.conf is a symlink to a file in /run/systemd/resolve, then it is being
managed by `systemd-resolved`
#### dhclient
If your Linux distribution uses dhclient, you can configure the system resolver to use
ziti-tunnel's internal DNS server first by adding the following to /etc/dhcp/dhclient.conf:
prepend domain-name-servers 127.0.0.1;
Then restart network manager. Unless you know the name of the NetworkManager systemd
service on your Linux distrubtion, it's probably easiest to reboot the host.
#### systemd-resolved
$ sudo ln -sf /run/systemd/resolve/resolv.conf /etc
$ echo -e "[Resolve]\nDNS=127.0.0.1" | sudo tee /etc/systemd/resolved.conf.d/ziti-tunnel.conf
$ sudo systemctl restart systemd-resolved
If you are unable to control the resolver on your operating system, ziti-tunnel can use/update a hosts file for
any hostnames that it tunnels:
ziti-tunnel run --resolver file:///etc/hosts "${HOME}/ziti.json"
### IP Address Assignment
If the service specifies a hostname for its address, ziti-tunnel resolves the hostname and adds the result to its
internal DNS server:
[0127] INFO adding myservice.mydomain.com -> 45.60.32.165 to resolver
If the service hostname does not resolve, ziti-tunnel will find an unused link-local address and assign it to
the route for the service:
[0012] INFO adding bogushost.net -> 169.254.1.4 to resolver
[0012] INFO ziti/tunnel/protocols/tcp.Listen: Accepting on 169.254.1.4:25 service=telnet