145 lines
5.8 KiB
Python
Executable File
145 lines
5.8 KiB
Python
Executable File
import docker
|
|
import pandas as pd
|
|
import re
|
|
|
|
def sanitize_id(text):
|
|
# Erlaubt nur Buchstaben, Zahlen und Unterstriche
|
|
return re.sub(r'\W|^(?=\d)', '_', text)
|
|
|
|
def get_container_info():
|
|
client = docker.from_env()
|
|
containers = client.containers.list(all=True)
|
|
|
|
data = []
|
|
|
|
for container in containers:
|
|
container_info = client.api.inspect_container(container.id)
|
|
|
|
name = container.name
|
|
labels = container_info.get('Config', {}).get('Labels', {})
|
|
watchtower_enabled = labels.get('com.centurylinklabs.watchtower.enable',
|
|
labels.get('watchtower.enable', 'not set'))
|
|
|
|
networks = container_info.get('NetworkSettings', {}).get('Networks', {})
|
|
|
|
for net_name, net_data in networks.items():
|
|
ip_address = net_data.get('IPAddress', 'N/A')
|
|
gateway = net_data.get('Gateway', 'N/A')
|
|
|
|
data.append({
|
|
'Container Name': name,
|
|
'Watchtower Enabled': watchtower_enabled,
|
|
'Network': net_name,
|
|
'IP Address': ip_address,
|
|
'Gateway': gateway
|
|
})
|
|
|
|
return data
|
|
|
|
def save_to_csv(data, filename='docker_container_info.csv'):
|
|
df = pd.DataFrame(data)
|
|
df.to_csv(filename, index=False)
|
|
print(f"[+] Daten erfolgreich in {filename} gespeichert.")
|
|
|
|
def save_to_graphml(data, filename='docker_container_info.graphml'):
|
|
from xml.etree.ElementTree import Element, SubElement, ElementTree, register_namespace
|
|
|
|
# Nur yEd Namespace registrieren!
|
|
register_namespace('y', "http://www.yworks.com/xml/graphml")
|
|
|
|
graphml = Element('graphml', xmlns="http://graphml.graphdrawing.org/xmlns")
|
|
|
|
# yEd-Label-Schlüssel
|
|
key_label = SubElement(graphml, 'key', id="d0", **{
|
|
"for": "node",
|
|
"yfiles.type": "nodegraphics"
|
|
})
|
|
key_watchtower = SubElement(graphml, 'key', id="d1", **{
|
|
"for": "node",
|
|
"attr.name": "Watchtower Enabled",
|
|
"attr.type": "string"
|
|
})
|
|
|
|
graph = SubElement(graphml, 'graph', id="G", edgedefault="undirected")
|
|
|
|
container_labels = {}
|
|
containers = set()
|
|
networks = set()
|
|
for entry in data:
|
|
cname = entry['Container Name']
|
|
containers.add(cname)
|
|
networks.add(entry['Network'])
|
|
if cname not in container_labels:
|
|
container_labels[cname] = {}
|
|
container_labels[cname]['Watchtower Enabled'] = entry['Watchtower Enabled']
|
|
|
|
# Star Schema: Netzwerke in der Mitte, Container im Kreis
|
|
import math
|
|
|
|
# Positioniere Netzwerk-Knoten im Zentrum
|
|
center_x = 500
|
|
center_y = 400
|
|
net_radius = 0 # Alle Netzwerke exakt im Zentrum
|
|
net_positions = {}
|
|
for idx, n in enumerate(networks):
|
|
node_id = f"network_{sanitize_id(n)}"
|
|
node = SubElement(graph, 'node', id=node_id)
|
|
net_infos = [entry for entry in data if entry['Network'] == n]
|
|
net_texts = [f"{entry['Container Name']}: IP: {entry['IP Address']}, GW: {entry['Gateway']}" for entry in net_infos]
|
|
label_text = f"{n}\n" + "\n".join(net_texts)
|
|
data_elem = SubElement(node, 'data', key="d0")
|
|
y_shape_node = SubElement(data_elem, '{http://www.yworks.com/xml/graphml}ShapeNode')
|
|
# Alle Netzwerke im Zentrum
|
|
geometry = SubElement(y_shape_node, '{http://www.yworks.com/xml/graphml}Geometry',
|
|
x=str(center_x), y=str(center_y), width="200", height="60")
|
|
y_node_label = SubElement(y_shape_node, '{http://www.yworks.com/xml/graphml}NodeLabel')
|
|
y_node_label.text = label_text
|
|
net_positions[n] = (center_x, center_y)
|
|
|
|
# Container-Knoten im Kreis um das Zentrum
|
|
container_count = len(containers)
|
|
radius = 300
|
|
angle_step = 2 * math.pi / max(container_count, 1)
|
|
container_positions = {}
|
|
for idx, c in enumerate(containers):
|
|
node_id = f"container_{sanitize_id(c)}"
|
|
ip_infos = [entry for entry in data if entry['Container Name'] == c]
|
|
ip_texts = [f"IP: {info['IP Address']}, GW: {info['Gateway']}, Net: {info['Network']}" for info in ip_infos]
|
|
# Ermittle Watchtower-Status aus com.centurylinklabs.watchtower.enable
|
|
watchtower_status = "aktiviert" if any(
|
|
info.get('Watchtower Enabled', '').lower() in ['true', '1', 'yes', 'enabled']
|
|
for info in ip_infos
|
|
) else "deaktiviert"
|
|
label_text = f"{c}\nWatchtower: {watchtower_status}\n" + "\n".join(ip_texts)
|
|
node = SubElement(graph, 'node', id=node_id)
|
|
data_elem = SubElement(node, 'data', key="d0")
|
|
y_shape_node = SubElement(data_elem, '{http://www.yworks.com/xml/graphml}ShapeNode')
|
|
# Kreisförmige Position
|
|
angle = idx * angle_step
|
|
x = center_x + radius * math.cos(angle)
|
|
y = center_y + radius * math.sin(angle)
|
|
geometry = SubElement(y_shape_node, '{http://www.yworks.com/xml/graphml}Geometry',
|
|
x=str(x), y=str(y), width="200", height="60")
|
|
y_node_label = SubElement(y_shape_node, '{http://www.yworks.com/xml/graphml}NodeLabel')
|
|
y_node_label.text = label_text
|
|
watchtower_elem = SubElement(node, 'data', key="d1")
|
|
watchtower_elem.text = container_labels[c]['Watchtower Enabled']
|
|
container_positions[c] = (x, y)
|
|
|
|
edge_id = 0
|
|
for entry in data:
|
|
source = f"container_{sanitize_id(entry['Container Name'])}"
|
|
target = f"network_{sanitize_id(entry['Network'])}"
|
|
edge = SubElement(graph, 'edge', id=f"e{edge_id}", source=source, target=target)
|
|
edge_id += 1
|
|
|
|
tree = ElementTree(graphml)
|
|
tree.write(filename, encoding='utf-8', xml_declaration=True)
|
|
print(f"[+] GraphML erfolgreich in {filename} gespeichert.")
|
|
|
|
if __name__ == '__main__':
|
|
print("[*] Sammle Container-Informationen...")
|
|
container_data = get_container_info()
|
|
save_to_csv(container_data)
|
|
save_to_graphml(container_data)
|