Last active
April 10, 2021 09:39
-
-
Save OlegJakushkin/b6f4d2976b3d22bda222e43d265c529b to your computer and use it in GitHub Desktop.
RemoteSSH SDN via l3ns
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# 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"] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[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 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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" """) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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