Перейти к содержанию

Скрипт

Запуск

  1. Отредактировать конфигурацию.
  2. Необходимо вручную настроить /etc/bind/options.conf на hq-srv.
  3. На ISP не установлен python3, нужно ставить вручную, на всех остальных всё ок.
  4. HQ-CLI не рекомендуется настраивать через скрипт. Скрипт заточен конкретно под etcnet, с native это просто не сработает, тем более обычно тот и так задан на DHCP.
ISP
python3 exam.py isp
HQ-RTR
python3 exam.py hq-rtr
BR-RTR
python3 exam.py br-rtr
HQ-SRV
python3 exam.py hq-srv
BR-SRV
python3 exam.py br-srv
HQ-CLI
python3 exam.py hq-cli

Скрипт

exam.py
#!/usr/bin/env python3
import os
import sys
import subprocess
import textwrap

# ==========================================
# КОНФИГУРАЦИОННЫЙ БЛОК
# ==========================================
CONFIG = {
    # Основной домен сети
    "domain": "au-team.irpo",

    # Системная таймзона для всех узлов
    "timezone": "Europe/Moscow",

    # Пароли для учетных записей и протоколов маршрутизации
    "passwords": {
        "net_admin": "P@ssw0rd",  # Пароль администратора сети (hq-rtr, br-rtr)
        "sshuser": "P@ssw0rd",    # Пароль пользователя SSH (hq-srv, br-srv)
        "ospf": "aboba"           # Ключ аутентификации для OSPF
    },

    # Настройки для сервиса SSH
    "ssh": {
        "port": 2026,                          # Кастомный порт SSH
        "sshuser_uid": 2026,                   # Фиксированный UID для sshuser
        "banner_text": "Authorized access only" # Текст баннера
    },

    # Номера VLAN
    "vlans": {
        "hq_srv": 100,  # VLAN для серверов HQ
        "hq_cli": 200,  # VLAN для клиентов HQ
        "mgmt": 999     # Управляющий VLAN
    },

    # Физические имена сетевых интерфейсов на виртуальных машинах
    "ifaces": {
        "isp_inet": "enp7s1",    # Внешний интерфейс ISP
        "isp_hq": "enp7s2",      # Интерфейс ISP в сторону HQ-RTR
        "isp_br": "enp7s3",      # Интерфейс ISP в сторону BR-RTR
        "hq_isp": "enp7s1",      # Интерфейс HQ-RTR в сторону ISP
        "hq_lan": "enp7s2",      # Локальный интерфейс HQ-RTR (транк для VLAN)
        "br_isp": "enp7s1",      # Интерфейс BR-RTR в сторону ISP
        "br_lan": "enp7s2",      # Локальный интерфейс BR-RTR в сторону BR-SRV
        "hq_srv_lan": "enp7s1",  # Локальный интерфейс HQ-SRV
        "hq_cli_lan": "enp7s1",  # Локальный интерфейс HQ-CLI
        "br_srv_lan": "enp7s1"   # Локальный интерфейс BR-SRV
    },

    # Полная таблица IP-адресации подсетей
    "ips": {
        # Линк ISP <-> HQ-RTR
        "isp_to_hq": {"net": "172.16.1.0", "mask": "28", "isp": "172.16.1.1", "hq": "172.16.1.2"},
        # Линк ISP <-> BR-RTR
        "isp_to_br": {"net": "172.16.2.0", "mask": "28", "isp": "172.16.2.1", "br": "172.16.2.2"},
        # Сеть серверов HQ (VLAN 100)
        "hq_srv": {"net": "192.168.1.0", "mask": "27", "gw": "192.168.1.1", "srv": "192.168.1.2"},
        # Сеть клиентов HQ (VLAN 200) + настройки пула DHCP
        "hq_cli": {"net": "192.168.2.0", "mask": "28", "gw": "192.168.2.1", "cli": "192.168.2.2", "dhcp_range": "192.168.2.2 192.168.2.15"},
        # Управляющая сеть HQ (VLAN 999)
        "hq_mgmt": {"net": "192.168.9.0", "mask": "29", "gw": "192.168.9.1"},
        # Сеть серверов филиала BR
        "br_srv": {"net": "192.168.3.0", "mask": "28", "gw": "192.168.3.1", "srv": "192.168.3.2"},
        # Адресация внутри GRE туннеля
        "gre": {"net": "192.168.4.0", "mask": "28", "hq": "192.168.4.1", "br": "192.168.4.2"},
        # Дополнительные внешние IP для DNS записей (Docker / Web)
        "docker": "172.16.2.1",
        "web": "172.16.1.1"
    }
}

# Соответствие префиксов CIDR и текстовых сетевых масок (для DHCP)
CIDR_TO_NETMASK = {
    "24": "255.255.255.0",
    "25": "255.255.255.128",
    "26": "255.255.255.192",
    "27": "255.255.255.224",
    "28": "255.255.255.240",
    "29": "255.255.255.248",
    "30": "255.255.255.252"
}

def run(cmd, input_data=None):
    print(f"[*] Выполнение: {cmd}")
    subprocess.run(cmd, shell=True, text=True, input=input_data, check=True)

def write_file(path, content):
    os.makedirs(os.path.dirname(path), exist_ok=True)
    with open(path, 'w') as f:
        f.write(content.strip() + "\n")
    print(f"[+] Файл сохранен: {path}")

def append_file(path, content):
    with open(path, 'a') as f:
        f.write("\n" + content.strip() + "\n")
    print(f"[+] Добавлено в файл: {path}")

def create_etcnet_iface(iface_name, options, ipv4address=None, ipv4route=None):
    base_dir = f"/etc/net/ifaces/{iface_name}"
    write_file(f"{base_dir}/options", options)
    if ipv4address:
        write_file(f"{base_dir}/ipv4address", ipv4address)
    if ipv4route:
        write_file(f"{base_dir}/ipv4route", ipv4route)

def set_hostname(hostname):
    full_name = f"{hostname}.{CONFIG['domain']}" if "srv" in hostname or "cli" in hostname or "rtr" in hostname else hostname
    run(f"hostnamectl hostname {full_name}")

def inject_temp_dns():
    """Временный рабочий DNS для обновления apt на чистой ОС через шлюзы"""
    write_file("/etc/resolv.conf", "nameserver 77.88.8.8")

def install_packages(packages):
    """Синхронизация apt, установка и настройка таймзоны"""
    run(f"apt-get update && apt-get install -y tzdata {packages}")
    run(f"timedatectl set-timezone {CONFIG['timezone']}")

def create_user(username, password, uid=None, is_admin=False):
    uid_str = f"-u {uid}" if uid else ""
    run(f"useradd -m {uid_str} {username} || true")
    run(f"echo '{password}' | passwd {username} --stdin")
    if is_admin:
        run(f"usermod -aG wheel {username}")
        write_file(f"/etc/sudoers.d/{username}", f"{username} ALL=(ALL) NOPASSWD: ALL")

def setup_nat(out_iface):
    run(f"iptables -t nat -A POSTROUTING -o {out_iface} -j MASQUERADE")
    run("iptables-save >> /etc/sysconfig/iptables")
    run("systemctl enable --now iptables")

def setup_ssh(username):
    ssh_conf = textwrap.dedent(f"""
        Port {CONFIG['ssh']['port']}
        PermitRootLogin no
        MaxAuthTries 2
        AllowUsers {username}
        Banner /etc/openssh/banner
    """)
    append_file("/etc/openssh/sshd_config", ssh_conf)
    write_file("/etc/openssh/banner", CONFIG['ssh']['banner_text'])
    run("systemctl restart sshd")

def setup_isp():
    set_hostname("isp")
    write_file("/etc/net/sysctl.conf", "net.ipv4.ip_forward = 1")

    create_etcnet_iface(CONFIG['ifaces']['isp_inet'], textwrap.dedent("""
        BOOTPROTO=dhcp
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
        NM_CONTROLLED=no
        SYSTEMD_BOOTPROTO=dhcp4
        SYSTEMD_CONTROLLED=no
    """))

    create_etcnet_iface(CONFIG['ifaces']['isp_hq'], textwrap.dedent("""
        BOOTPROTO=static
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
        NM_CONTROLLED=no
        SYSTEMD_BOOTPROTO=static
    """), f"{CONFIG['ips']['isp_to_hq']['isp']}/{CONFIG['ips']['isp_to_hq']['mask']}")

    create_etcnet_iface(CONFIG['ifaces']['isp_br'], textwrap.dedent("""
        BOOTPROTO=static
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
        NM_CONTROLLED=no
        SYSTEMD_BOOTPROTO=static
    """), f"{CONFIG['ips']['isp_to_br']['isp']}/{CONFIG['ips']['isp_to_br']['mask']}")

    run("systemctl restart network")
    install_packages("iptables")
    setup_nat(CONFIG['ifaces']['isp_inet'])


def setup_hq_rtr():
    set_hostname("hq-rtr")
    create_user("net_admin", CONFIG['passwords']['net_admin'], is_admin=True)
    write_file("/etc/net/sysctl.conf", "net.ipv4.ip_forward = 1")

    # Внешний интерфейс к ISP
    create_etcnet_iface(CONFIG['ifaces']['hq_isp'], textwrap.dedent("""
        BOOTPROTO=static
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
        NM_CONTROLLED=no
    """), f"{CONFIG['ips']['isp_to_hq']['hq']}/{CONFIG['ips']['isp_to_hq']['mask']}",
                        f"default via {CONFIG['ips']['isp_to_hq']['isp']}")

    # Физический интерфейс LAN (База для VLAN)
    create_etcnet_iface(CONFIG['ifaces']['hq_lan'], "BOOTPROTO=static\nTYPE=eth\nCONFIG_IPV4=yes\nDISABLED=no\nNM_CONTROLLED=no")

    # Нарезка VLAN интерфейсов
    vlans = [
        (CONFIG['vlans']['hq_srv'], CONFIG['ips']['hq_srv']['gw'], CONFIG['ips']['hq_srv']['mask']),
        (CONFIG['vlans']['hq_cli'], CONFIG['ips']['hq_cli']['gw'], CONFIG['ips']['hq_cli']['mask']),
        (CONFIG['vlans']['mgmt'], CONFIG['ips']['hq_mgmt']['gw'], CONFIG['ips']['hq_mgmt']['mask'])
    ]
    for vid, ip, mask in vlans:
        create_etcnet_iface(f"{CONFIG['ifaces']['hq_lan']}.{vid}", textwrap.dedent(f"""
            BOOTPROTO=static
            TYPE=vlan
            CONFIG_IPV4=yes
            DISABLED=no
            NM_CONTROLLED=no
            VID={vid}
            HOST={CONFIG['ifaces']['hq_lan']}
        """), f"{ip}/{mask}")

    # Поднятие GRE туннеля до филиала BR
    create_etcnet_iface("tunnel1", textwrap.dedent(f"""
        TYPE=iptun
        CONFIG_IPV4=yes
        DISABLED=no
        TUNTYPE=gre
        TUNLOCAL={CONFIG['ips']['isp_to_hq']['hq']}
        TUNREMOTE={CONFIG['ips']['isp_to_br']['br']}
        TUNTTL=32
        TUNOPTIONS='ttl 32'
        TUNMTU=1426
        HOST={CONFIG['ifaces']['hq_isp']}
    """), f"{CONFIG['ips']['gre']['hq']}/{CONFIG['ips']['gre']['mask']}")

    # Перезапуск сети для применения всех маршрутов и линков
    run("systemctl restart network")

    inject_temp_dns()
    install_packages("iptables dhcp-server")
    setup_nat(CONFIG['ifaces']['hq_isp'])

    # Динамический расчет маски для DHCP-сервера на основе CONFIG
    cli_cidr = CONFIG['ips']['hq_cli']['mask']
    cli_netmask = CIDR_TO_NETMASK.get(cli_cidr, "255.255.255.0")

    run("cp /etc/dhcp/dhcpd.conf.sample /etc/dhcp/dhcpd.conf || true")
    write_file("/etc/dhcp/dhcpd.conf", textwrap.dedent(f"""
        ddns-update-style none;
        subnet {CONFIG['ips']['hq_cli']['net']} netmask {cli_netmask} {{
                option routers                  {CONFIG['ips']['hq_cli']['gw']};
                option subnet-mask              {cli_netmask};
                option domain-name              "{CONFIG['domain']}";
                option domain-name-servers      {CONFIG['ips']['hq_srv']['srv']};
                range dynamic-bootp {CONFIG['ips']['hq_cli']['dhcp_range']};
                default-lease-time 21600;
                max-lease-time 43200;
        }}
    """))
    write_file("/etc/sysconfig/dhcpd", f"DHCPARGS={CONFIG['ifaces']['hq_lan']}.{CONFIG['vlans']['hq_cli']}")
    run("systemctl enable --now dhcpd")

    # Запуск и конфигурация динамической маршрутизации OSPF (FRR)
    write_file("/etc/frr/daemons", "ospfd=yes")
    run("systemctl enable --now frr")
    vtysh_cmd = textwrap.dedent(f"""
        conf t
        hostname hq-rtr.{CONFIG['domain']}
        router ospf
        passive-interface default
        network {CONFIG['ips']['hq_srv']['net']}/{CONFIG['ips']['hq_srv']['mask']} area 0
        network {CONFIG['ips']['hq_cli']['net']}/{CONFIG['ips']['hq_cli']['mask']} area 0
        network {CONFIG['ips']['hq_mgmt']['net']}/{CONFIG['ips']['hq_mgmt']['mask']} area 0
        network {CONFIG['ips']['gre']['net']}/{CONFIG['ips']['gre']['mask']} area 0
        exit
        interface tunnel1
        ip ospf authentication
        ip ospf authentication-key {CONFIG['passwords']['ospf']}
        no ip ospf passive
        end
        wr mem
    """)
    run("vtysh", input_data=vtysh_cmd)
    run("systemctl restart frr")

    # Финальная перенастройка DNS на внутренний сервер HQ-SRV
    write_file("/etc/resolv.conf", textwrap.dedent(f"""
        nameserver {CONFIG['ips']['hq_srv']['srv']}
        domain {CONFIG['domain']}
        search {CONFIG['domain']}
    """))


def setup_hq_srv():
    set_hostname("hq-srv")
    create_user("sshuser", CONFIG['passwords']['sshuser'], uid=CONFIG['ssh']['sshuser_uid'], is_admin=True)
    setup_ssh("sshuser")

    create_etcnet_iface(CONFIG['ifaces']['hq_srv_lan'], textwrap.dedent("""
        BOOTPROTO=static
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
    """), f"{CONFIG['ips']['hq_srv']['srv']}/{CONFIG['ips']['hq_srv']['mask']}",
                        f"default via {CONFIG['ips']['hq_srv']['gw']}")

    run("systemctl restart network")

    inject_temp_dns()
    install_packages("bind bind-utils")

    # ВЫВОД ИНСТРУКЦИЙ ДЛЯ /etc/bind/options.conf В КОНСОЛЬ (не перезаписываем файл)
    print("\n" + "="*60)
    print(" ВНИМАНИЕ: НЕОБХОДИМО ВРУЧНУЮ ОТРЕДАКТИРОВАТЬ /etc/bind/options.conf")
    print(" Добавьте или измените внутри блока options {} следующие строки:")
    print("-"*60)
    print(textwrap.dedent("""
        dnssec-validation no;
        forwarders { 77.88.8.8; };
        allow-query { any; };
        listen-on { any; };
    """).strip())
    print("="*60 + "\n")

    bind_local_zones = textwrap.dedent(f"""
        zone "{CONFIG['domain']}" {{
            type master;
            file "{CONFIG['domain']}";
        }};
        zone "168.192.in-addr.arpa" {{
            type master;
            file "168.192.in-addr.arpa";
        }};
    """)
    append_file("/etc/bind/local.conf", bind_local_zones)

    write_file(f"/etc/bind/zone/{CONFIG['domain']}", textwrap.dedent(f"""
        $TTL    1D
        @       IN      SOA     {CONFIG['domain']}. root.{CONFIG['domain']}. (
                                        1               ; serial
                                        12H             ; refresh
                                        1H              ; retry
                                        1W              ; expire
                                        1H              ; ncache
                                )
                IN      NS      hq-srv.{CONFIG['domain']}.
        hq-rtr  IN      A       {CONFIG['ips']['hq_srv']['gw']}
        hq-rtr  IN      A       {CONFIG['ips']['hq_cli']['gw']}
        br-rtr  IN      A       {CONFIG['ips']['br_srv']['gw']}
        hq-srv  IN      A       {CONFIG['ips']['hq_srv']['srv']}
        hq-cli  IN      A       {CONFIG['ips']['hq_cli']['cli']}
        br-srv  IN      A       {CONFIG['ips']['br_srv']['srv']}
        docker  IN      A       {CONFIG['ips']['docker']}
        web     IN      A       {CONFIG['ips']['web']}
    """))

    # Динамический расчет PTR-записей для обратной зоны
    def get_octets(ip): return ip.split('.')[2], ip.split('.')[3]
    hqs_gw_3, hqs_gw_4 = get_octets(CONFIG['ips']['hq_srv']['gw'])
    hqc_gw_3, hqc_gw_4 = get_octets(CONFIG['ips']['hq_cli']['gw'])
    hqs_sr_3, hqs_sr_4 = get_octets(CONFIG['ips']['hq_srv']['srv'])
    hqc_cl_3, hqc_cl_4 = get_octets(CONFIG['ips']['hq_cli']['cli'])

    write_file("/etc/bind/zone/168.192.in-addr.arpa", textwrap.dedent(f"""
        $TTL    1D
        @       IN      SOA     {CONFIG['domain']}. root.{CONFIG['domain']}. (
                                        1               ; serial
                                        12H             ; refresh
                                        1H              ; retry
                                        1W              ; expire
                                        1H              ; ncache
                                )
                IN      NS      hq-srv.{CONFIG['domain']}.
        {hqs_gw_4}.{hqs_gw_3}     IN      PTR     hq-rtr.{CONFIG['domain']}.
        {hqc_gw_4}.{hqc_gw_3}     IN      PTR     hq-rtr.{CONFIG['domain']}.
        {hqc_cl_4}.{hqc_cl_3}     IN      PTR     hq-cli.{CONFIG['domain']}.
        {hqs_sr_4}.{hqs_sr_3}     IN      PTR     hq-srv.{CONFIG['domain']}.
    """))

    run(f"chown root:named /etc/bind/zone/{CONFIG['domain']}")
    run("chown root:named /etc/bind/zone/168.192.in-addr.arpa")

    run("systemctl enable --now bind")
    run("rndc reload || true")

    write_file("/etc/resolv.conf", textwrap.dedent(f"""
        nameserver 127.0.0.1
        domain {CONFIG['domain']}
        search {CONFIG['domain']}
    """))


def setup_br_rtr():
    set_hostname("br-rtr")
    create_user("net_admin", CONFIG['passwords']['net_admin'], is_admin=True)
    write_file("/etc/net/sysctl.conf", "net.ipv4.ip_forward = 1")

    create_etcnet_iface(CONFIG['ifaces']['br_isp'], textwrap.dedent("""
        BOOTPROTO=static
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
    """), f"{CONFIG['ips']['isp_to_br']['br']}/{CONFIG['ips']['isp_to_br']['mask']}",
                        f"default via {CONFIG['ips']['isp_to_br']['isp']}")

    create_etcnet_iface(CONFIG['ifaces']['br_lan'], textwrap.dedent("""
        BOOTPROTO=static
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
    """), f"{CONFIG['ips']['br_srv']['gw']}/{CONFIG['ips']['br_srv']['mask']}")

    create_etcnet_iface("tunnel1", textwrap.dedent(f"""
        TYPE=iptun
        CONFIG_IPV4=yes
        DISABLED=no
        TUNTYPE=gre
        TUNLOCAL={CONFIG['ips']['isp_to_br']['br']}
        TUNREMOTE={CONFIG['ips']['isp_to_hq']['hq']}
        TUNTTL=32
        TUNOPTIONS='ttl 32'
        TUNMTU=1426
        HOST={CONFIG['ifaces']['br_isp']}
    """), f"{CONFIG['ips']['gre']['br']}/{CONFIG['ips']['gre']['mask']}")

    run("systemctl restart network")

    inject_temp_dns()
    install_packages("iptables")
    setup_nat(CONFIG['ifaces']['br_isp'])

    write_file("/etc/frr/daemons", "ospfd=yes")
    run("systemctl enable --now frr")
    vtysh_cmd = textwrap.dedent(f"""
        conf t
        hostname br-rtr.{CONFIG['domain']}
        router ospf
        passive-interface default
        network {CONFIG['ips']['br_srv']['net']}/{CONFIG['ips']['br_srv']['mask']} area 0
        network {CONFIG['ips']['gre']['net']}/{CONFIG['ips']['gre']['mask']} area 0
        exit
        interface tunnel1
        ip ospf authentication
        ip ospf authentication-key {CONFIG['passwords']['ospf']}
        no ip ospf passive
        end
        wr mem
    """)
    run("vtysh", input_data=vtysh_cmd)
    run("systemctl restart frr")

    write_file("/etc/resolv.conf", textwrap.dedent(f"""
        nameserver {CONFIG['ips']['hq_srv']['srv']}
        domain {CONFIG['domain']}
        search {CONFIG['domain']}
    """))


def setup_br_srv():
    set_hostname("br-srv")
    create_user("sshuser", CONFIG['passwords']['sshuser'], uid=CONFIG['ssh']['sshuser_uid'], is_admin=True)
    setup_ssh("sshuser")

    create_etcnet_iface(CONFIG['ifaces']['br_srv_lan'], textwrap.dedent("""
        BOOTPROTO=static
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
    """), f"{CONFIG['ips']['br_srv']['srv']}/{CONFIG['ips']['br_srv']['mask']}",
                        f"default via {CONFIG['ips']['br_srv']['gw']}")

    run("systemctl restart network")

    inject_temp_dns()
    install_packages("")

    write_file("/etc/resolv.conf", textwrap.dedent(f"""
        nameserver {CONFIG['ips']['hq_srv']['srv']}
        domain {CONFIG['domain']}
        search {CONFIG['domain']}
    """))


def setup_hq_cli():
    set_hostname("hq-cli")
    create_etcnet_iface(CONFIG['ifaces']['hq_cli_lan'], textwrap.dedent("""
        BOOTPROTO=dhcp
        TYPE=eth
        CONFIG_IPV4=yes
        DISABLED=no
        NM_CONTROLLED=no
        SYSTEMD_BOOTPROTO=dhcp4
    """))
    run("systemctl restart network")
    install_packages("")

    write_file("/etc/resolv.conf", textwrap.dedent(f"""
        nameserver {CONFIG['ips']['hq_srv']['srv']}
        domain {CONFIG['domain']}
        search {CONFIG['domain']}
    """))

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Использование: python3 setup_exam.py <ИМЯ_УЗЛА>")
        print("Доступные узлы: isp, hq-rtr, hq-srv, hq-cli, br-rtr, br-srv")
        sys.exit(1)

    node = sys.argv[1].lower()

    if os.geteuid() != 0:
        print("ОШИБКА: Скрипт необходимо запускать от пользователя root (или через sudo).")
        sys.exit(1)

    print(f"=== Запуск настройки для узла: {node.upper()} ===")

    if node == "isp": setup_isp()
    elif node == "hq-rtr": setup_hq_rtr()
    elif node == "hq-srv": setup_hq_srv()
    elif node == "br-rtr": setup_br_rtr()
    elif node == "br-srv": setup_br_srv()
    elif node == "hq-cli": setup_hq_cli()
    else:
        print(f"Неизвестный узел: {node}")
        sys.exit(1)

    print(f"=== Настройка {node.upper()} успешно завершена! ===")