initial
This commit is contained in:
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
certs/
|
||||||
55
docker-compose.yml
Normal file
55
docker-compose.yml
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
services:
|
||||||
|
dnsmasq:
|
||||||
|
image: jpillora/dnsmasq
|
||||||
|
container_name: dnsmasq
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "53:53/udp"
|
||||||
|
- "53:53/tcp"
|
||||||
|
volumes:
|
||||||
|
- ./dnsmasq.conf:/etc/dnsmasq.conf:ro
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
networks:
|
||||||
|
- devnet
|
||||||
|
|
||||||
|
nginx-proxy:
|
||||||
|
image: nginxproxy/nginx-proxy:alpine
|
||||||
|
container_name: nginx-proxy
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/tmp/docker.sock:ro
|
||||||
|
- ./certs:/etc/nginx/certs:ro
|
||||||
|
- ./vhost.d:/etc/nginx/vhost.d:ro
|
||||||
|
- ./html:/usr/share/nginx/html:rw
|
||||||
|
networks:
|
||||||
|
- devnet
|
||||||
|
labels:
|
||||||
|
- "com.github.nginx-proxy.nginx"
|
||||||
|
|
||||||
|
nginx-proxy-companion:
|
||||||
|
image: nginxproxy/acme-companion
|
||||||
|
container_name: nginx-proxy-companion
|
||||||
|
restart: always
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
- ./certs:/etc/nginx/certs:rw
|
||||||
|
- ./acme:/etc/acme.sh
|
||||||
|
- ./vhost.d:/etc/nginx/vhost.d
|
||||||
|
- ./html:/usr/share/nginx/html
|
||||||
|
networks:
|
||||||
|
- devnet
|
||||||
|
environment:
|
||||||
|
- NGINX_PROXY_CONTAINER=nginx-proxy
|
||||||
|
|
||||||
|
networks:
|
||||||
|
devnet:
|
||||||
|
external: true
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
acme:
|
||||||
|
vhost.d:
|
||||||
|
html:
|
||||||
113
scripts/cert_renew_restart.py
Normal file
113
scripts/cert_renew_restart.py
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
# cert_renew_restart.py
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
def generate_mkcert_command():
|
||||||
|
"""Генерирует команду для mkcert с указанием всех доменов"""
|
||||||
|
services = read_services_config()
|
||||||
|
domains = [f"{service['name']}.local" for service in services['services']]
|
||||||
|
domains.append("*.local")
|
||||||
|
|
||||||
|
# Создаем PowerShell команду
|
||||||
|
domains_str = " ".join([f'"{domain}"' for domain in domains])
|
||||||
|
powershell_cmd = f'mkcert {domains_str}'
|
||||||
|
return powershell_cmd
|
||||||
|
|
||||||
|
def copy_and_rename_certificates():
|
||||||
|
"""Копирует и переименовывает сертификаты из Windows в WSL"""
|
||||||
|
# Определяем пути
|
||||||
|
windows_certs_path = Path("C:/store/certs")
|
||||||
|
wsl_certs_path = Path.home() / "projects" / "docker" / "network" / "certs"
|
||||||
|
|
||||||
|
# Создаем директорию для сертификатов в WSL
|
||||||
|
wsl_certs_path.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
# Находим сгенерированные mkcert файлы
|
||||||
|
cert_files = list(windows_certs_path.glob("_wildcard.local+*.pem"))
|
||||||
|
if not cert_files:
|
||||||
|
print("Сертификаты не найдены в C:/store/certs")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Предполагаем, что mkcert генерирует файлы в формате _wildcard.domain+N.pem
|
||||||
|
cert_file = cert_files[0]
|
||||||
|
key_file = cert_files[0].with_name(cert_file.name.replace(".pem", "-key.pem"))
|
||||||
|
|
||||||
|
if not key_file.exists():
|
||||||
|
print(f"Файл ключа не найден: {key_file}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Копируем и переименовываем
|
||||||
|
target_cert = wsl_certs_path / "wildcard.local.pem"
|
||||||
|
target_key = wsl_certs_path / "wildcard.local.key"
|
||||||
|
|
||||||
|
shutil.copy2(cert_file, target_cert)
|
||||||
|
shutil.copy2(key_file, target_key)
|
||||||
|
|
||||||
|
# Создаем копии для default
|
||||||
|
default_cert = wsl_certs_path / "default.crt"
|
||||||
|
default_key = wsl_certs_path / "default.key"
|
||||||
|
|
||||||
|
shutil.copy2(target_cert, default_cert)
|
||||||
|
shutil.copy2(target_key, default_key)
|
||||||
|
|
||||||
|
print(f"Сертификаты скопированы в {wsl_certs_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
def run_docker_command(cmd):
|
||||||
|
"""Выполняет команду Docker и возвращает результат"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"Ошибка выполнения команды: {cmd}")
|
||||||
|
print(f"Ошибка: {result.stderr}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка выполнения команды: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Обновление сертификатов и перезапуск сервисов...")
|
||||||
|
|
||||||
|
# Получаем имя директории проекта (предполагаем, что это "network")
|
||||||
|
project_name = "network"
|
||||||
|
|
||||||
|
# Генерируем конфигурационный файл dnsmasq
|
||||||
|
generate_dnsmasq_config()
|
||||||
|
|
||||||
|
# Выводим команду для генерации сертификатов
|
||||||
|
mkcert_cmd = generate_mkcert_command()
|
||||||
|
print("\nДля генерации сертификатов выполните в PowerShell (от администратора):")
|
||||||
|
print(f"cd C:\\store\\certs")
|
||||||
|
print(mkcert_cmd)
|
||||||
|
print()
|
||||||
|
|
||||||
|
# Ждем подтверждения пользователя
|
||||||
|
input("Нажмите Enter после генерации сертификатов в PowerShell...")
|
||||||
|
|
||||||
|
# Копируем сертификаты
|
||||||
|
if not copy_and_rename_certificates():
|
||||||
|
print("Не удалось скопировать сертификаты")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Переходим в директорию проекта
|
||||||
|
project_path = Path.home() / "projects" / "docker" / project_name
|
||||||
|
if not project_path.exists():
|
||||||
|
print(f"Директория проекта не найдена: {project_path}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
os.chdir(project_path)
|
||||||
|
|
||||||
|
# Перезапускаем nginx-proxy для загрузки новых сертификатов
|
||||||
|
print("Перезапуск nginx-proxy...")
|
||||||
|
if not run_docker_command("docker-compose restart nginx-proxy"):
|
||||||
|
print("Не удалось перезапустить nginx-proxy")
|
||||||
|
# Если restart не сработал, пробуем поднять контейнеры
|
||||||
|
if not run_docker_command("docker-compose up -d"):
|
||||||
|
print("Не удалось поднять контейнеры")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("Сертификаты обновлены и сервисы перезапущены!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
112
scripts/common.py
Normal file
112
scripts/common.py
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
# common.py
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import platform
|
||||||
|
import json
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
def get_host_ip():
|
||||||
|
"""Получает IP-адрес хоста в локальной сети"""
|
||||||
|
try:
|
||||||
|
# Для WSL2 получаем IP хоста (Windows)
|
||||||
|
if 'microsoft' in platform.uname().release.lower():
|
||||||
|
# В WSL2 получаем IP хоста из файла resolv.conf
|
||||||
|
with open('/etc/resolv.conf', 'r') as f:
|
||||||
|
for line in f:
|
||||||
|
if line.startswith('nameserver'):
|
||||||
|
return line.split()[1]
|
||||||
|
# Для других систем используем подключение к внешнему серверу
|
||||||
|
else:
|
||||||
|
import socket
|
||||||
|
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||||
|
s.connect(("8.8.8.8", 80))
|
||||||
|
ip = s.getsockname()[0]
|
||||||
|
s.close()
|
||||||
|
return ip
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Не удалось получить IP-адрес хоста: {e}")
|
||||||
|
# Возвращаем IP по умолчанию, если не удалось получить
|
||||||
|
return "192.168.31.60"
|
||||||
|
|
||||||
|
def read_services_config():
|
||||||
|
"""Читает конфигурацию сервисов из файла"""
|
||||||
|
config_path = Path.home() / "projects" / "docker" / "network" / "services.json"
|
||||||
|
default_config = {
|
||||||
|
"services": [
|
||||||
|
{"name": "gitea", "port": 3000},
|
||||||
|
{"name": "youtrack", "port": 8080},
|
||||||
|
{"name": "n8n", "port": 5678}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
if config_path.exists():
|
||||||
|
with open(config_path, 'r', encoding='utf-8') as f:
|
||||||
|
return json.load(f)
|
||||||
|
else:
|
||||||
|
# Создаем файл конфигурации по умолчанию
|
||||||
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
with open(config_path, 'w', encoding='utf-8') as f:
|
||||||
|
json.dump(default_config, f, indent=2, ensure_ascii=False)
|
||||||
|
return default_config
|
||||||
|
|
||||||
|
def generate_dnsmasq_config():
|
||||||
|
"""Генерирует конфигурационный файл dnsmasq"""
|
||||||
|
host_ip = get_host_ip()
|
||||||
|
services = read_services_config()
|
||||||
|
|
||||||
|
config_content = f"""# Локальные домены → твой ПК
|
||||||
|
"""
|
||||||
|
for service in services['services']:
|
||||||
|
config_content += f"address=/{service['name']}.local/{host_ip}\n"
|
||||||
|
|
||||||
|
config_content += f"""address=/.local/{host_ip}
|
||||||
|
|
||||||
|
# Кэш DNS
|
||||||
|
cache-size=500
|
||||||
|
|
||||||
|
# Лог (опционально)
|
||||||
|
log-queries
|
||||||
|
log-facility=/var/log/dnsmasq.log
|
||||||
|
|
||||||
|
# Не перехватывать внешние запросы — делегируем
|
||||||
|
no-resolv
|
||||||
|
server=8.8.8.8
|
||||||
|
server=1.1.1.1
|
||||||
|
"""
|
||||||
|
|
||||||
|
# Путь к конфигурационному файлу
|
||||||
|
config_path = Path.home() / "projects" / "docker" / "network" / "dnsmasq.conf"
|
||||||
|
config_path.parent.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
with open(config_path, 'w', encoding='utf-8') as f:
|
||||||
|
f.write(config_content.strip())
|
||||||
|
|
||||||
|
print(f"Конфигурационный файл dnsmasq обновлен: {config_path}")
|
||||||
|
|
||||||
|
def run_docker_command(cmd):
|
||||||
|
"""Выполняет команду Docker и возвращает результат"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
||||||
|
if result.returncode != 0:
|
||||||
|
print(f"Ошибка выполнения команды: {cmd}")
|
||||||
|
print(f"Ошибка: {result.stderr}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Ошибка выполнения команды: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
def setup_project():
|
||||||
|
"""Настройка проекта: генерация конфигов и переход в директорию"""
|
||||||
|
# Генерируем конфигурационный файл dnsmasq
|
||||||
|
generate_dnsmasq_config()
|
||||||
|
|
||||||
|
# Переходим в директорию проекта
|
||||||
|
project_path = Path.home() / "projects" / "docker" / "network"
|
||||||
|
if not project_path.exists():
|
||||||
|
print(f"Директория проекта не найдена: {project_path}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
os.chdir(project_path)
|
||||||
|
return project_path
|
||||||
22
scripts/restart_services.py
Normal file
22
scripts/restart_services.py
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# restart_services.py
|
||||||
|
from common import *
|
||||||
|
|
||||||
|
def main():
|
||||||
|
print("Перезапуск сервисов...")
|
||||||
|
|
||||||
|
# Настройка проекта
|
||||||
|
setup_project()
|
||||||
|
|
||||||
|
# Перезапускаем контейнеры
|
||||||
|
print("Перезапуск контейнеров...")
|
||||||
|
if not run_docker_command("docker-compose restart"):
|
||||||
|
print("Не удалось перезапустить контейнеры")
|
||||||
|
# Если restart не сработал, пробуем поднять контейнеры
|
||||||
|
if not run_docker_command("docker-compose up -d"):
|
||||||
|
print("Не удалось поднять контейнеры")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
print("Сервисы перезапущены!")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
2
vhost.d/_local
Normal file
2
vhost.d/_local
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
ssl_certificate /etc/nginx/certs/wildcard.local.pem;
|
||||||
|
ssl_certificate_key /etc/nginx/certs/wildcard.local.key;
|
||||||
Reference in New Issue
Block a user