Skip to content

Instantly share code, notes, and snippets.

@OlegJakushkin
Last active April 10, 2021 09:39
Show Gist options
  • Save OlegJakushkin/b6f4d2976b3d22bda222e43d265c529b to your computer and use it in GitHub Desktop.
Save OlegJakushkin/b6f4d2976b3d22bda222e43d265c529b to your computer and use it in GitHub Desktop.
RemoteSSH SDN via l3ns
# docker build -t sshnode .
FROM ubuntu:20.04
## kubenode software base
RUN apt update \
&& apt install -yq software-properties-common ca-certificates openssh-client apt-transport-https \
wget curl nano htop iptables supervisor systemd
### SSH
RUN useradd --create-home --no-log-init --shell /bin/bash -g root vagrant && \
usermod -aG sudo vagrant && \
usermod -aG users vagrant && \
echo "vagrant:vagrant" | chpasswd
RUN apt update && apt --no-install-recommends install -y putty-tools plink openssh-server ssh && \
mkdir /var/run/sshd && \
sed -ri 's/^#?PermitRootLogn\s+.*/PermitRootLogin yes/' /etc/ssh/sshd_config && \
sed -ri 's/UsePAM yes/#UsePAM yes/g' /etc/ssh/sshd_config && \
echo "\nAllowGroups root" >> /etc/ssh/sshd_config && \
echo "\nAllowUsers vagrant" >> /etc/ssh/sshd_config && \
mkdir /root/.ssh && \
mkdir /home/vagrant/.ssh
EXPOSE 22
ENTRYPOINT ["startup.sh"]
CMD ["/bin/bash"]
[ldc] subnet master_worker2 started
[ldc] subnet master_worker1 started
node master started
node worker1 started
node worker2 started
unlocking master
unlocking worker1
unlocking worker2
on master running: /bin/bash -c "/usr/sbin/sshd"
0
on worker1 running: /bin/bash -c "/usr/sbin/sshd"
0
on worker2 running: /bin/bash -c "/usr/sbin/sshd"
0
started SSH deamons on all nodes
on master running: /bin/bash -c "echo y | plink -ssh [email protected] -pw vagrant "exit" "
0
The server's host key is not cached. You have no guarantee
that the server is the computer you think it is.
The server's ssh-ed25519 key fingerprint is:
ssh-ed25519 255 6e:c5:18:dc:94:61:d3:43:cb:24:c4:cf:f6:63:a5:16
If you trust this host, enter "y" to add the key to
PuTTY's cache and carry on connecting.
If you want to carry on connecting just once, without
adding the key to the cache, enter "n".
If you do not trust this host, press Return to abandon the
connection.
Store key in cache? (y/n)
on master running: /bin/bash -c "plink -ssh [email protected] -pw vagrant ls -lah"
0
total 24K
drwxr-xr-x 1 vagrant root 4.0K Dec 16 10:25 .
drwxr-xr-x 1 root root 4.0K Dec 16 09:36 ..
-rw-r--r-- 1 vagrant root 220 Feb 25 2020 .bash_logout
-rw-r--r-- 1 vagrant root 3.7K Feb 25 2020 .bashrc
-rw-r--r-- 1 vagrant root 807 Feb 25 2020 .profile
drwxr-xr-x 2 root root 4.0K Dec 16 10:25 .ssh
from l3ns.ldc import DockerNode
from l3ns.base.network import Network
from l3ns import defaults
#import docker
#client = docker.from_env()
#image, build_log = client.images.build(path=".", dockerfile='Dockerfile', tag="sshnode", rm=True)
defaults.network = Network('49.0.0.0/8')
master = DockerNode('master', image='sshnode', command='tail -f /dev/null', tty=True, stdin_open=True, )
worker1 = DockerNode('worker1', image='sshnode', command='tail -f /dev/null', tty=True, stdin_open=True)
worker2 = DockerNode('worker2', image='sshnode', command='tail -f /dev/null', tty=True, stdin_open=True)
master.connect_to(worker1)
master.connect_to(worker2)
master.connect_to_internet = True
worker1.connect_to_internet = True
worker2.connect_to_internet = True
def run_on_a_node(node_ctx, cmd):
print("on "+node_ctx.name+" running: " + cmd )
ret = node_ctx.container.exec_run(cmd)
print(ret.exit_code)
print(ret.output.decode('ascii'))
return ret
def run_on_all_nodes(str):
run_on_a_node(master, str)
run_on_a_node(worker1, str)
run_on_a_node(worker2, str)
with defaults.network:
run_on_all_nodes( """/bin/bash -c "/usr/sbin/sshd" """)
print("started SSH deamons on all nodes")
run_on_a_node(master, """/bin/bash -c "echo y | plink -ssh vagrant@"""+ str(worker1.get_ip())+ """ -pw vagrant \"exit\" " """)
run_on_a_node(master, """/bin/bash -c "plink -ssh vagrant@"""+ str(worker1.get_ip())+ """ -pw vagrant ls -lah" """)
from ipaddress import ip_network, IPv4Network
from math import log2, ceil
from .. import defaults
import concurrent.futures
import traceback
import time
class Network:
def __enter__(self):
#ttysetattr etc goes here before opening and returning the file object
self.start()
return self
def __exit__(self, type, value, traceback):
#Exception handling here
self.stop()
def __init__(self, ip_range: str or IPv4Network, local=False):
self.ip_range = str(ip_range)
self._available_subnets = [ip_network(ip_range) if type(ip_range) is str else ip_range, ]
self._subnets = set()
self._nodes = []
self.is_local = local
self.loaded = False
self.started = False
def add_node(self, node):
self._nodes.append(node)
node.add_network(self)
def add_subnet(self, subnet):
self._subnets.add(subnet)
def create_subnet(self, subnet_name, *args, subnet_type: type = None, **kwargs):
if subnet_type is None:
subnet_type = defaults.subnet_class
return subnet_type(subnet_name, *args, network=self, **kwargs)
def get_subnet_range(self, size: int):
max_prefix = 32 - ceil(log2(size + 2))
# size = 2^(32-p) - 2
try:
subnet = [net for net in self._available_subnets if net.prefixlen <= max_prefix][0]
except IndexError:
raise Exception('Network address pool is empty')
self._available_subnets.remove(subnet)
if subnet.prefixlen == max_prefix:
return subnet
try:
smaller_subnet = next(subnet.subnets(new_prefix=max_prefix))
except ValueError or StopIteration as e:
raise Exception('Error while splitting net: {}'.format(e))
self._available_subnets.extend(subnet.address_exclude(smaller_subnet))
self._available_subnets.sort(key=lambda n: 32 - n.prefixlen)
return smaller_subnet
def start(self, interactive=False):
if self.loaded:
raise Exception('Network already loaded!')
if not interactive:
for subnet in self._subnets:
subnet.start()
for node in self._nodes:
node.start()
for node in self._nodes:
# node.unlock()
pass
self.started = True
self.loaded = True
else:
try:
self.start(interactive=False)
except:
print('\nException while starting:\n_________________\n\n',
traceback.format_exc(), '\n_________________\n', sep='')
i = ''
while i not in ('y', 'n', 'yes', 'no'):
i = input('Stop? (y/n): ')
if i[0] == 'y':
self.stop()
def load(self, *args):
for node in self._nodes:
node.load(*args)
for subnet in self._subnets:
subnet.load(*args)
def stop(self, *args):
if not self.loaded:
self.load(*args)
for node in self._nodes:
node.stop(*args)
for subnet in self._subnets:
subnet.stop(*args)
def __contains__(self, item):
return item in self._nodes or item in self._subnets
class NetworkConcurrent(Network):
def __init__(self, *args, max_workers=None, **kwargs):
self.max_workers = max_workers
super().__init__(*args, **kwargs)
def start(self, interactive=False):
# TODO: debug & interactive realisation
if self.loaded:
raise Exception('Network already loaded!')
if not interactive:
for subnet in self._subnets:
subnet.start()
with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
for node in self._nodes:
executor.submit(node.start, dc=None)
print('start time:', time.strftime('[%H:%M:%S]', time.gmtime()))
# TODO: executor here too
with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
for node in self._nodes:
executor.submit(node.unlock)
'''
for node in self._nodes:
node.unlock()
'''
self.started = True
self.loaded = True
else:
try:
self.start(interactive=False)
except:
print('\nException while starting:\n_________________\n\n',
traceback.format_exc(), '\n_________________\n', sep='')
i = ''
while i not in ('y', 'n', 'yes', 'no'):
i = input('Stop? (y/n): ')
if i[0] == 'y':
self.stop()
def stop(self):
if not self.loaded:
self.load()
with concurrent.futures.ThreadPoolExecutor(max_workers=self.max_workers) as executor:
for node in self._nodes:
executor.submit(node.stop, dc=None)
for subnet in self._subnets:
subnet.stop()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment