Skip to content

Commit

Permalink
Merge pull request #4 from riotkit-org/feature/type-local
Browse files Browse the repository at this point in the history
Version 2.0 (compatible with current stable)
  • Loading branch information
blackandred authored Feb 25, 2019
2 parents 79e7d9e + 40471d9 commit 90d4b2f
Show file tree
Hide file tree
Showing 13 changed files with 449 additions and 94 deletions.
1 change: 1 addition & 0 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/Dockerfile
17 changes: 17 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
FROM alpine:3.9

COPY ./ /rn

RUN apk add --update bash autossh openssh-client netcat-openbsd grep \
&& rm -rf /var/cache/apk/* \
&& mkdir -p /home/revproxy \
&& addgroup -g 1005 revproxy \
&& adduser -D -u 1005 -h /home/revproxy -G revproxy revproxy \
&& chown -R revproxy:revproxy /home/revproxy

VOLUME "/rn/conf.d"
VOLUME "/home/revproxy/.ssh"
WORKDIR "/rn"

ENTRYPOINT ["/rn/docker-entrypoint.sh"]
CMD ["/rn/bin/bind-network.sh --healthcheck-loop"]
9 changes: 9 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
SUDO=sudo

all: build build_arm

build:
${SUDO} docker build . -t wolnosciowiec/reverse-networking

build_arm:
${SUDO} docker build -f ./armhf.Dockerfile . -t wolnosciowiec/reverse-networking:armhf
78 changes: 73 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,26 @@
# reverse-networking
Network setup automation scripts written in Bash, based on reverse proxy idea.
Allows to create multiple reverse tunnels from inside of NAT to the external server.
Network setup automation scripts written in Bash.
Allows to create multiple tunnels from inside of NAT to the external server.

Works in two cases:
- #1: Can expose a NAT hidden service to the external server (or to the internet via external server)
- #2: Can encrypt a connection with external server by adding SSH layer (eg. MySQL replication with external server with SSH encryption layer)

![example structure](./docs/Reverse%20networking%20infrastructure.png "Reverse networking structure")

## Requirements

Those packages needs to be installed:
Those very basic packages needs to be installed:
- bash
- autossh
- ssh (client)
- awk
- grep
- nc

Works with GNU utils as well as with Busybox.
Tested on Arch Linux, Debian and Alpine Linux.

*The remote server needs to support public-key authorization method.*

## Setup
Expand All @@ -26,7 +33,7 @@ Those packages needs to be installed:
3. File must be in a proper syntax and implement proper configuration variables
described as an example in the "config-example.sh.md"
```

Send public key to all servers described in your configuration
so the communication could be without a password using a ssh key.

Expand All @@ -41,6 +48,66 @@ Your local services should be exposed to the remote server and be
visible on eg. http://localhost:1234, so you need an internal proxy or
a load balancer like nginx to forward the traffic to the internet.

## Docker

Use images `wolnosciowiec/reverse-networking` and `wolnosciowiec/reverse-networking:armhf` to run container with reverse-networking installed.

## Example configurations


##### Expose MySQL from docker container

How to connect between two separate docker networks using SSH, and access a hidden MySQL server.

```gherkin
Given we have a HOST_1 with SSH container + MySQL container
And we have a client HOST_2
When we want to access MySQL:3306 from HOST_2
Then we make a tunnel from HOST_2 to HOST_1 SSH container that exposes db_mysql:3306
And we make it available as a localhost:3307 at HOST_2
```

```bash
PN_USER=revproxy # HOST_1 user in SSH
PN_PORT=9800 # HOST_1 port
PN_HOST=192.168.0.114 # HOST_1 host
PN_VALIDATE=none
PN_TYPE=local # connection type - we access remote resource, not exposing self to remote
PN_SSH_OPTS= # optional SSH options
PORTS[0]="3307>3306>db_mysql" # HOST_1 container name
#PORTS[1]="3307>3306>db_mysql>@gateway" # expose on HOST_2 gateway interface (visible from internet)
```

##### Expose ports to external server

Expose health check endpoints of a machine hidden behind NAT/firewall to an external machine via SSH.

```gherkin
Given we have a HOST_1 that is a VPS with public IP address and SSH server
And we have a HOST_2 that is behind NAT
When we want to access /healthcheck endpoint placed at HOST_2 from internet we call http://some-subdomain.HOST_1/healthcheck
Then we make a tunnel from HOST_2 to HOST_1 exposing a HTTP webserver from HOST_2 to HOST_1:8000
```

```bash
PN_USER=some_host_1_user
PN_PORT=22
PN_HOST=host_1.org
PN_VALIDATE=local
PN_TYPE=reverse
PN_SSH_OPTS=

# optional:
#PN_VALIDATE_COMMAND="curl http://mydomain.org" # custom validation command that will be ran locally or remotely

# destination port on remote server => local port, will be available as localhost:8000 on HOST_1
PORTS[0]="80>8000"

# requires GatewayPorts in SSH to be enabled, can be insecure, will be available at PUBLIC_IP_ADDRESS:8001
# easier option to configure, does not require a webserver to expose local port to the internet
#PORTS[1]="80>8001>@gateway" # port will be available publicly
```

#### Monitoring

There is a tool in `./bin/monitor.sh` that verifies all tunnels by doing a ping
Expand All @@ -61,4 +128,5 @@ Use `PN_VALIDATE_COMMAND` for custom validation executed locally or remotely if
Examples:
- PN_VALIDATE_COMMAND="/bin/true" # for testing purposes, try it yourself
- PN_VALIDATE_COMMAND="/bin/false" # for testing
- PN_VALIDATE_COMMAND="curl http://your-domain.org:8002"
- PN_VALIDATE_COMMAND="curl http://your-domain.org:8002"
- PN_VALIDATE_COMMAND="wget -O - -T 2 http://172.28.0.6:3307 2>&1|grep mariadb"
19 changes: 19 additions & 0 deletions armhf.Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
FROM balenalib/armv7hf-alpine:3.9

COPY ./ /rn

RUN [ "cross-build-start" ]
RUN apk add --update bash autossh openssh-client netcat-openbsd grep \
&& rm -rf /var/cache/apk/* \
&& mkdir -p /home/revproxy \
&& addgroup -g 1005 revproxy \
&& adduser -D -u 1005 -h /home/revproxy -G revproxy revproxy \
&& chown -R revproxy:revproxy /home/revproxy
RUN [ "cross-build-end" ]

VOLUME "/rn/conf.d"
VOLUME "/home/revproxy/.ssh"
WORKDIR "/rn"

ENTRYPOINT ["/rn/docker-entrypoint.sh"]
CMD ["/rn/bin/bind-network.sh --healthcheck-loop"]
56 changes: 56 additions & 0 deletions bin/add-to-known-hosts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/bin/bash

#--------------------------------------------
# Kill all previously opened ssh sessions
#
# @author RiotKit Team
# @see riotkit.org
#--------------------------------------------

cd "$( dirname "${BASH_SOURCE[0]}" )"
source include/functions.sh

KNOWN_HOSTS_FILE=~/.ssh/known_hosts

#
# Iterate over each host and fetch it's fingerprint
#
# @framework method
#
executeIterationAction() {
config_file_name=$1

if contains_fingerprint ${PN_HOST} ${PN_PORT}; then
echo " .. Fingerprint already present"
return 0
fi

echo " .. Fetching a fingerprint for ${PN_HOST}:${PN_PORT}"
ssh-keyscan -p "${PN_PORT}" "${PN_HOST}" >> ${KNOWN_HOSTS_FILE}
}

#
# $1 - hostname
# $2 - port
#
contains_fingerprint () {
content=$(cat ${KNOWN_HOSTS_FILE})
host_name=${1}

# non-standard port is differently formatted
if [[ "${2}" != "22" ]]; then
host_name="[${1}]:${2}"
fi

if [[ "${content}" == *"${host_name} ssh-"* ]] \
|| [[ "${content}" == *"${host_name} ecdsa"* ]] \
|| [[ "${content}" == *"${host_name},"* ]]; then
return 0
fi

return 1
}

echo " >> Fetching hosts fingerprint first time"
cat ${KNOWN_HOSTS_FILE}
iterateOverConfiguration
71 changes: 32 additions & 39 deletions bin/bind-network.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,48 +4,41 @@
# Bind network ports to the remote server
# using a reverse proxy strategy
#
# @author Wolnościowiec Team
# @see https://wolnosciowiec.net
# @author RiotKit Team
# @see riotkit.org
#--------------------------------------------

cd "$( dirname "${BASH_SOURCE[0]}" )"
source include/functions.sh
DIR=$(pwd)

./kill-previous-sessions.sh

for config_file_name in ../conf.d/*.sh
do
echo " >> Reading $config_file_name"
source "$config_file_name"

for forward_ports in ${PORTS[*]}
do
IFS='>' read -r -a parts <<< "$forward_ports"
source_port=${parts[0]}
dest_port=${parts[1]}
dest_host=""

if [[ "${parts[2]}" ]]; then
dest_host="${parts[2]}:"

if [[ "${dest_host}" == "@gateway:" ]]; then
dest_host="$(getHostIpAddress $PN_HOST):"
fi
fi

echo " --> Forwarding ${dest_host}${source_port}:${PN_HOST}:${dest_port}"
autossh -M 0 -N -f -o "PubkeyAuthentication=yes" -o "PasswordAuthentication=no" -R "${dest_host}${source_port}:localhost:${dest_port}" "${PN_USER}@${PN_HOST}" -p ${PN_PORT}

if [[ $? != 0 ]]; then
echo " ~ The port forwarding failed, please verify if your SSH keys are well installed"
exit 1
fi
done
done

if [[ $1 == "--loop" ]]; then
while true; do
sleep 10
done
fi
#
# @framework method
#
executeIterationAction () {
setupTunnelsForHost "${PN_USER}" "${PN_HOST}" "${PN_PORT}" "${PN_TYPE}" "${PORTS}"
}

main () {
./kill-previous-sessions.sh
iterateOverConfiguration

if [[ $1 == "--loop" ]]; then
echo ' >> Running in a loop'

while true; do
sleep 10
done
fi

if [[ $1 == "--healthcheck-loop" ]]; then
echo " >> Running a healthcheck loop (SLEEP_TIME=${LOOP_SLEEP_TIME})"

while true; do
sleep ${LOOP_SLEEP_TIME:-5}
$(dirname "${BASH_SOURCE[0]}")/../bin/monitor.sh
done
fi
}

main "$@"
Loading

0 comments on commit 90d4b2f

Please sign in to comment.