start work on templating schemas

This commit is contained in:
Bruno MATEU 2025-02-04 02:06:47 +01:00
parent 90d79eb3a4
commit 8e318d9b92
10 changed files with 678 additions and 1 deletions

3
.gitignore vendored
View file

@ -128,3 +128,6 @@ sympy-plots-for-*.tex/
# WinEdt
*.bak
*.sav
catgrl.tex

View file

@ -2,15 +2,22 @@ TEXFILE=catgrl.org-diagram
#LATEXCMD=latexmk -f -xelatex
LATEXCMD=latexmk -f -pdf
all: netdiag
all: catgrl.pdf
netdiag:
$(LATEXCMD) $(TEXFILE)
catgrl.tex: catgrl-description.yml templates/*
jinja2 templates/catgrl.org-diagram.tex.j2 catgrl-description.yml -o catgrl.tex
catgrl.pdf: catgrl.tex
latexmk -f -pdf catgrl.tex
clean:
latexmk -c
rm -Rf *.log *.aux *~ *.toc *.brf *.bbl *.blg *.ps *.fdb_latexmk \
*.nav *.out *.snm *.vrb *.pag auto *.maf *.mtc *.mtc0 *.fls *.synctex.gz
rm -f catgrl.tex
clean.all: #clean
latexmk -C

203
catgrl-description.yml Normal file
View file

@ -0,0 +1,203 @@
subnets:
- name: britaliope
color: '0b258e'
- name: chapoline
color: '660672'
- name: contabo
color: '724206'
- name: faimaison
color: '1f6604'
- name: vpn
color: 'ad0022'
regions:
- name: Britaliope
color: blue
fai: OVH
position: '[shift=({-17em,-6em})]below-internet'
upstreams:
- host: "hosts1-0"
interface: 0
gateways:
britaliope:
host: "hosts1-0"
interface: 1
hosts1:
- hostname: gw.britaliope.intra.catgrl.org
ips:
- address: 10.17.90.1
subnet: none
- address: 10.90.10.254
subnet: britaliope
- address: 10.90.200.10
subnet: vpn
os: Debian Bookworm
ram: 4GB
disk: 8GB
type: LXC
services:
- name: 'Wireguard'
logo: 'wireguard'
interfaces:
- name: eth0
logo: ethernet
- name: eth1
logo: ethernet
subnet: britaliope
- name: wg0
logo: dragon
subnet: vpn
tag: vpn
hosts2:
- hostname: pmg2.britaliope.intra.catgrl.org
ips:
- address: 10.90.10.6
subnet: britaliope
os: Debian Bookworm
ram: 4GB
disk: 8GB
type: LXC
services:
- name: 'Proxmox\\Mail Gateway'
logo: 'proxmox'
interfaces:
- name: eth0
logo: ethernet
subnet: britaliope
- hostname: mail.britaliope.intra.catgrl.org
ips:
- address: 10.90.10.3
subnet: britaliope
os: Debian Bookworm
ram: 1GB
disk: 8GB
type: LXC
services:
- name: 'Postfix'
logo: 'postfix'
- name: ''
logo: 'Dovecot'
interfaces:
- name: eth0
logo: ethernet
subnet: britaliope
- hostname: ldap.britaliope.intra.catgrl.org
ips:
- address: 10.90.10.103
subnet: britaliope
os: Debian Bookworm
ram: 1GB
disk: 8GB
type: LXC
services:
- name: 'lldap'
tag:
label: Slave
color: role-slave
interfaces:
- name: eth0
logo: ethernet
subnet: britaliope
- hostname: rp.britaliope.intra.catgrl.org
ips:
- address: 10.90.10.1
subnet: britaliope
os: Debian Bookworm
ram: 1GB
disk: 8GB
type: LXC
services:
- name: nginx
logo: nginx
interfaces:
- name: eth0
logo: ethernet
subnet: britaliope
- hostname: pg.britaliope.intra.catgrl.org
ips:
- address: 10.90.10.102
subnet: britaliope
os: Debian Bookworm
ram: 1GB
disk: 8GB
type: LXC
services:
- name: 'PostgreSQL'
logo: postgres
tag:
label: Slave
color: role-slave
interfaces:
- name: eth0
logo: ethernet
- name: Chapoline
mirror: true
color: red
fai: Free
position: '[shift=({17em,-6em})]below-internet'
upstreams:
- host: "hosts1-0"
interface: 0
gateways:
chapoline:
host: "hosts1-0"
interface: 1
hosts1:
- hostname: gw.chapoline.intra.catgrl.org
ips:
- address: 10.255.3.250
subnet: none
- address: 10.90.10.254
subnet: chapoline
- address: 10.90.200.20
subnet: vpn
os: Debian Bookworm
ram: 4GB
disk: 8GB
type: LXC
services:
- name: 'Wireguard'
logo: 'wireguard'
interfaces:
- name: eth0
logo: ethernet
- name: eth1
logo: ethernet
- name: wg0
logo: dragon
subnet: vpn
tag: vpn
hosts2:
- hostname: pmg1.chapoline.intra.catgrl.org
ips:
- address: 10.90.10.6
subnet: chapoline
os: Debian Bookworm
ram: 4GB
disk: 8GB
type: LXC
services:
- name: 'Proxmox\\Mail Gateway'
logo: 'proxmox'
interfaces:
- name: eth0
logo: ethernet
subnet: chapoline
- hostname: mail.chapoline.intra.catgrl.org
ips:
- address: 10.90.10.3
subnet: chapoline
os: Debian Bookworm
ram: 1GB
disk: 8GB
type: LXC
services:
- name: 'Postfix'
logo: 'postfix'
- name: ''
logo: 'Dovecot'
interfaces:
- name: eth0
logo: ethernet
subnet: chapoline

BIN
img/services/haproxy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
img/services/postgres.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

BIN
img/services/proxmox.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

18
shell.nix Normal file
View file

@ -0,0 +1,18 @@
let
nixpkgs = fetchTarball "https://github.com/NixOS/nixpkgs/tarball/nixos-24.05";
pkgs = import nixpkgs { config = {}; overlay = []; };
tex = (pkgs.texlive.combine {
inherit (pkgs.texlive) scheme-small
standalone noto fontaxes enumitem fontawesome5
tcolorbox environ tikzfill;
});
in
pkgs.mkShell {
name = "Ansible catgrl";
packages = with pkgs; [
tex
jinja2-cli
];
LOCALE_ARCHIVE = "${pkgs.glibcLocales}/lib/locale/locale-archive";
}

View file

@ -0,0 +1,300 @@
{% set globals = namespace(mirror = false) %}
{% macro west() %}{{ 'east' if globals.mirror else 'west'}}{% endmacro %}
{% macro east() %}{{ 'west' if globals.mirror else 'east'}}{% endmacro %}
{% macro left() %}{{ 'right' if globals.mirror else 'left'}}{% endmacro %}
{% macro right() %}{{ 'left' if globals.mirror else 'right'}}{% endmacro %}
{% macro m() %}{{ '+' if globals.mirror else '-'}}{% endmacro %}
{% macro p() %}{{ '-' if globals.mirror else '+'}}{% endmacro %}
{% macro mirror() %}{% set globals.mirror = not globals.mirror %}{% endmacro %}
{% raw %}
% Home Network Diagram
% Author: Michael Bugert
% License: CC BY 4.0
\documentclass{standalone}
\usepackage[utf8]{inputenc}
\usepackage[sfdefault]{noto}
\usepackage[T1]{fontenc}
\usepackage{enumitem}
\usepackage[fixed]{fontawesome5}
\usepackage{etoolbox}
\usepackage{tcolorbox}
\usepackage{tikz}
\usepackage[hidelinks]{hyperref}
\tcbuselibrary{skins,raster}
\usetikzlibrary{positioning,matrix,fit,decorations.pathreplacing}
\graphicspath{{img/}}
{% endraw %}
% colors for subnetworks
\definecolor{subnet-none}{HTML}{000000}
{% for subnet in subnets %}
\definecolor{{ "{{subnet-{}}}".format(subnet.name) }}{HTML}{{ "{{{}}}".format(subnet.color)}}
{% endfor %}
\definecolor{docker-bright}{HTML}{0A97C4}
\definecolor{docker-dark}{HTML}{088AB4}
\definecolor{role-master}{HTML}{8e0010}
\definecolor{role-slave}{HTML}{f39c12}
{% raw %}
\tcbset{
skin=enhanced,
boxrule=0.1em,
left=0.25em,
right=0.25em,
% box with hostname and device specs
host description box/.style={
width=25em,
sidebyside,
righthand ratio=0.4,
fonttitle=\bfseries\large,
fontupper=\small,
fontlower=\small
},
% tcbraster for network interfaces
interfaces raster/.style={
raster columns=1,
raster width=6.75em,
% fix raster height to accomodate for fontawesome icon height differences
raster height=4.25em,
raster row skip=0.4em,
raster every box/.style={
fontupper=\ttfamily,
left=0em,
right=0em,
top=.15em,
bottom=.15em,
valign=center
}
},
% tcbraster for host services
services raster/.style={
raster columns=1,
boxrule=0.1em,
raster width=14em,
raster row skip=0.4em,
raster every box/.style={
left=0em,
right=0em,
top=.15em,
bottom=.15em,
fontupper=\small,
boxrule=0.1em
}
},
% box around host services:
% Never drawn, only used as a container. Width is set to the same width as "services". If width is not specified, the box would extend much further towards the right because the default width is \linewidth (uncomment 'blankest' to see it).
services box/.style={
blankest,
width=14em
},
% box around dockerized host services
dockerized box/.style={
title={Dockerized\hfill\includegraphics[height=12pt]{services/docker}},
fonttitle=\bfseries,
colframe=docker-dark,
colback=docker-bright,
left=0.25em,
right=0.25em
},
% box around devices with less details
devices box/.style={
width=20em,
fonttitle=\bfseries\large,
fontupper=\small,
attach boxed title to top left,
boxed title style={frame hidden, opacityback=0.0, left=0em},
colback=white,
coltitle=black!75!white
},
% tcbraster for devices with less details
devices raster/.style={
raster columns=1,
boxrule=0.1em,
raster width=20em,
raster row skip=0.4em,
raster every box/.style={
left=0em,
right=0em,
top=.15em,
bottom=.15em,
fontupper=\small,
boxrule=0.1em,
height=3em,
valign=center
}
}
}
\tikzset{
single icon/.style={
font={\fontsize{34}{38}\selectfont}
},
host description/.style={
},
% box drawn around all bits defining a host using tikz fit
host box/.style={
inner sep=0.5em,
draw=black!75!white,
fill=white,
rounded corners,
line width=0.1em
},
% box drawn around all bits defining a region using tikz fit
region box/.style={
inner sep=2em,
rounded corners=2em,
line width=0.1em,
fill=#1!5!white
},
region descriptor/.style={
outer sep=0,
execute at begin node={\fontsize{7em}{7em}\selectfont\scshape},
color=#1!15!white
},
% tag for marking devices or services as DNS, DHCP, etc. with a customized color
tag/.style={
draw=none,
fill=#1,
rectangle,
rounded corners=2pt,
outer sep=3pt,
font=\bfseries\scriptsize,
text=white
},
% connection types
generic/.style={
solid,
line width=0.2em,
rounded corners
},
ethernet/.style={
generic
},
wifi/.style={
generic,
dashed,
},
vpn/.style={
generic,
decoration={
waves,
radius=0.8em,
segment length=.55em,
angle=30
}
}
}
% commands to show device and service icons at fixed size, etc.
\newcommand{\interfaceImage}[1]{%
\raisebox{-.1\height}{\includegraphics[height=12pt]{#1}}%
}
\newcommand{\deviceImage}[1]{%
\includegraphics[width=5em, height=3em, keepaspectratio]{devices/#1}%
}
\newcommand{\deviceImageInline}[1]{%
\raisebox{-.25\height}{%
\parbox[t]{2em}{%
\centering%
\includegraphics[width=2em, height=2em, keepaspectratio]{devices/#1}%
}%
}%
}
\newcommand{\ip}[2]{%
\textbf{\textcolor{#2}{#1}}
}
% clickable URLs shown without protocol
\newcommand{\https}[1]{%
\href{https://#1}{\nolinkurl{#1}}%
}
\newcommand{\http}[1]{%
\href{http://#1}{\nolinkurl{#1}}%
}
% usage: \service{image}{textual description}{url}
\newcommand{\service}[3]{%
\ifblank{#1}{%
\hspace{2em}%
}{%
\raisebox{-.3\height}{%
\includegraphics[height=2em]{services/#1}%
}\hspace{.5em}%
}%
\parbox[m]{10em}{%
#2%
\ifblank{#3}{}{%
\newline%
\tiny\textcolor{gray}{#3}%
}%
}
}
\newcommand{\planned}{\hfill\scriptsize\textcolor{black!60}{\textit{(planned)}}}
\newcommand{\temporary}{\hfill\scriptsize\textcolor{black!60}{\textit{(temporary)}}}
\begin{document}
% remember picture is needed to retain positions of tcolorboxes (otherwise all edges will be misplaced)
\begin{tikzpicture}[remember picture]
\pgfdeclarelayer{regions}
\pgfdeclarelayer{hostboxes}
\pgfsetlayers{regions,hostboxes,main}
% --------------- start internet ---------------
\node[single icon] (internet) {\faIcon{globe}};
\path (internet) -- (0,-2) coordinate[name=below-internet];
\path (internet) -- (0,.7) coordinate[name=above-internet];
% --------------- end internet ---------------
{% endraw %}
{% for region in regions %}
{{ mirror() if region.mirror }}
{% set region_loop = loop %}
{% include 'region.tex.j2' %}
{{ mirror() if region.mirror }}
{% endfor %}
{% raw %}
\input{contabo-intra}
% --------------- start legend ---------------
\matrix[
anchor=north west,
yshift=2em,
matrix of nodes,
row sep=-0.1em,
every node/.style={anchor=west},
host box,
font=\small,
above=20em of internet
] (legend) {
& {\Large\sffamily\bfseries Legend} \\
\draw[ethernet] (0,0) -- (0.75,0); & Ethernet \\
\draw[wifi] (0,0) -- (0.75,0); & Wifi \\
\draw[vpn, decorate] (0,0) -- (0.75,0); & VPN \\
\draw[generic] (0,0) --(0.75,0); & Other \\
};
\node[above=1em of legend.north west, anchor=south west, text width=8em, font=\small] {last updated:\\\today};
% --------------- end legend ---------------
\begin{scope}[generic]
\draw (internet) -- (below-internet);
\end{scope}
\end{tikzpicture}
\end{document}
{% endraw %}

61
templates/host.tex.j2 Normal file
View file

@ -0,0 +1,61 @@
% --------------- start {{ host.id }} ---------------
% host description
\node[host description, {{ first_pos if host_loop.first else "below=2em of {}-host-box.south".format(host_loop.previtem.id) }}] ({{ host.id }}) {{ '{%' }}
\begin{tcolorbox}[
adjusted title={{ host.hostname }},
host description box
]
{% for ip in host.ips %}
\ip{{ '{' }}{{ ip.address }}{{ '}{' }}{{ "subnet-{}".format(ip.subnet) }}{{ '}' }}
{% endfor %}
\vspace{\baselineskip}
\begin{tabular}{@{}ll@{}}
\faIcon{compact-disc} & {{ host.os }} \\
\faIcon{memory} & {{ host.ram }} \\
\faIcon{hdd} & {{ host.disk }} \\
\end{tabular}
\tcblower
{{ host.type }}
\end{tcolorbox}
};
% services
\node[
below=0em of {{ host.id }}.south {{ east() }},
anchor=north {{ east() }}
] ({{host.id}}-services) {
\begin{tcboxedraster}[services raster]{services box}
{% for service in host.services %}
\tcbox{{"[enhanced, remember as={}-{}-service-tag]".format(loop.index0, host.id) if service.tag }}{\service{{ '{' }}{{ service.logo }}{{ '}{' }}{{ service.name }}{{ '}{}}'}}
{% endfor %}
\end{tcboxedraster}
};
{% for service in host.services %}
{% if service.tag %}
\node[anchor=east, tag={{service.tag.color}}] at ({{ loop.index0 }}-{{ host.id }}-service-tag.east) {{ '{' }}{{service.tag.label}}{{ '}' }};
{% endif %}
{% endfor %}
% network interfaces
\node[anchor=north] at ({{ host.id }}-services.north {{ west() }} -| {{ host.id }}.{{ west() }}) ({{ host.id }}-interfaces) {
\begin{tcbitemize}[interfaces raster]
{% for interface in host.interfaces %}
\tcbitem[remember as={{ host.id }}-{{ loop.index0 }}] {{ interface.name }}\hfill\faIcon{{ '{' }}{{ interface.logo }}{{ '}' }}
{% endfor %}
\end{tcbitemize}
};
% draw box around all parts defining a host
\begin{pgfonlayer}{hostboxes}
\node[
host box,
fit=({{ host.id }}.center) ({{ host.id }}-services.south)
({{ host.id }}-services.{{ east() }}) ({{ host.id }}-interfaces.south)
] ({{ host.id }}-host-box) {};
\end{pgfonlayer}
% --------------- end {{ host.id }} ---------------

85
templates/region.tex.j2 Normal file
View file

@ -0,0 +1,85 @@
% --------------- start {{ region.name }} fai ---------------
\node[single icon, label=below:{{ region.fai }}] at ({{ region.position }}) (fai-{{ region_loop.index0 }}) {\faIcon{building} };
% --------------- end {{ region.name }} fai ---------------
{% set first_pos = "below=6em of fai-{}".format(region_loop.index0) %}
{% for host in region.hosts1 %}
{% set host_loop = loop %}
{% set _ = host.update(id="{}-{}-{}".format(region_loop.index0, 'hosts1', host_loop.index0)) %}
{% include 'host.tex.j2' %}
{% endfor %}
{% set first_pos = "{}=18em of {}".format(left(), region.hosts1[0].id) %}
{{ mirror() }}
{% for host in region.hosts2 %}
{% set host_loop = loop %}
{% set _ = host.update(id="{}-{}-{}".format(region_loop.index0, 'hosts2', host_loop.index0)) %}
{% include 'host.tex.j2' %}
{% endfor %}
{{ mirror() }}
% ############### DECORATIONS ################
% --------------- start network regions ---------------
\begin{pgfonlayer}{regions}
\node[
region box={{ region.color }},
fit=
{%- for host in region.hosts1 -%}
({{ host.id}}-host-box.{{ east() }}) ({{ host.id}}-host-box.{{ west() }}) ({{ host.id }}.north) ({{ host.id}}-host-box.south)
{%- endfor -%}
{%- for host in region.hosts2 -%}
({{ host.id}}-host-box.{{ east() }}) ({{ host.id}}-host-box.{{ west() }}) ({{ host.id }}.north) ({{ host.id}}-host-box.south)
{%- endfor -%}
] ({{ region.id }}-box) {};
\node[region descriptor={{ region.color }}, above, anchor=south {{ east() }}, outer sep=1.5em] at ({{region.id }}-box.south {{ east() }}) { {{region.name}} };
\end{pgfonlayer}
% --------------- end network regions ---------------
{#
{% raw %}
% --------------- start network connections ---------------
\begin{scope}[generic]
% connections inside the router
\draw foreach \intf in {gatetway-britaliope-localnet, gateway-britaliope-wg0}
{(gateway-britaliope-uplink.{{ east() }}) ++(0,-0.15) -- +(0.25,0) |- (\intf)};
\end{scope}
\begin{scope}[vpn, subnet-vpn]
% using large radii on the waves decoration introduces ugly whitespace, so we apply some offset to compensate
\draw[decorate] (gateway-britaliope-wg0.{{ west() }}) ++(.3,0) -- ++(-.8,0);
\end{scope}
{% endraw %}
#}
\begin{scope}[generic]
\draw (fai-{{region_loop.index0}}) -| (below-internet);
{% for upstream in region.upstreams %}
\draw ({{ region_loop.index0 }}-{{ upstream.host }}-{{ upstream.interface }}.{{ west() }}) -- ++({{ m() }}1,0) |- (fai-{{ region_loop.index0 }});
{% endfor %}
\end{scope}
\begin{scope}[ethernet]
{% for host in region.hosts2 %}
{% for interface in host.interfaces %}
{% if interface.subnet %}
{% set gw = region.gateways[interface.subnet] %}
\draw[{{ "subnet-{}".format(interface.subnet) }}] ({{ host.id }}-{{ loop.index0 }}.{{ east() }}) -- ++({{ p() }}1,0) |- ({{ "{}-{}-{}".format(region_loop.index0, gw.host, gw.interface) }}.{{ west() }});
{% endif %}
{% endfor %}
{% endfor %}
\end{scope}
{% for host in region.hosts1 %}
{% for interface in host.interfaces %}
{% if interface.tag == 'vpn' %}
\begin{scope}[vpn, {{ "subnet-{}".format(interface.subnet) }}]
% using large radii on the waves decoration introduces ugly whitespace, so we apply some offset to compensate
\draw[decorate] ({{ host.id }}-{{ loop.index0 }}.{{ west() }}) ++({{ p() }}.3,0) -- ++({{ m() }}.8,0);
\end{scope}
{% endif %}
{% endfor %}
{% endfor %}