Advertisement
loayai

nat_manager.py

Sep 17th, 2024 (edited)
425
1
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 27.58 KB | Source Code | 1 0
  1. #!/usr/bin/env python3
  2. """
  3. nat_manager.py - NAT Port Forwarding Manager for Proxmox
  4.  
  5. This script was developed with assistance from OpenAI's ChatGPT.
  6. For more information, visit https://www.openai.com/chatgpt
  7. """
  8.  
  9. import sys
  10. import sqlite3
  11. import subprocess
  12. import os
  13. import re
  14. import shutil
  15. import argparse
  16. import json
  17. from datetime import datetime
  18.  
  19. # Constants
  20. PORT_START = 50000
  21. DB_FILE = '/etc/port_mappings.db'
  22. NETWORK_INTERFACE = 'vmbr0'  # Adjust if using a different interface
  23. BACKUP_DIR = '/etc/nat_manager_backups'
  24.  
  25. # Ensure script is run as root
  26. if os.geteuid() != 0:
  27.     print("Please run as root.")
  28.     sys.exit(1)
  29.  
  30. # Check if iptables-persistent is installed and enabled
  31. def check_iptables_persistent():
  32.     # Check if the package is installed
  33.     iptables_persistent_installed = False
  34.     try:
  35.         result = subprocess.run(['dpkg-query', '-W', '-f=${Status}', 'iptables-persistent'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
  36.         if 'install ok installed' in result.stdout:
  37.             iptables_persistent_installed = True
  38.     except Exception:
  39.         pass
  40.  
  41.     # Check if the netfilter-persistent service is enabled
  42.     netfilter_service_enabled = False
  43.     try:
  44.         result = subprocess.run(['systemctl', 'is-enabled', 'netfilter-persistent'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
  45.         if 'enabled' in result.stdout.strip():
  46.             netfilter_service_enabled = True
  47.     except Exception:
  48.         pass
  49.  
  50.     # Display warning if not installed or not enabled
  51.     if not iptables_persistent_installed:
  52.         print("WARNING: 'iptables-persistent' is not installed.")
  53.         print("The iptables rules will not be loaded on the next reboot unless 'iptables-persistent' is installed and enabled.")
  54.     elif not netfilter_service_enabled:
  55.         print("WARNING: 'netfilter-persistent' service is not enabled.")
  56.         print("The iptables rules will not be loaded on the next reboot unless 'netfilter-persistent' is enabled.")
  57.  
  58. # Run the check
  59. check_iptables_persistent()
  60.  
  61. # Command-line argument parsing
  62. parser = argparse.ArgumentParser(description='NAT Manager Script')
  63.  
  64. subparsers = parser.add_subparsers(dest='action', help='Available actions')
  65.  
  66. # Add action
  67. add_parser = subparsers.add_parser('add', help='Add port mappings for a container')
  68. add_parser.add_argument('container_ip', help='IP address of the container')
  69. add_parser.add_argument('--mode', choices=['automatic', 'manual'], default='automatic', help='Mode of operation')
  70. add_parser.add_argument('--num-ports', type=int, default=6, help='Number of ports to forward (default 6)')
  71. add_parser.add_argument('--internal-ports', nargs='*', type=int, help='List of internal ports for manual mode')
  72. add_parser.add_argument('--temporary', action='store_true', help='Create temporary NAT rules that are not saved')
  73. add_parser.add_argument('--external-ports', nargs='*', type=int, help='Specify external ports (advanced)')
  74. add_parser.add_argument('--protocols', nargs='*', choices=['tcp', 'udp'], help='Protocols for the ports (tcp or udp)')
  75.  
  76. # Remove action
  77. remove_parser = subparsers.add_parser('remove', help='Remove port mappings for a container')
  78. remove_parser.add_argument('container_ip', help='IP address of the container')
  79.  
  80. # List action
  81. list_parser = subparsers.add_parser('list', help='List port mappings')
  82. list_parser.add_argument('container_ip', nargs='?', help='IP address of the container to list')
  83.  
  84. # Update action
  85. update_parser = subparsers.add_parser('update', help='Update port mappings for a container')
  86. update_parser.add_argument('container_ip', help='IP address of the container')
  87. update_parser.add_argument('--internal-ports', nargs='*', type=int, help='New internal ports')
  88. update_parser.add_argument('--external-ports', nargs='*', type=int, help='External ports to update')
  89. update_parser.add_argument('--protocols', nargs='*', choices=['tcp', 'udp'], help='Protocols for the ports (tcp or udp)')
  90.  
  91. # Reserve action
  92. reserve_parser = subparsers.add_parser('reserve', help='Reserve ports for the host machine')
  93. reserve_parser.add_argument('ports', nargs='+', type=int, help='Ports to reserve')
  94.  
  95. # Unreserve action
  96. unreserve_parser = subparsers.add_parser('unreserve', help='Unreserve ports')
  97. unreserve_parser.add_argument('ports', nargs='+', type=int, help='Ports to unreserve')
  98.  
  99. # List-reserved action
  100. list_reserved_parser = subparsers.add_parser('list-reserved', help='List reserved ports')
  101.  
  102. # Export action
  103. export_parser = subparsers.add_parser('export', help='Export port mappings to a JSON file')
  104. export_parser.add_argument('file', help='File to export port mappings to')
  105.  
  106. # Import action
  107. import_parser = subparsers.add_parser('import', help='Import port mappings from a JSON file')
  108. import_parser.add_argument('file', help='File to import port mappings from')
  109.  
  110. # Backup action
  111. backup_parser = subparsers.add_parser('backup', help='Backup current configuration')
  112.  
  113. # Restore action
  114. restore_parser = subparsers.add_parser('restore', help='Restore configuration from backup')
  115. restore_parser.add_argument('timestamp', help='Timestamp of the backup to restore')
  116.  
  117. # Rebuild Database action
  118. rebuild_db_parser = subparsers.add_parser('rebuild-db', help='Rebuild the database from existing iptables rules')
  119.  
  120. args = parser.parse_args()
  121.  
  122. ACTION = args.action
  123. CONTAINER_IP = getattr(args, 'container_ip', None)
  124.  
  125. # Database setup
  126. def init_db():
  127.     conn = sqlite3.connect(DB_FILE)
  128.     cursor = conn.cursor()
  129.     cursor.execute('''
  130.        CREATE TABLE IF NOT EXISTS port_mappings (
  131.            id INTEGER PRIMARY KEY AUTOINCREMENT,
  132.            container_ip TEXT NOT NULL,
  133.            external_port INTEGER NOT NULL,
  134.            internal_port INTEGER NOT NULL,
  135.            protocol TEXT NOT NULL DEFAULT 'tcp',
  136.            temporary INTEGER NOT NULL DEFAULT 0
  137.        )
  138.    ''')
  139.     cursor.execute('''
  140.        CREATE TABLE IF NOT EXISTS reserved_ports (
  141.            port INTEGER PRIMARY KEY
  142.        )
  143.    ''')
  144.     conn.commit()
  145.     return conn
  146.  
  147. def validate_ip(ip):
  148.     pattern = re.compile(
  149.         r'^(\d{1,3}\.){3}\d{1,3}$'
  150.     )
  151.     return pattern.match(ip)
  152.  
  153. conn = init_db()
  154. cursor = conn.cursor()
  155.  
  156. # Function to check if port is in use
  157. def is_port_in_use(port):
  158.     result = subprocess.run(
  159.         ['ss', '-tulpn'],
  160.         stdout=subprocess.PIPE,
  161.         stderr=subprocess.PIPE,
  162.         text=True
  163.     )
  164.     return f":{port} " in result.stdout or f":{port}\n" in result.stdout
  165.  
  166. # Function to assign ports automatically
  167. def assign_ports(num_ports):
  168.     cursor.execute('SELECT external_port FROM port_mappings')
  169.     assigned_ports = [row[0] for row in cursor.fetchall()]
  170.     cursor.execute('SELECT port FROM reserved_ports')
  171.     reserved_ports = [row[0] for row in cursor.fetchall()]
  172.  
  173.     next_port = PORT_START
  174.     while True:
  175.         conflict = False
  176.         for i in range(num_ports):
  177.             port = next_port + i
  178.             if port in assigned_ports or is_port_in_use(port) or port in reserved_ports:
  179.                 conflict = True
  180.                 break
  181.         if not conflict:
  182.             break
  183.         else:
  184.             next_port += num_ports
  185.  
  186.     assigned_ports_to_container = [next_port + i for i in range(num_ports)]
  187.     return assigned_ports_to_container
  188.  
  189. # Function to check if an iptables rule exists
  190. def iptables_rule_exists(protocol, external_port, container_ip, internal_port):
  191.     cmd = [
  192.         'iptables', '-t', 'nat', '-C', 'PREROUTING', '-i', NETWORK_INTERFACE,
  193.         '-p', protocol, '--dport', str(external_port),
  194.         '-j', 'DNAT', '--to-destination', f'{container_ip}:{internal_port}'
  195.     ]
  196.     result = subprocess.run(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
  197.     return result.returncode == 0
  198.  
  199. # Function to set up iptables rules
  200. def setup_iptables_rules(container_ip, external_ports, internal_ports, protocols, save_rules=True):
  201.     for external_port, internal_port, protocol in zip(external_ports, internal_ports, protocols):
  202.         if not iptables_rule_exists(protocol, external_port, container_ip, internal_port):
  203.             subprocess.run([
  204.                 'iptables', '-t', 'nat', '-A', 'PREROUTING', '-i', NETWORK_INTERFACE,
  205.                 '-p', protocol, '--dport', str(external_port),
  206.                 '-j', 'DNAT', '--to-destination', f'{container_ip}:{internal_port}'
  207.             ])
  208.         else:
  209.             print(f"Iptables rule for external port {external_port}/{protocol} already exists. Skipping.")
  210.     # Enable IP forwarding
  211.     subprocess.run(['sysctl', '-w', 'net.ipv4.ip_forward=1'])
  212.  
  213.     if save_rules:
  214.         # Save iptables rules
  215.         subprocess.run(['iptables-save'], stdout=open('/etc/iptables/rules.v4', 'w'))
  216.  
  217. # Function to remove iptables rules
  218. def remove_iptables_rules(container_ip, external_ports, internal_ports, protocols, save_rules=True):
  219.     for external_port, internal_port, protocol in zip(external_ports, internal_ports, protocols):
  220.         subprocess.run([
  221.             'iptables', '-t', 'nat', '-D', 'PREROUTING', '-i', NETWORK_INTERFACE,
  222.             '-p', protocol, '--dport', str(external_port),
  223.             '-j', 'DNAT', '--to-destination', f'{container_ip}:{internal_port}'
  224.         ])
  225.     if save_rules:
  226.         # Save iptables rules
  227.         subprocess.run(['iptables-save'], stdout=open('/etc/iptables/rules.v4', 'w'))
  228.  
  229. # Function to add port mappings
  230. def add_container(container_ip, mode, num_ports, internal_ports_input, external_ports_input, protocols_input, temporary=False):
  231.     if not validate_ip(container_ip):
  232.         print("Invalid IP address.")
  233.         sys.exit(1)
  234.  
  235.     # Check if the specific container_ip already exists in the database
  236.     cursor.execute('SELECT COUNT(*) FROM port_mappings WHERE container_ip = ?', (container_ip,))
  237.     if cursor.fetchone()[0] > 0:
  238.         print(f"IP {container_ip} is already assigned ports in the database.")
  239.         sys.exit(1)
  240.     else:
  241.         # Check iptables directly for any existing rules for the IP
  242.         existing_rules = get_iptables_rules_for_ip(container_ip)
  243.         if existing_rules:
  244.             print(f"IP {container_ip} has existing iptables rules.")
  245.             print("Please remove them before adding new mappings or rebuild the database using 'rebuild-db'.")
  246.             sys.exit(1)
  247.  
  248.     if external_ports_input:
  249.         assigned_ports = external_ports_input
  250.         num_ports = len(assigned_ports)
  251.     else:
  252.         assigned_ports = assign_ports(num_ports)
  253.     internal_ports = []
  254.     protocols = []
  255.  
  256.     if mode == 'automatic':
  257.         # Predefined internal ports for the first 4 ports
  258.         INTERNAL_PORTS_DEFAULT = [22, 80, 443, 8080]
  259.         PROTOCOLS_DEFAULT = ['tcp', 'tcp', 'tcp', 'tcp']
  260.         for i in range(num_ports):
  261.             if i < len(INTERNAL_PORTS_DEFAULT):
  262.                 internal_ports.append(INTERNAL_PORTS_DEFAULT[i])
  263.                 protocols.append(PROTOCOLS_DEFAULT[i])
  264.             else:
  265.                 internal_ports.append(assigned_ports[i])  # External port equals internal port
  266.                 protocols.append('tcp')
  267.     elif mode == 'manual':
  268.         if internal_ports_input:
  269.             if len(internal_ports_input) != num_ports:
  270.                 print("Number of internal ports provided does not match num-ports.")
  271.                 sys.exit(1)
  272.             internal_ports = internal_ports_input
  273.         else:
  274.             print("Internal ports are required in manual mode.")
  275.             sys.exit(1)
  276.         if protocols_input:
  277.             if len(protocols_input) != num_ports:
  278.                 print("Number of protocols provided does not match num-ports.")
  279.                 sys.exit(1)
  280.             protocols = protocols_input
  281.         else:
  282.             protocols = ['tcp'] * num_ports  # Default to tcp if not specified
  283.     else:
  284.         print("Invalid mode.")
  285.         sys.exit(1)
  286.  
  287.     # Set up iptables rules
  288.     save_rules = not temporary
  289.     setup_iptables_rules(container_ip, assigned_ports, internal_ports, protocols, save_rules=save_rules)
  290.  
  291.     if not temporary:
  292.         # Insert mappings into database
  293.         for external_port, internal_port, protocol in zip(assigned_ports, internal_ports, protocols):
  294.             cursor.execute('''
  295.                INSERT INTO port_mappings (container_ip, external_port, internal_port, protocol)
  296.                VALUES (?, ?, ?, ?)
  297.            ''', (container_ip, external_port, internal_port, protocol))
  298.         conn.commit()
  299.  
  300.     print(f"Assigned ports to {container_ip}:")
  301.     for external_port, internal_port, protocol in zip(assigned_ports, internal_ports, protocols):
  302.         print(f"External port {external_port}/{protocol} -> {container_ip}:{internal_port}")
  303.  
  304. def remove_container(container_ip):
  305.     # Step 1: Remove rules from iptables
  306.     cursor.execute('SELECT external_port, internal_port, protocol FROM port_mappings WHERE container_ip = ?', (container_ip,))
  307.     port_mappings = cursor.fetchall()
  308.  
  309.     if not port_mappings:
  310.         print(f"No port mappings found for IP {container_ip}.")
  311.         sys.exit(1)
  312.  
  313.     for external_port, internal_port, protocol in port_mappings:
  314.         # Remove the corresponding iptables rule
  315.         remove_iptables_rule(container_ip, external_port, internal_port, protocol)
  316.  
  317.     # Step 2: Remove the entries from the database
  318.     cursor.execute('DELETE FROM port_mappings WHERE container_ip = ?', (container_ip,))
  319.     conn.commit()
  320.  
  321.     # Step 3: Save iptables rules to make changes persistent
  322.     save_iptables_rules()
  323.  
  324.     print(f"Port mappings for IP {container_ip} have been successfully removed.")
  325.  
  326. def remove_iptables_rule(container_ip, external_port, internal_port, protocol):
  327.     """
  328.    Removes a specific iptables rule.
  329.    """
  330.     command = f"iptables -t nat -D PREROUTING -i {NETWORK_INTERFACE} -p {protocol} --dport {external_port} -j DNAT --to-destination {container_ip}:{internal_port}"
  331.     result = subprocess.run(command, shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  332.     if result.returncode == 0:
  333.         print(f"Removed iptables rule: {command}")
  334.     else:
  335.         print(f"Failed to remove iptables rule: {command}. Error: {result.stderr.decode()}")
  336.  
  337. def save_iptables_rules():
  338.     """
  339.    Saves the current iptables rules to the rules file.
  340.    """
  341.     try:
  342.         result = subprocess.run(['iptables-save'], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
  343.         if result.returncode == 0:
  344.             with open('/etc/iptables/rules.v4', 'wb') as f:
  345.                 f.write(result.stdout)
  346.             print("iptables rules have been saved successfully.")
  347.         else:
  348.             print(f"Failed to save iptables rules. Error: {result.stderr.decode()}")
  349.     except Exception as e:
  350.         print(f"Error saving iptables rules: {e}")
  351.  
  352. # Function to get iptables rules for a specific container IP
  353. def get_iptables_rules_for_ip(container_ip):
  354.     result = subprocess.run(['iptables-save'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
  355.     rules = []
  356.     for line in result.stdout.splitlines():
  357.         if '-A PREROUTING' in line and f'--to-destination {container_ip}' in line:
  358.             protocol_match = re.search(r'-p (\w+)', line)
  359.             dport_match = re.search(r'--dport (\d+)', line)
  360.             to_dest_match = re.search(r'--to-destination ([^:]+):(\d+)', line)
  361.             if protocol_match and dport_match and to_dest_match:
  362.                 protocol = protocol_match.group(1)
  363.                 external_port = int(dport_match.group(1))
  364.                 internal_port = int(to_dest_match.group(2))
  365.                 rules.append({
  366.                     'protocol': protocol,
  367.                     'external_port': external_port,
  368.                     'internal_port': internal_port,
  369.                     'container_ip': container_ip
  370.                 })
  371.     return rules
  372.  
  373. # Function to rebuild the database from existing iptables rules
  374. def rebuild_database():
  375.     print("Rebuilding database from existing iptables rules...")
  376.     existing_rules = get_all_iptables_rules()
  377.     for rule in existing_rules:
  378.         container_ip = rule['container_ip']
  379.         external_port = rule['external_port']
  380.         internal_port = rule['internal_port']
  381.         protocol = rule['protocol']
  382.         # Check if the mapping already exists in the database
  383.         cursor.execute('''
  384.            SELECT COUNT(*) FROM port_mappings WHERE container_ip = ? AND external_port = ? AND protocol = ?
  385.        ''', (container_ip, external_port, protocol))
  386.         if cursor.fetchone()[0] == 0:
  387.             # Insert into database
  388.             cursor.execute('''
  389.                INSERT INTO port_mappings (container_ip, external_port, internal_port, protocol)
  390.                VALUES (?, ?, ?, ?)
  391.            ''', (container_ip, external_port, internal_port, protocol))
  392.             conn.commit()
  393.             print(f"Added mapping: External port {external_port}/{protocol} -> {container_ip}:{internal_port}")
  394.     print("Database rebuild complete.")
  395.  
  396. # Function to get all relevant iptables rules
  397. def get_all_iptables_rules():
  398.     result = subprocess.run(['iptables-save'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
  399.     rules = []
  400.     for line in result.stdout.splitlines():
  401.         if '-A PREROUTING' in line and '-j DNAT' in line and f'-i {NETWORK_INTERFACE}' in line:
  402.             protocol_match = re.search(r'-p (\w+)', line)
  403.             dport_match = re.search(r'--dport (\d+)', line)
  404.             to_dest_match = re.search(r'--to-destination ([^:]+):(\d+)', line)
  405.             if protocol_match and dport_match and to_dest_match:
  406.                 protocol = protocol_match.group(1)
  407.                 external_port = int(dport_match.group(1))
  408.                 container_ip = to_dest_match.group(1)
  409.                 internal_port = int(to_dest_match.group(2))
  410.                 rules.append({
  411.                     'protocol': protocol,
  412.                     'external_port': external_port,
  413.                     'internal_port': internal_port,
  414.                     'container_ip': container_ip
  415.                 })
  416.     return rules
  417.  
  418. # Function to list port mappings
  419. def list_mappings(container_ip=None):
  420.     cursor.execute('SELECT container_ip, external_port, internal_port, protocol, temporary FROM port_mappings ORDER BY container_ip')
  421.     rows = cursor.fetchall()
  422.     if not rows:
  423.         print("No port mappings found.")
  424.         return
  425.  
  426.     if container_ip:
  427.         print(f"Port mappings for {container_ip}:")
  428.         for row in rows:
  429.             ip, external_port, internal_port, protocol, temporary = row
  430.             if ip == container_ip:
  431.                 temp_str = " (Temporary)" if temporary else ""
  432.                 print(f"External port {external_port}/{protocol} -> {ip}:{internal_port}{temp_str}")
  433.     else:
  434.         print("All port mappings:")
  435.         current_ip = None
  436.         for row in rows:
  437.             ip, external_port, internal_port, protocol, temporary = row
  438.             temp_str = " (Temporary)" if temporary else ""
  439.             if ip != current_ip:
  440.                 current_ip = ip
  441.                 print(f"\nContainer IP: {ip}")
  442.             print(f"  External port {external_port}/{protocol} -> {ip}:{internal_port}{temp_str}")
  443.  
  444. # Function to update port mappings
  445. def update_container(container_ip, internal_ports_input=None, external_ports_input=None, protocols_input=None):
  446.     cursor.execute('SELECT external_port, internal_port, protocol FROM port_mappings WHERE container_ip = ?', (container_ip,))
  447.     rows = cursor.fetchall()
  448.     if not rows:
  449.         print(f"No existing port mappings found for {container_ip}.")
  450.         sys.exit(1)
  451.  
  452.     if external_ports_input:
  453.         # Update only specified external ports
  454.         external_ports = external_ports_input
  455.         old_mappings = {row[0]: (row[1], row[2]) for row in rows if row[0] in external_ports}
  456.         if not old_mappings:
  457.             print(f"No existing port mappings found for the specified external ports.")
  458.             sys.exit(1)
  459.     else:
  460.         external_ports = [row[0] for row in rows]
  461.         old_mappings = {row[0]: (row[1], row[2]) for row in rows}
  462.  
  463.     # Prepare lists for new internal ports and protocols
  464.     new_internal_ports = []
  465.     new_protocols = []
  466.  
  467.     if internal_ports_input is not None and protocols_input is not None:
  468.         # Non-interactive mode: both internal ports and protocols are provided
  469.         if len(internal_ports_input) != len(external_ports):
  470.             print("Number of internal ports provided does not match number of external ports.")
  471.             sys.exit(1)
  472.         if len(protocols_input) != len(external_ports):
  473.             print("Number of protocols provided does not match number of external ports.")
  474.             sys.exit(1)
  475.         new_internal_ports = internal_ports_input
  476.         new_protocols = protocols_input
  477.     else:
  478.         # Interactive mode
  479.         print("Updating port mappings. Leave input empty to keep the same mapping.")
  480.         for ext_port in external_ports:
  481.             old_internal_port, old_protocol = old_mappings[ext_port]
  482.             internal_port = input(f"External port {ext_port} -> Internal port (current: {old_internal_port}): ").strip()
  483.             if internal_port == '':
  484.                 internal_port = old_internal_port
  485.             else:
  486.                 if not internal_port.isdigit():
  487.                     print("Invalid port number.")
  488.                     sys.exit(1)
  489.                 internal_port = int(internal_port)
  490.             protocol = input(f"External port {ext_port} -> Protocol (current: {old_protocol}): ").strip()
  491.             if protocol == '':
  492.                 protocol = old_protocol
  493.             elif protocol not in ['tcp', 'udp']:
  494.                 print("Invalid protocol. Please enter 'tcp' or 'udp'.")
  495.                 sys.exit(1)
  496.             new_internal_ports.append(internal_port)
  497.             new_protocols.append(protocol)
  498.  
  499.     # Remove existing iptables rules for specified ports
  500.     old_internal_ports = [old_mappings[ext_port][0] for ext_port in external_ports]
  501.     old_protocols = [old_mappings[ext_port][1] for ext_port in external_ports]
  502.     remove_iptables_rules(container_ip, external_ports, old_internal_ports, old_protocols)
  503.  
  504.     # Update database entries and set up new iptables rules
  505.     for external_port, internal_port, protocol in zip(external_ports, new_internal_ports, new_protocols):
  506.         cursor.execute('''
  507.            UPDATE port_mappings
  508.            SET internal_port = ?, protocol = ?
  509.            WHERE container_ip = ? AND external_port = ?
  510.        ''', (internal_port, protocol, container_ip, external_port))
  511.  
  512.     conn.commit()
  513.     setup_iptables_rules(container_ip, external_ports, new_internal_ports, new_protocols)
  514.  
  515.     print(f"Updated port mappings for {container_ip}:")
  516.     for external_port, internal_port, protocol in zip(external_ports, new_internal_ports, new_protocols):
  517.         print(f"External port {external_port}/{protocol} -> {container_ip}:{internal_port}")
  518.  
  519. def reserve_ports(ports):
  520.     for port in ports:
  521.         cursor.execute('INSERT OR IGNORE INTO reserved_ports (port) VALUES (?)', (port,))
  522.     conn.commit()
  523.     print(f"Reserved ports: {', '.join(map(str, ports))}")
  524.  
  525. def unreserve_ports(ports):
  526.     for port in ports:
  527.         cursor.execute('DELETE FROM reserved_ports WHERE port = ?', (port,))
  528.     conn.commit()
  529.     print(f"Unreserved ports: {', '.join(map(str, ports))}")
  530.  
  531. def list_reserved_ports():
  532.     cursor.execute('SELECT port FROM reserved_ports ORDER BY port')
  533.     rows = cursor.fetchall()
  534.     if not rows:
  535.         print("No reserved ports.")
  536.         return
  537.     ports = [str(row[0]) for row in rows]
  538.     print("Reserved ports:")
  539.     print(', '.join(ports))
  540.  
  541. def export_mappings(file_path):
  542.     cursor.execute('SELECT container_ip, external_port, internal_port, protocol FROM port_mappings')
  543.     rows = cursor.fetchall()
  544.     data = []
  545.     for row in rows:
  546.         data.append({
  547.             'container_ip': row[0],
  548.             'external_port': row[1],
  549.             'internal_port': row[2],
  550.             'protocol': row[3]
  551.         })
  552.     with open(file_path, 'w') as f:
  553.         json.dump(data, f, indent=4)
  554.     print(f"Port mappings exported to {file_path}")
  555.  
  556. def import_mappings(file_path):
  557.     if not os.path.exists(file_path):
  558.         print(f"File {file_path} does not exist.")
  559.         sys.exit(1)
  560.     with open(file_path, 'r') as f:
  561.         data = json.load(f)
  562.     for entry in data:
  563.         container_ip = entry['container_ip']
  564.         external_port = entry['external_port']
  565.         internal_port = entry['internal_port']
  566.         protocol = entry.get('protocol', 'tcp')
  567.         # Check if the mapping already exists
  568.         cursor.execute('SELECT COUNT(*) FROM port_mappings WHERE container_ip = ? AND external_port = ?', (container_ip, external_port))
  569.         if cursor.fetchone()[0] == 0:
  570.             # Set up iptables rule
  571.             setup_iptables_rules(container_ip, [external_port], [internal_port], [protocol])
  572.             # Insert into database
  573.             cursor.execute('''
  574.                INSERT INTO port_mappings (container_ip, external_port, internal_port, protocol)
  575.                VALUES (?, ?, ?, ?)
  576.            ''', (container_ip, external_port, internal_port, protocol))
  577.     conn.commit()
  578.     print(f"Port mappings imported from {file_path}")
  579.  
  580. def backup_configuration():
  581.     timestamp = datetime.now().strftime('%Y%m%d%H%M%S')
  582.     backup_path = os.path.join(BACKUP_DIR, f'backup_{timestamp}')
  583.     os.makedirs(backup_path, exist_ok=True)
  584.     # Backup database
  585.     shutil.copy(DB_FILE, os.path.join(backup_path, 'port_mappings.db'))
  586.     # Backup iptables rules
  587.     subprocess.run(['iptables-save'], stdout=open(os.path.join(backup_path, 'rules.v4'), 'w'))
  588.     print(f"Backup created at {backup_path}")
  589.  
  590. def restore_configuration(timestamp):
  591.     backup_path = os.path.join(BACKUP_DIR, f'backup_{timestamp}')
  592.     if not os.path.exists(backup_path):
  593.         print(f"Backup with timestamp {timestamp} does not exist.")
  594.         sys.exit(1)
  595.     # Restore database
  596.     shutil.copy(os.path.join(backup_path, 'port_mappings.db'), DB_FILE)
  597.     # Restore iptables rules
  598.     subprocess.run(['iptables-restore'], stdin=open(os.path.join(backup_path, 'rules.v4'), 'r'))
  599.     print(f"Configuration restored from backup {backup_path}")
  600.  
  601. # Main execution
  602. if ACTION == 'add':
  603.     container_ip = args.container_ip
  604.     mode = args.mode
  605.     num_ports = args.num_ports
  606.     internal_ports_input = args.internal_ports
  607.     external_ports_input = args.external_ports
  608.     protocols_input = args.protocols
  609.     temporary = args.temporary
  610.     add_container(container_ip, mode, num_ports, internal_ports_input, external_ports_input, protocols_input, temporary=temporary)
  611.  
  612. elif ACTION == 'remove':
  613.     container_ip = args.container_ip
  614.     remove_container(container_ip)
  615.  
  616. elif ACTION == 'list':
  617.     container_ip = args.container_ip
  618.     list_mappings(container_ip)
  619.  
  620. elif ACTION == 'update':
  621.     container_ip = args.container_ip
  622.     internal_ports_input = args.internal_ports
  623.     external_ports_input = args.external_ports
  624.     protocols_input = args.protocols
  625.     update_container(container_ip, internal_ports_input, external_ports_input, protocols_input)
  626.  
  627. elif ACTION == 'reserve':
  628.     ports = args.ports
  629.     reserve_ports(ports)
  630.  
  631. elif ACTION == 'unreserve':
  632.     ports = args.ports
  633.     unreserve_ports(ports)
  634.  
  635. elif ACTION == 'list-reserved':
  636.     list_reserved_ports()
  637.  
  638. elif ACTION == 'export':
  639.     file_path = args.file
  640.     export_mappings(file_path)
  641.  
  642. elif ACTION == 'import':
  643.     file_path = args.file
  644.     import_mappings(file_path)
  645.  
  646. elif ACTION == 'backup':
  647.     backup_configuration()
  648.  
  649. elif ACTION == 'restore':
  650.     timestamp = args.timestamp
  651.     restore_configuration(timestamp)
  652.  
  653. elif ACTION == 'rebuild-db':
  654.     rebuild_database()
  655.  
  656. else:
  657.     print("Invalid action.")
  658.     sys.exit(1)
  659.  
  660. conn.close()
  661.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement