sshuttle: | |
pkg.installed: [] | |
group.present: | |
- gid: 2024 | |
user.present: | |
- fullname: sshuttle | |
- uid: 2024 | |
- gid: 2024 | |
- allow_uid_change: True | |
- allow_gid_change: True | |
- createhome: True | |
- shell: '/bin/bash' | |
- home: /home/sshuttle | |
## Manage SSH Keys | |
ssh_auth.present: | |
- user: sshuttle | |
- names: | |
- ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKlQ2xxxxxxxxxxxxxxxx sshuttle | |
file.managed: | |
- names: | |
- /home/sshuttle/.ssh/id_ed25519: | |
- source: salt://{{ slspath }}/files/id_ed25519 | |
- mode: 600 | |
- /home/sshuttle/.ssh/id_ed25519.pub: | |
- source: salt://{{ slspath }}/files/id_ed25519.pub | |
- mode: 644 | |
- user: sshuttle | |
- group: sshuttle | |
/etc/sshuttle: | |
file.directory: | |
- user: sshuttle | |
- group: sshuttle | |
- dirmode: 744 | |
- makedirs: True | |
sshuttle_config: | |
file.serialize: | |
- name: /etc/sshuttle/config.json | |
- formatter: json | |
- create: True | |
- user: sshuttle | |
- group: sshuttle | |
- mode: 644 | |
- makedirs: True | |
- dataset: | |
{{ salt['pillar.get']('sshuttle') }} | |
sshuttle_runscript: | |
file.managed: | |
- name: /etc/sshuttle/sshuttle.py | |
- source: salt://{{ slspath }}/files/sshuttle.py | |
- user: sshuttle | |
- group: sshuttle | |
- mode: 744 | |
sshuttle_sudoers: | |
file.managed: | |
- name: /etc/sudoers.d/sshuttle | |
- source: salt://{{ slspath }}/files/sudoers | |
- user: root | |
- group: root | |
- mode: 644 | |
sshuttle_service_file: | |
file.managed: | |
- name: /etc/systemd/system/sshuttle.service | |
- source: salt://{{ slspath }}/files/service | |
- user: root | |
- group: root | |
- mode: 644 | |
sshuttle_service: | |
service.running: | |
- name: sshuttle | |
- enable: True | |
- watch: | |
- file: sshuttle_config | |
#!/usr/bin/env python | |
## REQUIRES MINIMUM PY VERSION 2.7 | |
from __future__ import print_function | |
import os | |
import sys | |
import json | |
import signal | |
import time | |
import socket | |
import subprocess | |
from subprocess import CalledProcessError | |
import logging | |
import logging.handlers | |
log = logging.getLogger(__name__) | |
log.setLevel(logging.DEBUG) | |
handler = logging.handlers.SysLogHandler(address = '/dev/log') | |
formatter = logging.Formatter('%(module)s.%(funcName)s: %(message)s') | |
handler.setFormatter(formatter) | |
log.addHandler(handler) | |
conf = "/etc/sshuttle/config.json" | |
ssh_user = "sshuttle" ## username thats used for SSH connection | |
def precheck(): | |
if len(sys.argv) < 2: | |
print("need to pass argument: start | stop | restart | status ") | |
sys.exit() | |
if sys.argv[1] in ["help", "-h", "--help", "h"]: | |
print("sshuttle.py start | stop | restart | status") | |
sys.exit() | |
if not sys.argv[1] in ["start", "stop", "restart", "status"]: | |
print("usage: sshuttle.py start | stop | restart | status") | |
sys.exit() | |
if not os.path.exists(conf): | |
print("no sshuttle config file present, exiting.") | |
sys.exit() | |
# check if sshuttle is installed | |
try: | |
subprocess.check_output(["which", "sshuttle"]).strip() | |
except CalledProcessError: | |
print("sshuttle is not installed on this host") | |
sys.exit() | |
def start(): | |
with open(conf) as jsondata: | |
data = json.load(jsondata) | |
keys = sorted(data.keys()) | |
for rhost in data.keys(): | |
if ":" in rhost: | |
relay = rhost.split(":")[1] | |
else: | |
relay = rhost | |
netrange = "" | |
# if single network, turn into List | |
if not type(data[rhost]) is list: | |
networks = data[rhost].split() | |
else: | |
networks = data[rhost] | |
for network in networks: | |
# check if CIDR format | |
if "/" in network: | |
netrange = netrange + " " + network | |
else: | |
netrange = netrange + " " + socket.gethostbyname(network) | |
netrange = netrange.strip() | |
# build rpath | |
rpath = "-r {0}@{1} {2} -l listen '0.0.0.0' --ssh-cmd 'ssh -o ServerAliveInterval=60' --no-latency-control".format(ssh_user, rhost, netrange) | |
try: | |
print("starting sshuttle..") | |
log.info("starting sshuttle for networks: %s via %s" % (netrange, rhost)) | |
subprocess.Popen("sshuttle {}".format(rpath), shell=True) | |
except CalledProcessError as err: | |
log.error("error running sshuttle: %s" % str(err)) | |
# sleep to give connection time to establish SSH handshake, in case other connections use this conn as a hop | |
time.sleep(3) | |
def get_pid(): | |
search = "ps -ef | grep '/usr/bin/python /usr/share/sshuttle/main.py /usr/bin/python -r' | grep -v grep | awk {'print $2'}" | |
pids = [] | |
for line in os.popen(search): | |
fields = line.split() | |
pids.append(fields[0]) | |
return pids | |
def stop(): | |
pids = get_pid() | |
for pid in pids: | |
print("stopping sshuttle PID %s " % pid) | |
log.info("stopping sshuttle") | |
os.kill(int(pid), signal.SIGTERM) | |
def status(): | |
pids = get_pid() | |
if pids: | |
print("sshuttle is running..") | |
else: | |
print("sshuttle is not running..") | |
if __name__ == "__main__": | |
precheck() | |
cmd = sys.argv[1].lower() | |
if cmd == "start": | |
start() | |
if cmd == "stop": | |
stop() | |
if cmd == "restart": | |
print("restarting sshuttle..") | |
stop() | |
start() | |
if cmd == "status": | |
status() |
[Unit] | |
Description=sshuttle service | |
After=network.target | |
[Service] | |
User=sshuttle | |
Restart=always | |
Type=forking | |
WorkingDirectory=/etc/sshuttle | |
ExecStart=/etc/sshuttle/sshuttle.py start | |
ExecStop=/etc/sshuttle/sshuttle.py stop | |
[Install] | |
WantedBy=multi-user.target |
#!/bin/sh | |
### SSHUTTLE INIT.D SERVICE SCRIPT | |
### REQUIRED MINIMUM Python version of 2.7 | |
SCRIPT=/etc/sshuttle/sshuttle.py | |
RUNAS=sshuttle | |
LOGFILE=/var/log/messages | |
start() { | |
su -c "$SCRIPT start" $RUNAS >&2 | |
} | |
stop() { | |
su -c "$SCRIPT stop" $RUNAS >&2 | |
} | |
restart() { | |
stop | |
start | |
} | |
status() { | |
su -c "$SCRIPT status" >&2 | |
} | |
case "$1" in | |
start) | |
start | |
;; | |
stop) | |
stop | |
;; | |
restart) | |
stop | |
start | |
;; | |
status) | |
status | |
;; | |
*) | |
echo "Usage: $0 {start|stop|restart|status}" | |
esac |
sshuttle ALL=(root) NOPASSWD: /usr/bin/python /usr/share/sshuttle/main.py /usr/bin/python --firewall 12*** 0 | |
I think it's better to leave out the remote_user
from the config completely and let sshuttle
use the user specified in ~/.ssh/config
.
Also, and more importantly, please send SIGTERM
rather than SIGKILL
, so that sshuttle
can clean up the iptables rules. Otherwise the host is left in a messy state with non-working connections, and requires a restart (or manual removing of iptables
rules). In general, you should never send SIGKILL
unless the process has become unresponsive and there is no other way to stop it.
updated to use SIGTERM
Hello
thank you for good job
but where i add remote server credentials like username / password and port?
can add to /home/sshuttle/.ssh/config
Host abc1
Hostname abc1.corp.com
User joe
Port 22
IdentityFile /home/joe/.ssh/id_rsa
Using passwords for ssh is not recommended , its insecure, and theres no way to store them, you have to manually enter them during ssh handshake, use keypairs instead
Thank you for your reply
I have already added it to config file but by questions is
How does this script know which host in config to connect to
Should I call the “Host” abc1?
Shouldn’t I add my “Host” name into script ? Or the script reads it as “Host” abc1?
Patched for modern python:
Also, the remote SSH user is now a json parameter: