diff --git a/diagram/home_infra.png b/diagram/home_infra.png new file mode 100644 index 00000000..4235cbfd Binary files /dev/null and b/diagram/home_infra.png differ diff --git a/diagram/kubernetes_network.png b/diagram/kubernetes_network.png new file mode 100644 index 00000000..d4bf23c9 Binary files /dev/null and b/diagram/kubernetes_network.png differ diff --git a/diagram/main.py b/diagram/main.py new file mode 100644 index 00000000..2af15a55 --- /dev/null +++ b/diagram/main.py @@ -0,0 +1,211 @@ +from unicodedata import name +from diagrams import Diagram, Cluster, Edge, Node +from diagrams.generic.compute import Rack +from diagrams.aws.compute import EC2 +from diagrams.aws.database import RDS +from diagrams.k8s.network import Service, Ingress +from diagrams.k8s.compute import Pod +from diagrams.aws.network import ELB +from diagrams.onprem.network import Nginx, Pfsense +from diagrams.generic.network import Firewall, Router, Switch, VPN +from diagrams.generic.storage import Storage +from diagrams.generic.os import Windows, Raspbian, IOS +from diagrams.generic.device import Mobile +from diagrams.onprem.client import Client, Users +from diagrams.aws.iot import IotCamera, IotAnalyticsChannel +from kubernetes import client, config + +vpn_clients: dict[str, Node] = {} +# namespaces_to_visualize = { +# "website", "vaultwarden", "uptime", "technitium", "reverse-proxy", +# "oauth2", "monitoring", "mailserver", "kms", "immich", "headscale", +# "frigate", "f1-stream", "excalidraw", "dashy", "calibre", "audiobookshelf" +# } +namespaces_to_not_visualize = { + "ytdlp", "wireguard", "webhook-handler", "url", "travel-blog", "registry", + "redis", "openid-help-page", "localai", "kubernetes-dashboard", + "headscale", "hackmd", "finance-app", "drone", "dbaas", "crowdsec", + "cloudflared", "city-guesser" +} + + +def border_router( + name: str, + include_vpn_client: bool = False, +) -> tuple[Firewall, Router]: + with Cluster(name): + tp_link_fw = Firewall() + tp_link_router = Router() + tp_link_fw >> tp_link_router + if include_vpn_client: + vpn_client = VPN(f"{name} Tailscale Client") + vpn_clients[name] = vpn_client + return tp_link_fw, tp_link_router + + +def sofia(): + with Cluster("Sofia"): + _, tp_link_router = border_router("Border Router") + ext_switch = Switch('Extension Switch') + tp_link_router >> ext_switch + with Cluster('R730'): + with Cluster("Pfsense"): + pfsense = Pfsense('Firewall') + vpn_client = VPN("Pfsense Tailscale Client") + vpn_clients["pfsense"] = vpn_client + + with Cluster('Kubernetes Network'): + k8s_switch = Switch() + + config.load_kube_config() + v1 = client.CoreV1Api() + network_api = client.NetworkingV1Api() + for namespace in v1.list_namespace(watch=False).items: + namespace_name = namespace.metadata.name + # if namespace_name not in namespaces_to_visualize: + # continue + if namespace_name in namespaces_to_not_visualize: + continue + with Cluster(namespace_name): + for ingress in network_api.list_namespaced_ingress( + namespace_name).items: + # for k8s_svc in v1.list_namespaced_service( + # namespace_name).items: + ingress = Ingress(ingress.spec.rules[0].host) + # service = Service(k8s_svc.metadata.name) + # k8s_switch >> service + k8s_switch >> ingress + + pfsense >> k8s_switch + with Cluster('Management Network'): + mgt_switch = Switch() + # Truenas + truenas = Storage("Truenas") + # pxe server + pxe_server = Rack("PXE Server") + # HA + home_assistant = Rack("Home Assistant") + with Cluster("Devvm"): + devvm = Rack("Devvm") + devvm_vpn_client = VPN("Tailscale Client") + vpn_clients["devvm"] = devvm_vpn_client + + mgt_switch >> truenas + mgt_switch >> pxe_server + mgt_switch >> home_assistant + mgt_switch >> devvm + + pfsense >> mgt_switch + + windows10 = Windows("Windows 10 Server") + tp_link_router >> windows10 + + ext_switch >> pfsense + + nas = Storage('Synology NAS') + tp_link_router >> nas + + +def london(): + with Cluster("London"): + _, openwrt = border_router("London OpenWRT", include_vpn_client=True) + rpi = Raspbian() + # client = Mobile() + # ios_client = IOS() + ip_cam = IotCamera("IP Camera") + users = Users() + + openwrt >> rpi + # openwrt >> client + # openwrt >> ios_client + openwrt >> users + rpi >> Edge() << ip_cam + + +def valchedrym(): + with Cluster("Valchedrym"): + _, openwrt = border_router("Valchedrym OpenWRT", + include_vpn_client=True) + + users = Users() + ip_cam = IotCamera("Surveillance System") + alarm_system = IotAnalyticsChannel("Alarm System") + + openwrt >> users + openwrt >> ip_cam + openwrt >> alarm_system + + +def mladost3(): + with Cluster("Mladost 3"): + _, tp_link = border_router("Mladost 3 Router ") + laptop = Windows() + tp_link >> laptop + + +def outer_infra(): + with Diagram("Home Infra", show=False, outformat="png", direction="LR"): + sofia() + london() + valchedrym() + mladost3() + with Cluster("Mobile VPN Clients"): + mobile_vpn_clients = VPN() + vpn_clients["mobile vpn users"] = mobile_vpn_clients + mobile_vpn_users = Users("headscale.viktorbarzin.me/manager") + mobile_vpn_clients >> mobile_vpn_users + # link all vpn clients + existing_links = set() + for vpn_client in vpn_clients.values(): + for other_vpn_client in vpn_clients.values(): + if other_vpn_client == vpn_client: + continue + key = vpn_client.label + other_vpn_client.label + reverse_key = other_vpn_client.label + vpn_client.label + if key in existing_links or reverse_key in existing_links: + continue + vpn_client >> Edge(color="darkgreen") << other_vpn_client + existing_links.add(key) + + +def k8s_network(): + with Diagram("Kubernetes Network", + show=False, + outformat="png", + direction="LR"): + with Cluster("Kubernetes Network"): + k8s_switch = Switch() + config.load_kube_config() + v1 = client.CoreV1Api() + network_api = client.NetworkingV1Api() + for namespace in v1.list_namespace(watch=False).items: + namespace_name = namespace.metadata.name + # if namespace_name not in namespaces_to_visualize: + # continue + if namespace_name in namespaces_to_not_visualize or namespace_name == "monitoring": + continue + with Cluster(namespace_name): + for ingress in network_api.list_namespaced_ingress( + namespace_name).items: + ing = Ingress(ingress.spec.rules[0].host) + rule = ingress.spec.rules[0] + # for rule in ingress.spec.rules: + path = rule.http.paths[0] + # for path in rule.http.paths: + k8s_svc = path.backend.service + svc = Service(f"{k8s_svc.name}:{k8s_svc.port.number}") + ing >> svc + pods = v1.list_namespaced_pod(namespace_name) + for k8s_pod in pods.items: + if k8s_pod.status.phase != "Running": + continue + pod = Pod(k8s_pod.metadata.name) + svc >> pod + # service = Service(k8s_svc.metadata.name) + # k8s_switch >> service + k8s_switch >> ing + + +if __name__ == '__main__': + outer_infra() + k8s_network() diff --git a/diagram/requirements.txt b/diagram/requirements.txt new file mode 100644 index 00000000..96b08f1a --- /dev/null +++ b/diagram/requirements.txt @@ -0,0 +1,40 @@ +asttokens==2.4.1 +cachetools==5.3.2 +certifi==2023.11.17 +charset-normalizer==3.3.2 +decorator==5.1.1 +diagrams==0.23.4 +exceptiongroup==1.2.0 +executing==2.0.1 +google-auth==2.26.1 +graphviz==0.20.1 +idna==3.6 +ipdb==0.13.13 +ipython==8.19.0 +jedi==0.19.1 +Jinja2==3.1.2 +kubernetes==28.1.0 +MarkupSafe==2.1.3 +matplotlib-inline==0.1.6 +oauthlib==3.2.2 +parso==0.8.3 +pexpect==4.9.0 +prompt-toolkit==3.0.43 +ptyprocess==0.7.0 +pure-eval==0.2.2 +pyasn1==0.5.1 +pyasn1-modules==0.3.0 +Pygments==2.17.2 +python-dateutil==2.8.2 +PyYAML==6.0.1 +requests==2.31.0 +requests-oauthlib==1.3.1 +rsa==4.9 +six==1.16.0 +stack-data==0.6.3 +tomli==2.0.1 +traitlets==5.14.1 +typed-ast==1.5.5 +urllib3==1.26.18 +wcwidth==0.2.13 +websocket-client==1.7.0