Multi-processing or multihreading ? Build your own distributed system to find the answer.

My (gross) latin teacher used to say, stuff always sounds more profound when said in latin.
And then proceeded to close the door of the toilets saying : « fluctuat, nec mergitur » (it barely floats, but is doesn't sink).
Let's talk about distributed systems ? Simply said it is swarm based computing : you have a pubsub as a databus and send commands to lightweitgth probes that answer back on the bus.

Almost always using a network socket as a bus.

On these sockets we send tasks and expect results over the wire. Very often, distributed system are used at the core of ... SRE/measurement system.

And the first think a hard taught real life lesson on distributed system will always teach you is : you know nothing of time.

This old war story has been deprecated thanks to the work of the python devs on the PEP418. But, there used to be a time, your carefully timestamped task made in python would come back executed in the past. Leading to a mysterious bug in the invoices of customer were a non expensive land line call going through a SIP gateway would cost thousandths instead of cents.

The first thing you do when you code a distributed system is to make it measure time, and its own reaction in face of congestion.

You see, you : the distributed system guy you are probably being PAID so people can observe when incident in production OCCURES, which often is when part or all system is saturated/congested.

So, first think you do when building a distributed system is to be the hammer that will burn the CPU and measure that it behaves well. Basically, the torture test is the test itself.

Copying from the zmq book

Here I found a very minimalistic framework so I adapted the REQ/ROUTEUR model to the code in annexe.
As you can see the code is both Multi threading, multi processing. Hence, you can make a nice trick : measuring the impact of the measure of time in fonction of multi processing/multi threading.

It's pretty meta, but it yields results : Lauching workers 128 by 128 on a 4 core intel pentium in multi processing on linux gives this result :
TIME IS LINEAR WITH TIME. You don't know what happens on the system, which fault it is (your code saturing the cores for instance) but you want time to stay monotonicly shaped and looking like x=f(x).

However, with multi threading .........

Clearly times measured by the system is less desirable.

But, look, I have virtual machine with freebsd 14.0 coming from another post... why not try with freeBSD too ? (hence varying the « nested virtualisation, and OS parameters)

Well, I don't know why :
  • freebsd time() call return time within a second resolution with multi processing
  • the curve for time = f(time) is clearly dilatating faster on multithreading also on freeBSD in a non nice smooth way.
You see, this is where quarrels about multi threading and processing should stop : when you can observe that is matters (or not).

And, all of this is in a very peculiar context of using pyzmq, python and linux or freebsd, not mac or windows. Actually, the API for both of those are so compatible that python gives us the luxury to not care about the decision of choosing either threading or processing. We can always change our mind... And it's cool.

Annexe I

La colère du daron : « la voie du lâche, c'est parfois le coup de poing. »

Lao Tseu a dit, si ça pue la merde, évites le trouble, car le bon combattant c'est avant tout celui qui survit un autre jour.


De mes années en banlieue j'ai gardé un solide sens de l'esquive multi-dimensionnelle. Avec comme doctrine principal : la fuite c'est le courage de survivre un autre jour.

Me traiter de veule et de roublard en le domaine me conviendrait assez, et j'en fais même une certaine fierté : j'ai toutes mes dents, contrairement aux idiots de bravaches.

J'ai même eu arrangé par la connexion du petit portugal où je vivais, l'alliance avec le little portugal des brouillards qui a permi d'étoffer les effectifs de 4 pelés 2 tondus du quartier contre les 12 des louvrais à un effectif de 50.

Arrangeant une fuite par les voies diplomatiques confortables. J'étais le pire des veules, recourrant à la ruse du haut de mes douzes piges que vous donne la confiance d'être bête.

Bref, la fuite : c'est l'attaque, tu es comme muhammed ali, tu penses à esquiver avant de penser à frapper. Avec dextérité et brio.

Normalement, t'es le roi de l'esquive, t'évites même de te battre.

Puis un jour, t'as 20 kg sur les épaules. Ça diminue ta capacité à esquiver la confrontation, physiquement.

Un scooter à 6m/s sur le trottoir, à 10m de là, ça te laisse peu de temps pour esquiver, quand t'as en plus ta fille sur les épaules. Ta fille quoi. Ça réduit ta marge physique de fuite, mais aussi ton niveau de tolérance à la violence potentielle physique qu'on pourrait faire à ta fille.
Puis là, c'est comme un animé, le temps se découpe lentement, on pense, on pense à esquiver en premier.

On fait un pas chassé à la vitesse la plus lente possible pour éviter le moto-connard. Ça prend un poil plus de temps avec 20 kg sur les épaules. Donc on a le temps à une deuxième accélération de temps où l'on pense à la suite.

Éviter le trouble, et le 2é truc qui poppe.

On prend la voie du couard, car C'EST LA BONNE.

Puis, le temps s'accélère encore, on voit le mec sourire en nous voyant esquiver, et ne pas ralentir.

Et là on se rend compte que si il accellère, c'est trop tard si on veut éviter la collision, et que c'est pas certain qu'il accellère pas, et qu'il trouve ça drôle.

Et toi tu vois blanc...

Et en même temps que tu te mets à sourire pour capter son attention, tu lèves ton bras contre ton corps comme un bon def de basket à la hauteur de ton épaule, et donc, de son casque.

Et boum, comme dans un animé où le temps s'est encore dilaté (cpt tsubasa style), en même temps que tu réalises que tu viens d'intentionnellement plaquer ta main contre son casque tu as tout un tas de pensées qui te traversent le cerveau.

La première est, PUTAIN ÇA FAIT DU BIEN !

La deuxième est PUTAIN, ÇA VA ME FAIRE MAL ET DU KINÉ À PRÉVOIR.

Et là, ça repart : le casque qui tire les muscles de l'épaule qui aime pas trop,

Le coup de boule en arrière du conducteur sur sa passagère heureusement casquée,

la chûte évitée de soi ... du scooter aussi ...

L'adrénaline qui pompe violemment à t'en faire vomir.

Et le flash forward sur les 3 mois de kinés avec une douleur qui me tance toujours dans l'épaule à cet endroit les mauvais jours.

Comme pour me rappeler, le prix à payer à la couardise et pourquoi on évites le trouble, et pourquoi quand on l'évites pas, on est prêt à payer le prix d'être lâche. Et ouais, cette douleure me relance et me tance en même temps qu'elle a un goût deplaisir coupable de la mémoire d'avoir enfreint ma règle du couard avant tout dans un beau coup de paume dans la visière d'un casque brillamment executé comme une frappe de bruce lee.



C'est la colère du daron, la colère blanche du lâche qui acculé qui ne peut plus esquiver car maintenant il doit aussi intégrer les siens dans sa bulle de sécurité.

The « asynchronuous » pubsub library « moulé à l'ancienne »

Asyncronuous : controlling the timing of operations by the use of pulses sent when the previous operation is completed rather than at regular intervals.
So it follows how to do a pubsub server in bash in less than 200 lines of code, and deploy it the overengineered way in freeBSD thinjail running on qemu in linux (for fun). I remember my Perl days when using system or the more modern Popen and procedural programming were not tabooed.

So, in nostalgia let met develop a multithreaded or multiprocessing library according to your taste. I'am really not religious on the topic, because both have the same API.

First thing to say before specialists of « orthodoxy » in python style pukes : I don't care about PEP8 and « ONE BEST WAYS ». I tend to avoid parts of python that are poorly documented like socket, hence I prefer to shamelessly « popen » a socat that transforms socket calls in processing stdin/stdout. And no, I don't say if a command succeeded because it's a « good enough » API.

Basically the API is made of 3 components :
  • pubsub that connects and authentifies
  • collect that collects all the messages (and eventually non important messages starting with "#" according to configuration) you subscribed to
  • send that sends the raw « pubsub » commands which are basically : sub channel to subscribe, pub channel content to publish on a channel
It is asynchronuous code using the timing of operation with Event (a kind of reliable signal for multiprocessing/multithreading code on python), therefore instead of putting psuedo code, I added the events relating the pseudo code...


And since I put a sample callback that mess up stdout by printing anything not on general here is a sample of how it's messy invocation looks like :
$python3  -i ./pubsub.py 
2024/05/24 16:17:29 socat[39509] W OpenSSL: Warning: this implementation does not check CRLs
>>> collect()
['# [D]:14:17:33:\x1b[0;90m\x1b[1;47m publishing in general \x1b[0m', '# [D]:14:17:34:\x1b[0;90m\x1b[1;47m subscribing <general>',
'pubsub on general at 14:17:33: new subscriber entering ']
>>> send("pub this nada")
>>> *# [I] creating <this>

>>> send("sub this")
>>> *# [D]:14:17:58: subscribing <this> 
*pubsub on this at 14:17:48: nada 

>>> send("pub this hello")
>>> *pubsub on this at 14:18:11: hello 
collect()
['# [\x1b[33mI\x1b[0m] creating <this>', 
'# [D]:14:17:58:\x1b[0;90m\x1b[1;47m subscribing <this> \x1b[0m',
'pubsub on this at 14:17:48: nada ', 
'pubsub on this at 14:18:11: hello ']
>>> 
Nothing thrilling, but, I am just always amazed at how complexity : simple pieces of less than 100 lines of codes in simple interactions with one another beats complication (huge pieces of code coupled together).

At the end, the library does not even makes 100 lines of code and you can chose your flavour of multihreading or multiprocessing and the code still works the same.

The bullshit of security in mind first before coding.

Today's trolling with code topic is a kick in dee nutz of the « software urbanists security architects from space ».

I had a long beef with these « experts » that basically want to control everything you do while being totally pedantic idiots. They are often Ivy leagues student with a degree in Computer Security that define themselves as white hat.

One of their claim is to be embedded in design decision early to make sure new products are secured. Let's prove this claim is utter bullshit by « over securing » (one way that is a tad trollesque) a clear text protocol which server is in bash.

To wrap up : And today is the grand finale wrapping it up : we secure the service as a second thought.

Define « securing », pliz



In the head of the software architect securing is :
  • confidentiality (making sure evedropping is tough)
  • AAA
    • Authentication : people must be identified to use the service
    • Authorization (also called Role Based Accounting) : you must be able to have distinct users that don't see each others
    • Accounting : login/logout must be logged in a « standard way » that can be consolidated on an external computer
  • containerisation : if your service is broken, you should ensure your whole infrastructure is not compromised
  • it should avoid ressource exhaustion
  • you should provide safe API to the users
Of course, a bash script according to all the « ready made thinking » of this smart asses does not elect to the category of « good enough », and to add insult to injury a Prototype made to work on stdin/stdout.

Thus, now, let's secure it.

SSL tunneling : I love socat bis, terse documentation, but SO ... GOOD



We EXACTLY Follow the certificate generation such as written here : http://www.dest-unreach.org/socat/doc/socat-openssltunnel.html.

It of course REQUIRES you have a DNS with a local zone resolving the A and PTR (reverse operation giving the name for the IP address locally). I have an unbound server already set for adblocking thanks to a ... bash script of my own I can add it a small zone with both hostname that will be THE only required information to have valid self signed certificate based on the common name (common name = host name).

I could argue that authentication per SSL certificate is strong and my job is done. But, it does not handle Authorization and accounting. What would be nice ... Would be ... groups, login/pass and accounting.

Exactly what /bin/login provides for the authentifcation and accounting and what unix groups/home dir provides :D. No new tools required.

All this is condensed in THIS


The client side



socat READLINE OPENSSL-CONNECT:pubsub.home.:2023,cert=./client.pem,cafile=./server.crt
We politely let socat do the job after we set the DNS virtual address address of pubsub.home (the jail) in the local zone.

The server side



Since the jail is setup we just need a simple rc.d script :
#!/bin/sh
. /etc/rc.subr

name=pubsub
rcvar=pubsub_enable
desc="pubsub server"
start_cmd=pubsub_start
stop_cmd=pubsub_stop
: ${pubsub_enable:=no} 

load_rc_config $name
command=start_cmd
pubsub_start() {
        echo "pubsub started."
        su -m serve -c '/usr/local/bin/socat \
        	OPENSSL-LISTEN:2023,cert=/home/serve/server.pem,cafile=/home/serve/client.crt,fork,reuseaddr \
        	EXEC:"/usr/libexec/getty Pc",ctty,pty,stderr' &
}
pubsub_stop() {
        echo "pubsub stopped."
        killall socat
}
echo hello
run_rc_command "$1"
The socat invocation will listen on port 2023 and use the client certificate in conjonction with its key to secure the connection and fork a new pseudo login tty for each connection.

Hence, given that /etc/bash_profile is :
while [ 1 ]; do
	/home/pubsub/pubsub.sh
	exit
done
You add in the jail
sysrc pubsub_enable="YES" 
And restart the jail and the job is finished.

New account are added with user sharing the same group, /home/pubsub path, and umask at the beginning of the script thanks a sticky bit on the group will ensure that rights are without conflict.

And you can create new users sharing different channels, thus having distinct users possible on a mutualized platform.

All login access will be logged in /var/log/auth.log giving us accounting.

It's basically a pseudo telnet protocol over ssl and a pattern to develop fast when manipulating sockets bores you to death, whereas using stdin/stdout is darn simple.

But ???!!!!

Of course, it's half baked, it lacks the rotation of the channel and reeks of ressource exhaustion, but ... if I would be a company I would devote time later to this, I would probably freeze the pubsub with a SIGHUP trap handler and ask them to kill all their files they follow, sleep, rotate the file, and resume following them (which require an additionnal associative array). Else, we have everything by using a layered design where every layer does it's part of the job : TCP does the transport, SSL the ciphering and one authentication, login+unix groups provides AAA (Authentication, Authorization, Accounting) and the jails a container that can be further hardened thanks to pf.

Security, does not need to be a first thought, security engineers give us very nice tools that can be composed at the best level possible which is the Operating System and its base.

I find the devops hype tiring because abstractions are leaky and each level are coupled in insane ways for the sake of not knowing where your money goes and funnel a lot of money out of both their companies and customers, but we actually can work with 300Mb of memory on small instances of mutualized iron. So I think I proved securing a prototype in a decent way as a second thought can be done fairly easily.

But the joke is not over, a protocol is not over ... until you provide a python library for the protocol. And believe me, it holds in 85 lines of code.

Annexe

Making a SIMPLE bridge is so COMPLICATED under linux.

Aristotle : only the idiots make stuff that are complex look simple.
Feynman : Science is the belief in the ignorance of the expert making anything look to complex for you.
Software defined network is begnining with virtual ip, ethernet bonding, software bridge, software vlan, tap interface, ip tunnel. You don't actually plug HARDWARE, hence it's software defined.

As funny as it can be, most of these hardware are full of the same software code we run on our computer. BSD being the most borrowed TCP stack used in network appliance, linux coming second, but other OSes are used (QNX for instance). In a way, all networks are actually software defined. But it is classier to say you do « software defined networks » than to say I am a « network techie ».

So, so, so....

Yesterday, I managed to make sense of And I must say, I was kind of pissed.

You see, I began linux with the linux bible HOWTOs printed on paper when internet was sparse and it was helpful.

HOWTOs differ from doc in that they do not give you the solution, they also help you improve your understanding.

So before you can give a network in which your host and guest can talk, you need to understand what you do :D




The bridge is like a device to route to and from to LAN and use the magic of STP (Spanning Tree Protocol) to decide if a packet goes in a direction or the other.

TUN/TAP devices are like ethernet cards for user space code. Enabling code to have a full fledge device driver looking like an ethernet card that is all made of software. You cannot be more into «software defined network» than this.

Making the script was straightforward to connect all of this ...

One to setup :
$cat qemu.net.sh 
#!/usr/bin/env bash
TAP=${TAP:-tap0}
BRIDGE=${BRIDGE:-br0}
HOST_ETH=${HOST_ETH:-eno1}
USER=${USER:-jul}

#create tuntap device accessible to the user for the guest
ip tuntap add dev $TAP mode tap user $USER
ip link set $TAP up
# create bridge

ip link add $BRIDGE type bridge 
# plug host and guest on the bridge
ip link set dev $HOST_ETH master $BRIDGE
ip link set dev $TAP master $BRIDGE
ip link set $BRIDGE up
sleep 1
# get an ip for the bridge
dhclient $BRIDGE
sleep 1
# re-get an IP for the host
dhclient $HOST_ETH
one to cleanup
$cat qemu.net.cleanup.sh 
#!/usr/bin/env bash

TAP=${TAP:-tap0}
BRIDGE=${BRIDGE:-br0}
HOST_ETH=${HOST_ETH:-eno1}
# turn off bridge
ip  link set $BRIDGE down
# unplug  guest 
ip  link set $TAP down
# remove bridge
ip link delete $BRIDGE
# delete tun/tap
ip link delete $TAP
ip tuntap del dev tap mod tap
sleep 1
# re-get an IP for the host assuming you use dhcp
dhclient $HOST_ETH

Notice all the variables... br0, tap0 are used in so many example that they may already have been used by ... docker, kvm, or any other virtualization solution. To avoid conflict in this case I used variables that can be set with environment variable. eno1 shoudl be replaced with YOUR ethernet interface on YOUR linux ... Name of the interfaces varies according to version, direction of the wind and systemd humor. Gone are the good old days when eth0 was univoquely the name of your ethernet card when you had only one.

The qemu startup script will thus look like this:
#!/usr/bin/env bash
TAP=${TAP:-tap0}
qemu-system-x86_64 -m 3g -smp 2 -nographic \
	/home/jul/src/fbsd/FreeBSD-14.1-BETA3-amd64.qcow2 \
    -net nic -net tap,ifname=$TAP,script=no,downscript=no
The two scripts requires root to run them. But funnily enough, to work ... I discovered by trial and error that I needed to shut down my ethernet host interface in network manager, else you have two network interfaces trying to fight for the same IP network.

Qemu freebsd's equivalent, behyve have the nicety to put it in the Freebsd's handbook. The place where the motd redirects you at login.

I spared you the kvm pages on the topic that uses the deprecated brctl interface to do the same, while technically kvm is just a layer of shit above qemu, and above which openstack is another layer of poo, because my problem was understanding why network manager conflicts with the script. Long story short, I admit my defeat. I don't know why I must disable my ethernet interface in network manager. It makes sense, somehow. That I have to redefine my network connection « à l'ancienne » in /etc/network/interfaces. Which is bothering when you have wifi.

If you have (as I do) a local custom DNS server with black holer as a forwarder, you must not forget to tell dhclient TO NOT override your DNS.

In normal distros edit /etc/dhclient.conf (/etc/dhcp/dhclient.conf on debian based distro like ubuntu or linux mint) and add
prepend domain-name-servers 127.0.0.1;

On freebsd you must do the same and also disable this wonderful piece of software coming from linux's world : resolvconf by doing :
sysrc resolv_enable="NO"


What I learned today is the shift of normality in documentation. When linux began and blog posts were not simple « cookbooks » for one problem, we had « howtos » that were not only giving actual example but also knowledge. Nowadays, commands are thrown at your face to deal with it, as if it was trivial. It is not.

My latest series of posts are looking like cookbooks, but actually, they are just me having fun and not pretending to do anything more than a log of having fun reinventing the wheel with less lines of code. The networl part is my least favourite and the command lines are not self explanatory.

Anyway, next post, now that we have pubsub running in a jail with a way to setup the network nicely is adding TLS and login/pass like a troll ! And then making a python library for the protocol (actually it is finished).

Pubsub bash on a freebsd 14.1-BETA 3 running on linux (because we can)

People takes for profound thougths which only quality is the obscurity of their redaction is obscure complication.
Complication is a big spaghetty code, complexity is a lot of small code interacting like a nicely dressed lasagna.

-- Fridriech Ecthebest Nietzsche in Howto to code with a hammer (1880)


So in the last episode of last mad coder designs a pubsub protocol and implements it in bash, we experienced how easy doing a « it works for me »©® prototype in bash was thanks to socat/netcat/nc.

I had numerous feedback (it's a lie) from software urbanist architect of security claiming a prototype doeth not exists until there is a clear path of secured deployment in a container, on a container with cryptography and strong authentication.

So, today, I will illustrate -the corporately wrong, funny BUT totally legitimate- way to deploy it and secure it in the lazy lasagna devops way. And since, freebsd 14.1-BETA3 has been released, we have qemu and and jails on our plate.

The plan is the following :
  • Installing a customized freeBSD on linux in less than 15 minutes
  • Connecting it with a modern linux as a host
  • deploy a BSD thin jails the root way in a container (which makes as much sense as deploying docker in an openstack/kubernetes infrastructure except you need only one computer to experiment)


Installing freeBSD 14.1 Beta3 on linux with bash



Well, long story short : every release of freeBSD comes with a qcow2 image usable by qemu, and I have a script that does tailor a minimal image of freeBSD for my need including:
  • installing what I need to code (python3, git, bash)
  • customizing the keyboard to french
  • booting a non graphical comsole to be able to have « natural cut'n paste » between host and guest for a « unique improved CLI experience »,
  • and adding 1.5Gb to the root partition
The script (in bash) is fairly easy to customize, and you will need to do so if your keyboard is not french. It just asks at the beginning the default password for the user «user» and «root», doas is installed and configured as a passwordless sudo for user. Ports are not installed because it takes way to much time to call this experience fun anymore (plus ports architecture has been migrated and are now done a better way my script would not use).
Once the script finishes it boots directly into a consoled freeBSD 14.1-beta3 that can be left by login as root and typing
poweroff
.

The freebsd can be rebooted fast by using the generated ./start_14.1_BETA3.sh script. But right now, we need an openzfs partition to be able to make thin jails.

As a foreword, qemu docs are not very usable, even me (running the family comfy linumint that is debian based) uses the best linux documentation in town : the archlinux documentation. Whatever the linux I must admin, that is always my first source of information before reading the upstream docs/man pages because they redact their documentations in a readable way. Kudo archlinux !

Configuring a zfs based thinjail with freeBSD



So, following archlinux doc we make a 2Gb additionnal space for the zfs partition before having the fun of playing with freeBSD's gpart.
 qemu-img resize FreeBSD-14.1-BETA3-amd64.qcow2 +2G

Time to login as root on freeBSD by running ./start_14.1_BETA3.sh.

The violent drive resizing needs some repairing that are fixed with :
fsck -y
gpart recover ada0
[root@freebsd-14 ~]# gpart show
=>      34  19915958  ada0  GPT  (9.5G)
        34       122     1  freebsd-boot  (61K)
       156     66584     2  efi  (33M)
     66740   2097152     3  freebsd-swap  (1.0G)
   2163892  13557796     4  freebsd-ufs  (6.5G)
  15721688   4194304        - free -  (2.0G)

Now is time to add a zfs partition in the 5th «p»artiton
gpart add -t freebsd-zfs -a 4k ada0
gpart show
=>      34  19915958  ada0  GPT  (9.5G)
        34       122     1  freebsd-boot  (61K)
       156     66584     2  efi  (33M)
     66740   2097152     3  freebsd-swap  (1.0G)
   2163892  13557796     4  freebsd-ufs  (6.5G)
  15721688   4194304     5  freebsd-zfs  (2.0G)

Good, we reboot in single user mode, and repair the butechring we made with
fsck -f -y
** /dev/gpt/rootfs
** Last Mounted on /
** Root file system
** Phase 1 - Check Blocks and Sizes
** Phase 2 - Check Pathnames

UPDATE FILESYSTEM TO TRACK DIRECTORY DEPTH? yes

** Phase 3 - Check Connectivity
** Phase 4 - Check Reference Counts
** Phase 5 - Check Cyl groups
52073 files, 1058127 used, 581220 free (68 frags, 72644 blocks, 0.0% fragmentation)

***** FILE SYSTEM IS CLEAN *****

***** FILE SYSTEM WAS MODIFIED *****
** /dev/gpt/efiesp
** Phase 1 - Read FAT and checking connectivity
** Phase 2 - Checking Directories
** Phase 3 - Checking for Lost Files
Next free cluster in FSInfo block (2) not free
Fix? yes
4 files, 31 MiB free (64236 clusters)
Good, filesystem is clean, we reboot and login as admin to finish setting up zfs
# echo 'zfs_enable="YES"' >> /etc/rc.conf
# service zfs start
ZFS filesystem version: 5
ZFS storage pool version: features support (5000)
zpool create zroot /dev/ada0p5

Back in the track of the awesome FreeBSD handbook on jails we are gonna setup the system for thin jails relying on zfs for snapshoting.. We activate the jails subsystem :
sysrc jail_enable="YES"
sysrc jail_parallel_start="YES"
then create the dir structure with zfs underlying (a nice couche of lasagna).
zfs create -o mountpoint=/usr/local/jails zroot/jails
zfs create zroot/jails/media
zfs create zroot/jails/templates
zfs create zroot/jails/containers
zfs list
NAME                     USED  AVAIL  REFER  MOUNTPOINT
zroot                    261K  1.75G    24K  /zroot
zroot/jails               96K  1.75G    24K  /usr/local/jails
zroot/jails/containers    24K  1.75G    24K  /usr/local/jails/containers
zroot/jails/media         24K  1.75G    24K  /usr/local/jails/media
zroot/jails/templates     24K  1.75G    24K  /usr/local/jails/templates


What I love before using automation, is doing stuff the root way, thus, when you have a problem, you can understand how it fails. Hence the reason we don't use the promising AppJails I will evocate later (really later).

So let's to the zfs snapshot for our base :

[root@freebsd-14 ~]# zfs create -p zroot/jails/templates/14.1-BETA3
[root@freebsd-14 ~]# fetch https://download.freebsd.org/ftp/releases/amd64/amd64/14.1-BETA3/base.txz -o /usr/local/jails/media/14.1-BETA3-base.txz
/usr/local/jails/media/14.1-BETA3-base.txz             197 MB 1078 kBps 03m08s
[root@freebsd-14 ~]# tar -xf /usr/local/jails/media/14.1-BETA3-base.txz -C /usr/local/jails/templates/14.1-BETA3 --unlink
[root@freebsd-14 ~]# cp /etc/resolv.conf /usr/local/jails/templates/14.1-BETA3/etc/resolv.conf
[root@freebsd-14 ~]# freebsd-update -b /usr/local/jails/templates/14.1-BETA3 fetch install
src component not installed, skipped
Looking up update.FreeBSD.org mirrors... 3 mirrors found.
Fetching public key from update2.freebsd.org... done.
Fetching metadata signature for 14.1-BETA3 from update2.freebsd.org... done.
Fetching metadata index... done.
Fetching 1 metadata files... done.
Inspecting system... done.
Preparing to download files... done.

No updates needed to update system to 14.1-BETA3-p0.

WARNING: FreeBSD 14.1-BETA3 is approaching its End-of-Life date.
It is strongly recommended that you upgrade to a newer
release within the next 1 week.
No updates are available to install.
[root@freebsd-14 ~]# zfs snapshot zroot/jails/templates/14.1-BETA3@base
[root@freebsd-14 ~]# zfs clone zroot/jails/templates/14.1-BETA3@base zroot/jails/containers/pubsub
(reverse-i-search)`res': cp /etc/resolv.conf /usr/local/jails/templates/14.1-BETA3/etc/^Csolv.conf
[root@freebsd-14 ~]# /etc/rc.d/jail restart
Stopping jails:.
Starting jails: pubsub.
We install the dependencies :
jexec pubsub pkg install socat
jexec pubsub pkg install bash
jexec pubsub pkg install perl5
jexec pubsub pkg install ucspi-tcp
Here is the content of /etc/jail.conf
pubsub {
  # STARTUP/LOGGING
  exec.start = "/bin/sh /etc/rc";
  exec.stop = "/bin/sh /etc/rc.shutdown";
  exec.consolelog = "/var/log/jail_console_${name}.log";

  # PERMISSIONS
  allow.raw_sockets;
  exec.clean;
  mount.devfs;

  # HOSTNAME/PATH
  host.hostname = "${name}";
  path = "/usr/local/jails/containers/${name}";

  # NETWORK
  ip4 = inherit;
  interface = em0;
}

A basic jail ... with bash ... using the latest BETA that soon will be obsolete :D

We add a user pubsub (regular with login/pass:
jexec pubsub pkg install perl
And download the script, and test it works !
 curl https://gist.githubusercontent.com/jul/e9dcfdfb2490f6df3930dfe8ee29ead1/raw/1ddd6411c1ae8894f506fb4fcb0db0f2af6f45bb/pubsub.sh > /usr/local/jails/containers/pubsub/home/pubsub/pubsub.sh
chmod +x /usr/local/jails/containers/pubsub/home/pubsub/pubsub.sh
jexec pubsub pkg install perl
Nice. Now, is the time for PERVERSION !

Gluing stuff together to make an « production ready prototype complying with all corporate bells and whistles »



What we need ?
  • to avoid traffic being evesdropped (SSL)
  • modular authentication/authorization and login
but before this ... we need our qemu instance to get clean communication with the linux host and play the « software defined tricks ».
As soon as you don't have a material interface plugged in hardware, it is software defined networking and we will begin with qemu manual...
We choose the simplest form of connection bridging qemu to the host with a tap interface. First we create the network interface in linux (fix your dhcp leases, and let your firewall talk to both ips). Addedum And configuring a darn simple bridge because of modern linux and its culture of not documenting properly is to be updated as a story in its whole.

Right now, we ALMOST have readied the playground to add « a true secure design ISO 27000 paperwork compliant with pieces of ITIL and ISO 14000 death by one thousand cut :D

We just need a WORKING BRIDGE that works the intended way .... and is making me regret the simplicity of slackware (my first distro). But I don't have the juice to finish today : I hate networking. So, the next episode comes next : DNS, TLS1.3, RBAC with pam hell \o/ and a python library to use the protocol as a noob that codes in python.

Socat, netcat, nc, tcpserver and open source "moulé sous les aisselles".

In my last post, I explored Laurent Bercot's premisce that for making a piped based server requiring a script to only know how to talk on stin/stdout tcpserver we don't need no systemd.

I experimented with tcpserver, and it worked fine.

I also « stofled » (went my way trhough stack overflow's answers) the topic and re-discovered nc, netcat, socat possible alternatives to tcpserver.

Well, as a first, all these tools are great. But, one conquered my heart : socat.

First, all honests persons should state their bias : I am a linux user since 1993 (slackware, debian) and since systemd have got some freeBSD, openBSD, and devuan. I long for systems that are idiot friendly. A special kind : the one that are not afraid to read the documentation and go on the web page of the software to get the upstrem documentation.

For famlily reasons, my main battle-station is a 12 yo core i3 with linux mint so to say debian, hence software are rarely conforming with upstream vanilla since debian political commisars have got weired ideas about how to package software.

But first, before this long digression about a certain idea of free software, let's remind the core of the problem :

Making a shell script act as a server by reading from stdin, writing to stdout and having a magical stuff transforming the shebang in a multi-connection server.

An echo server would probably look like :
while [ 1 ]; do
    read a
    echo $a
    echo $( export ) # maybe the server can see our IP address in the environment variables set ?
done
And with telnet if you connect on the port this server listen to everything you write is repeated.

Let's review the « There Is More Than One Way To Do It » (Perl's (in)famous TIMTOWTDI vs python « one best way » state of mind).
 tcpserver 127.0.0.1 1234 ./echo.sh 
https://cr.yp.to/ucspi-tcp/tcpserver.html . A tool smelling of BSD's spirit of doing one thing and only one thing well.
Netcat's way. Well. They are 3 netcats !
  • The root of all nc «hobbit» netcat, that is not available anymore ;
  • the openBSD netcat fork (also called nc) providing more protocols ;
  • the NMAP fork providing the -e option that I need (also called ncat) ;
As a result you can only do the same as tcpserver with NMAP's netcat (aka ncat) :
ncat -nlvp 1234 -e echo.sh 
Where l means listen, p port, n stand for numeric dotted quad IP address, v is verbose, e stands for execve. Trust me explainshell.com does an awesome job at explaining command line arguments in a readable way better than me.

And then, there is socat's way :
socat        TCP4-LISTEN:1234,bind=127.0.0.1,fork,reuseaddr  EXEC:"./echo.sh" 
All of these servers are accessed by doing :
  • telnet localhost 1234
  • nc localhost 1234
  • /usr/local/bin/socat READLINE TCP:localhost:123
  • and in the previous post you can also roll your own client to have history with tcpclient


So far, all these tools DO what I want, and they all transmit the IP/PORT of the client with the same convention (TOOLS)_PEERADDR, (TOOLS)_PEERPORT where tools can be SOCAT, NCAT, TCPSERVER (check yourself).

So, all these tools are equals, benchmarking over, choosing the right tool is a question of taste and religion. Over.

Or is it ? ... my religion


I may love concision, but concision sometimes does not help memory. Socat raises above all else by being user friendly but not idiot friendly.

Socat has an awesome PATTERN :
 socat [opt] SOURCE_FAMILY:args,opts DEST_FAMILY:args,opts
This make myself hate this tool for being overly verbose, but loving it for saving my precious memory muscle. For instance, READLINE is a family of source/dest that is a wrapper around stdin/stdout to provide history and line editing out of the box, which is AWESOME when you test a server repetively.

Since DEBIAN, is still so special by not providing upstream packages in their vanilla flavour, when you try READLINE source with debian based distro you will have this cryptic error :
$socat    READLINE   TCP:localhost:1234
2024/05/16 10:14:10 socat[5947] E unknown device/address "READLINE"
Requiring you to visit the upstream provider of socat and enjoy a free software « moulé sous les aisselles à l'ancienne ».

I mean, no git, no reactive website, nothing fancy, first the changelog, a tarball with checksums you can download and a man page with examples.

Ah ! It reminds me so much of my early days on linux. And a golden nugget hidden in the not that obvious link to the repos.
Curated examples commented by the author (you also have in the man page)

I think it even beats openBSD amish's style by being feature rich and consistent.

So, because I WANTED my socat with READLINE I had to compile it, and it was a delight a ./configure for portability, and even though linux may not be the primary target I had very few warnings, few of them being scary.

Oh, and there is a test suite in bash (that I red of course) and it ... was nice to check the compiled software against the expectation of the author. I have a bug in the interaction between READLINE and openSSL.

And you see also an hidden nugget of a broker + fanin/fanout network pattern example in shell that makes me question my usage of ZMQ (I basically use ZMQ only for this).

At this level, I think my will for benchmarking went out of the technical path to become much more fandom.

I mean, it even has CHROOTING out of the box, pf integration, (very) basic ingres/outgres control (CDB), SSL tunneling, cork screwing (very specific options to pass ill configured firewall with UDP), STUN like features (another way of firewall piercing) explained in a concise but funny way.

It's like christmas before christmas, with nice gift falling from the sky with mnemonics strengthening my sysadmin usage of sockets (nodelay, lingering, addresse reuse and other options that are coined as everyone everywhere else).

socat is my new favourite tool because it has a learning curve that totally worths it. It is fitting my brain topology nicely, maybe not yours.

/me <3 gerhard at dest-unreach.org

Is systemd bloated ? A pubsub server in 200 lines of code in bash talking on stdout/stdin that does not require systemd

Two days ago I was reading this about a « new vendor locking » made in lehnart pottering (systemd) and it gave me the will to see how much the assertion of this Laurent Bercot was true.
The new vendor locking is about an old technique used in inetd.
It took me one day to decide what to code, and I began today using uscpi toolsuite.

To be honest, I am a little bit of a fanboy of DJ Bernstein. He is from the school of keep it simple stupid and ROCK HARD idiot proof. And ... I am an idiot of my kind. I just want to do server logic by talking ot stdin/stdout and I am pretty glad to see there exists a correct tool to connect stdion/stdout to a TCP socket.

I decided to add injury to the team of the « I need the latest shibazam* team » and to code in bash.

* shibazam being any of « memory safe », « cryptographically proven », « portable » ...

Doing a pubsub server in bash



a publish/subscribe server is a very simple concept : you have channels on which you write and concurrent readers who can read. It was first used for tickers of stock exchange.

A channel can be bijected to a file, and as long as you handle concurrent writing with an exclusive lock, nothing wrong can happen.

A TCP server is basically a REP/REQ Pattern, while true, you listen to what the user says and you answer and come back to listening.

The perfect job for a DISPATCH TABLE I use to bashize my makefile, hence, the code for which I could basically steal my own code.

I don't know what to say, because it actually worked as a skeleton (saying what it would do but actually doing nothing) below 2 hours flat after the beginning of the coding so I dare say using bash was neat and fun.

Sometimes, I have the feeling we over-engineer our solutions.

Securing the server ?



DJB gives some nice input out of the box with the possibility to add ingres and outgres network rules in a portable way (CDB). And, if you are (rightly) paranoid, and want to deploy it on the internet, I strongly advise to use the beauty of TCP/IP tunneling. Stunnel will add both private key authentication and ciphering without having to roll your own cryptography by tunnelling your clear text protocol in an SSL ciphered IP socket.

I really have the feeling modern days is about over-complexifying design to make money and justify our bullshit jobs. I know by having tried that this kind of solutions don't make it to production line because « it looks to simple and don't give warranties of being sure enough ».

Annexe

The code with a tad of explanation on how to use it in the "?)" case that took me 6 hours to code, SLOWLY. VERY SLOWLY.

Percolons ! Les simulations numériques à l'aide du monde réel en 200 lignes de code.

Je suis sûr que vous ne vous êtes jamais demander combien on pouvait débrancher sauvagement de routeur sur internet avant que les paquets n'arrivent plus à être routé ? Ou, quelle est la taille optimale de mouture (la taille à laquelle on moud le café) pour retirer le maximum d'arôme dans une cafetière à piston sans se faire exploser la cafetière (au propre comme au figuré).

Cette classe de problème est la percolation.

C'est un domaine dans lequel les solutions mathématiques exactes sont dures à calculer et pour lequel faire tourner des simulations aide. On va regarder ici une petite simulation de mon crû et aborder dans la foulée : Laué, les réseaux de Boltzman, Monté Carlo, et Galton.

Au début, venait un problème simple : cette impression que coder est déconnecté de la réalité, alors j'ai eu envie de détourner le summum de la branlette informatique en truc utile : le jeu de la vie.

J'ai un module python pour faire joujou (gof) et quelques gists dont un jeu de la vie en réseau héxagonal.

Pourquoi un réseau héxagonal et pas carré ?



L'intérêt d'un réseau hexagonal est qu'il est géométriquement plus régulier et moins déformé qu'un réseau carré utilisé pour le jeu de la vie. Selon Laué (mathématicien célèbre en cristallographie pour avoir étudier les impacts de la symétrie des réseaux sur les causalités) plus on a de groupes de symmétrie, mieux c'est.

Un réseau carré c'est L2, L4. Un réseau héxagonal c'est symmétrie d'ordre 2 (miroir), 3 (tiers de tours) et 6. Plus on a de symétries, moins on « diverge » du monde réel en introduisant du moirage.



l'Automate à état



Pour notre simulation on va introduire un automate à état simple dont le pseudo fonctionnement est :
Pour tout x de droite à gauche:
    pour tout y de haut en bas:
        Suis je vide ?
            au dessus de moi (2 choix) est-ce plein ?
                si oui prendre au hasard le contenu de la cellule en haut et le mettre dans la mienne
C'est encore plus simple que le jeu de la vie.

Une fois qu'on a fait ça, on utilise python-tk et on affiche des pseudos particules qu'on injecte en haut et on regarde comment elle tombe.

Si la pseudo-physique est bien faite, qu'on plante des clous virtuels à la sortie d'un flux de particule et qu'on les collecte « en bas » dans des « bins » (littérallement les bacs physiques de l'expérience de Galton avant d'être un terme de physique statistique quand on fait des histogrammes) ALORS je dois avoir une belle gaussienne qui se dessine.

Ceci est traité dans cette vidéo :


Résultat 1 : physique bolchévique et monté carlo



J'espère que vous me pardonnerez de planter aussi mal mes clous virtuels que réels, mais regardons LE premier résultat que j'ai en comparaison.


Le résultat est sans appel je suis : CRYPTO BOLCHÉVIQUE. Je code des abstractions dont les gaussiennes penchent à gauche ! Malheur de moi, j'ai la DGSI qui va débarquer si je ne deviens pas centriste républicain.

Avant de corriger : comprenons la géométrie du canvas : les x sont croissant de gauche à droite, les y croissants de haut en bas.

Ma simulation est normalement biaisées haut/bas, mais en scannant séquentiellement de droite à gauche, je favorise la chûte à gauche.

En simulation physique ce problème est connu dans les laboratoires, c'est la raison d'être de la méthode dite de Monté Carlo. On randomise pour casser les ordres qui n'existent pas.

Physique corrigée

Vu que python
random.randrange
est asbolument pas conforme à l'API de range, recodons là de manière saine
def randrange(x):
    c = list(range(x))
    shuffle(c)
    return c
Et remplaçons le
for i in range(x)
par
for i in randrange(i)
soit un mélange faisant disparaître l'anisotropie droite-gauche qui n'existe pas dans le monde réel et faisons retourner la simulation.
Hey, en méthodologie « Good enough », on peut s'arrêter là. Oki, la gaussienne est un peu ... trop phallique à mon goût, c'est mon coté Jul in Shape qui fait ses gainages à la boxe pour rester un adonis éternel.





PERCOLONS



Enfin, on peut s'intéresser à la mouture du café et internet.

Certains problèmes physique sont BIEN CHIANTS à calculer donc, il faut
  1. pouvoir s'épargner les calculs
  2. trouver les moyens de vérifier les calculs simplement
Et c'est là qu'on simule. Cette simulation imparfaite va nous permettre de commencer à PERCOLER.

Percoler pour un fluide, c'est passer à travers un réseau d'obstacle aléatoire. Comme par exemple de l'eau à travers des grains de café dans une cafetière à piston.

Si vos grains sont moutus, moulus? moudus? bref passés trop fin au moulin, ça vous explose à la yeule. Si vos grains sont trop gros, l'eau passe sans extraire le café. L'art de déterminer les bonnes tailles de grains et la bonne vitesse/pression d'eau est donc l'art de percoler. C'est pour ça qu'une machine à café s'appelle un percolateur.

Mais, ça touche aussi la conception d'internet.

Imaginez un réseau hexagonal de routeurs qui passe aléatoirement les paquets de droite à gauche, mais déterministiquement du plus court chemin du haut vers le bas, et cette simulation simulerait un inernet spécial.

Néanmoins, elle permet de bâtir une intuition à des questions comme : à combien de pourcent de nœuds détorioré le réseau peut survivre, quels sont les signes avant-coureur d'une trop forte dégradation (latence, débit, perte de paquets) ? Quelle est la topologie optimale pour fonctionner dans le mode le plus dégradé possible ?

Cette science de la percolation a incité internet à être structuré d'une manière hérétique pour les ingénieurs X/Ponts Télecom c'est à dire en étant peu étanche (très connecté) et partiellement stochastique aux frontières (lire la RFC BGP).

Internet est conçu pour avoir topoliguquement et dans ses choix d'algorithme de routage au frontière la résistance à la dégradation du réseau. On prétend qu'il a été conçu pour survivre à une attaque nucléaire globale.

Voilà à quoi ressemble un « run » de simulation :

Ce qu'il reste à faire (si j'étais pas fainéant)



Si vous êtes pas fainéant, vous faîtes des histogrammes sur des milliers de run. Le plus de simuls le plus l'incertitude diminue (vite) (test de Student).

Pour chaque valeurs de pertubations vous notez : la latence induite, les paquets perdus, la diminution totale ou partielle de flux et vous faîtes des histogrammes.

Normalement, vous allez voir apparaître une valeur de rupture abrupte où statistiquement le réseau passe de quasi 100% de proba de passant à quasi 0% passant qu'on appelle la valeur de percolation. Et pour cette valeur vous allez pondérer les métriques et voir une belle courbe en forme de sigma (une ... sigmoïde) qu'on appelle une courbe de transition. Ça ressemble à une réponse d'un transistor ? Et bien oui, un transistor est une application de la transition abrupte en percolation sauf qu'au lieu que ce soit des grains de matières macroscopiques ce sont des électrons qui sont impliqués, et la plage d'amplification est celle de la transition.

Voilà, des fois quand on fait trop d'info on en a marre de ne plus s'approcher du monde réel, alors une petite simulation physique ça détend.

Annexe : code final et screenshots

200 lignes de codes ! C'est queue de chie.

Why make a makefile? Can reproducible build ever be achieved again?

When I code I like to pride myself in doing code that works everywhere. If unit testing is a good way to go, the fundation of testing a tool chain does work the same everywhere is checking artefacts are the same.

I recently made a code for a sociogram and added at the top of my « make » (a bash script that does all assembling in a suppositly deterministic way) a claim that by using the same input you would have the same output.

Let's see if I lied by taking a snapshot of both the last frame of videos built on 2 different computers :




If the graph is the same, the topology is not. For something about building geometrical shape this may some questions.

First : why can't computer academics build graphviz, but applied physicist do ?

Graphviz is breaking an unspoken standard of academic computer programming : it is based on a probabilistic simulation with a lot of random in it. Basically you first layout the nodes randomly, and randomly you swap to nodes, count the numbers of edges crossing and keep if less edges crossed than before. The kind of dumb algorithm that works but INVOLVES RANDOMNESS.

Well, computer scientific DO reproducible build ! Don't they ? And RANDMONESS is non reproducible, isn't it ?

Hard Scientists (which exclude the litterature lovers called computer scientists) use PRNG when working, so we can reproduce our builds ?




Seems better :D But not perfect once we set the SEED of the random generator in the graphviz output (the only obvioous source of randomness in the code I control).

Let's wonder what kind of non deterministic element I introduced in my build ?

multi-processing/threading is non déterministic


Enough experience in coding will make you smell were danger is. But, I love danger so I knowingly wanted to be a GOOD hard scientist and make my CPU BURN to 100%. It is a stuff I learned to embrace in physics lab. So, I parallized my code (see bash exemple on how to to it with a snippet :
NB_CORE=$(getconf _NPROCESSORS_ONLN)
function pwait() {
    local nb_core=${1:-$NB_CORE}
    while [ $(jobs -p | wc -l) -ge $nb_core ]; do
        sleep 1
    done
}
for i in *dot; do
    $DOT -Tjpg "$i" > $( basename "$i" .dot).jpg &
    ppids+=( "$!" )
    echo -n .
    pwait ;
done
It does the same thing as python multiprocessing async apply : fork process in the background and wait for all the processes to finish before going on. And, it is clear by exploration of both videos that I miss frames, well, ffmpeg (involved in the process is quite explicit :

[swscaler @ 0x5580a8337840] [swscaler @ 0x5580a8375fc0] deprecated pixel format used, make sure you did set range correctly
[swscaler @ 0x5580a8337840] [swscaler @ 0x5580a8b9dd00] deprecated pixel format used, make sure you did set range correctly
So I put a sleep 10, and it helped ... but not enough, because well modern computing on linux is chaotic :
  • signal delivery is unreliable : signals may be missed or can be « acausal », a core can say : I finished while the kernel is still waiting for a file to be written
  • versions of software may differ even on 2 debian based distros (one is debian testing (hence outdated), the other is linuxmint current (less outdated)
So ... I did my possible to have the same result given the same parameters by fixing all I controled including seeds of PRNG to have the same results and suffice to say, that at the end of the day, EVEN FOR A SIMPLE SCRIPT deterministic « same results are impossible ».



It is fucking neither the same topology, NOR chronology. For a dynamic picture of a topology it kind of sucks terribly and should not be considered « good enough ».

It is good enough for a « side project », but not for a production ready project.

Famous last words


When I code in python, I have a freeBSD station, because BSDs are more boring than linuxes. However I can't play my favourite games with wine (Need for speed, Quake III, Urban Terror, various pinballs), hence the reason when I code for fun it's on my linux computers. (check my script to build a tailored freebsd qemu image on linux) but I dare say modern coding is not the « boring » activity I grew up with, hence my manic way of trying to make the most I can to ensure « maximum reproducibility in my power ». My power is about rationality, I give up when it comes to the entfshification of linux distributions that clearly went down the path of not caring, after all, you just need to spin a docker alpine to make stuff reproducible, don't you ?

I'm a single man army that want to code, not maintain a kubernetes cluster just for the sake of creating a « snowflake » of reproducibility that negates the purpose of coding. When I was in 1982 I could give my basic source code on a floppy and I was sure that another C64 would yield the same result given the same input. Nowadays, this is a wishful thinking.

The state of modern computing (especially on linux) is BAD. I even think of reconverting to COBOL on OS/360 to gain back a minimum of sanity back regarding my expectations.

I strongly advice devs to have put more effort on their assembling code (docker build, makefile, your own tool) than their « muscle code » (unit test included), because it's not in the code you control you might find the most surprising factor of loss of control, but in the lack of trust you should reasonably have from your hardware, your OSes and distributions. And it's a shift of normality, a new NORMAL, not an absolute NORMAL state.



Annexe

Here is the command line involved


SHOW=1 EDGE_SCALE=1 MIN_MAIL=20 \
  PERS_EDGE_SCALE=.2 BY_DAYS=50 SHOW=1 \
  THRESHOLD_ILOT=1 DOT="dot"  ./make very_clean movie
the « make » involved. And the main script. It DOES NOT EVEN MAKE 500 lines of code. It is small by any standards, even a « BASIC » program of the 1980s. I AM PISSED ! (╯°Д°)╯彡┻━┻

Addendum : and that's how I discovered the too silent fan stopped working silently without telling a thing in the log or the sensors complaining about heat on my laptop. Even hardware are becoming shitty. Addendum 2 : after some tinkering I discovered I nearly burnt my CPU thanks to debian removing fan control/cpufreqd ... AGAIN (╯°Д°)╯彡┻━┻ Now I must under clock this computer to make it compute anything because. Fuck debian !