chore: доделываем поднятие сервера
This commit is contained in:
1 parent
adab286884
commit
8532557ef6
18 files changed
+749
-37
No files matched your search
@@ -40,3 +40,11 @@ Django приложение связывающее все части сайта
|
||||
4. facts: `uname -a` для карточки сервера
|
||||
|
||||
"Переустановить" сбрасывает отпечаток ssh сервера и повторяет установку с новым паролем от провайдера или, по кнопке "Использовать старый", с паролем, присланным при добавлении сервера. Отпечаток можно сбросить и в админке.
|
||||
|
||||
# TODO:
|
||||
|
||||
-[ ] для панели 3x-ui нужно активировать ssl, панель умеет сама создавать сертификаты для домена или ip адреса.
|
||||
-[ ] можно ли поставить домен после того как настроили сервер на ip? я имею ввиду поставить домен и выпустить ssl сертификат от letsencrypt.
|
||||
-[ ] что нужно сделать чтобы сервис мог нормально работать и раздавать прокси интернет через cloudflare? когда домен за прокси клауда.
|
||||
-[ ] если используется и прокси сервер и домен за claudflare, то будет работать?
|
||||
-[ ] нужна функция установки прокси в исходящие (outbound)
|
||||
@@ -0,0 +1,39 @@
|
||||
from django.db import migrations, models
|
||||
from serverus import generate_password, generate_port
|
||||
|
||||
|
||||
def generate_subscription(apps, schema_editor):
|
||||
# у уже установленных точек подписка появится после переустановки
|
||||
Point = apps.get_model('App', 'Point')
|
||||
for point in Point.objects.all():
|
||||
point.sub_port = generate_port(exclude={point.panel_port})
|
||||
point.sub_path = generate_password(12)
|
||||
point.save(update_fields=['sub_port', 'sub_path'])
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('App', '0003_server_default_root_password'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='point',
|
||||
name='panel_inbound_id',
|
||||
field=models.PositiveIntegerField(blank=True, help_text='Подключение reality в панели', null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='point',
|
||||
name='sub_port',
|
||||
field=models.PositiveIntegerField(default=0, help_text='Порт подписки панели'),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='point',
|
||||
name='sub_path',
|
||||
field=models.CharField(default='', help_text='Путь подписки без слешей', max_length=64),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.RunPython(generate_subscription, migrations.RunPython.noop),
|
||||
]
|
||||
+9
-7
@@ -4,10 +4,7 @@ from dataclasses import dataclass
|
||||
from django.contrib.auth.base_user import BaseUserManager
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.db import models, transaction
|
||||
from serverus import Host, generate_password
|
||||
|
||||
PANEL_PORT_RANGE = (10000, 20000)
|
||||
"""Не пересекается с диапазоном ssh портов serverus."""
|
||||
from serverus import Host, generate_password, generate_port, generate_username
|
||||
|
||||
|
||||
class UserManager(BaseUserManager):
|
||||
@@ -95,7 +92,7 @@ class PointManager(models.Manager.from_queryset(PointQuerySet)):
|
||||
|
||||
Точка без сервера панели не существует, поэтому панель обязательна. Имя точки и доступ к панели генерируются.
|
||||
"""
|
||||
low, high = PANEL_PORT_RANGE
|
||||
panel_port = generate_port()
|
||||
with transaction.atomic():
|
||||
point = self.create(
|
||||
owner=owner,
|
||||
@@ -103,10 +100,12 @@ class PointManager(models.Manager.from_queryset(PointQuerySet)):
|
||||
domain=domain,
|
||||
cloudflare=cloudflare,
|
||||
outbound_proxy=outbound_proxy,
|
||||
panel_username=generate_password(12),
|
||||
panel_username=generate_username(),
|
||||
panel_password=generate_password(),
|
||||
panel_port=low + secrets.randbelow(high - low),
|
||||
panel_port=panel_port,
|
||||
panel_path=generate_password(18),
|
||||
sub_port=generate_port(exclude={panel_port}),
|
||||
sub_path=generate_password(12),
|
||||
)
|
||||
Server.objects.create_server(point, Server.Role.PANEL, panel)
|
||||
if mask:
|
||||
@@ -152,6 +151,9 @@ class Point(models.Model):
|
||||
panel_port = models.PositiveIntegerField()
|
||||
panel_path = models.CharField(max_length=64)
|
||||
panel_api_token = models.CharField(max_length=255, blank=True)
|
||||
panel_inbound_id = models.PositiveIntegerField(null=True, blank=True, help_text='Подключение reality в панели')
|
||||
sub_port = models.PositiveIntegerField(help_text='Порт подписки панели')
|
||||
sub_path = models.CharField(max_length=64, help_text='Путь подписки без слешей')
|
||||
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
|
||||
+7
-1
@@ -82,15 +82,21 @@ def _bootstrap(server: Server):
|
||||
|
||||
def _install_panel(server: Server):
|
||||
point = server.point
|
||||
result = _check(server, serverus.XuiPanel(server.host()).install(
|
||||
panel = serverus.XuiPanel(server.host())
|
||||
result = _check(server, panel.install(
|
||||
username=point.panel_username,
|
||||
password=point.panel_password,
|
||||
panel_port=point.panel_port,
|
||||
web_base_path=point.panel_path,
|
||||
sub_port=point.sub_port,
|
||||
sub_path=point.sub_path,
|
||||
domain=point.domain,
|
||||
))
|
||||
point.panel_api_token = result.data.get('api_token', '')
|
||||
point.save(update_fields=['panel_api_token'])
|
||||
result = _check(server, panel.create_inbound())
|
||||
point.panel_inbound_id = result.data.get('inbound_id')
|
||||
point.save(update_fields=['panel_inbound_id'])
|
||||
|
||||
|
||||
def _redirect_to_panel(server: Server):
|
||||
|
||||
@@ -5,7 +5,9 @@ from django.test import TestCase
|
||||
from Telegram.models import User as TelegramUser
|
||||
|
||||
from . import services
|
||||
from .models import PANEL_PORT_RANGE, Credentials, Point, Server
|
||||
from serverus.credentials import SERVICE_PORT_RANGE
|
||||
|
||||
from .models import Credentials, Point, Server
|
||||
|
||||
|
||||
class FakeRunner:
|
||||
@@ -19,7 +21,11 @@ class FakeRunner:
|
||||
self.calls.append((playbook, host, extravars))
|
||||
if playbook == self.fail:
|
||||
return serverus.Result(ok=False, status='failed', error='boom', known_hosts=f'kh {host.address}')
|
||||
data = {'facts': {'uname': 'Linux test'}, 'xui': {'api_token': 'token'}}.get(playbook, {})
|
||||
data = {
|
||||
'facts': {'uname': 'Linux test'},
|
||||
'xui': {'api_token': 'token'},
|
||||
'xui_inbound': {'inbound_id': 7},
|
||||
}.get(playbook, {})
|
||||
return serverus.Result(ok=True, status='successful', data=data, known_hosts=f'kh {host.address}')
|
||||
|
||||
def playbooks(self, address):
|
||||
@@ -48,9 +54,12 @@ class CreatePointTests(TestCase):
|
||||
self.assertEqual(point.status, Point.Status.INSTALLING)
|
||||
self.assertEqual((point.panel.address, point.panel.root_password), ('10.0.0.1', 'panel-pw'))
|
||||
self.assertEqual((point.mask.address, point.mask.root_password), ('10.0.0.2', 'mask-pw'))
|
||||
low, high = PANEL_PORT_RANGE
|
||||
low, high = SERVICE_PORT_RANGE
|
||||
self.assertTrue(low <= point.panel_port < high)
|
||||
self.assertTrue(point.panel_username and point.panel_password and point.panel_path)
|
||||
self.assertTrue(low <= point.sub_port < high)
|
||||
self.assertNotEqual(point.panel_port, point.sub_port)
|
||||
self.assertEqual(len(point.panel_username), 6)
|
||||
self.assertTrue(point.panel_password and point.panel_path and point.sub_path)
|
||||
self.assertEqual(point.panel.default_root_password, 'panel-pw')
|
||||
self.assertEqual(point.entry, point.mask)
|
||||
|
||||
@@ -94,7 +103,8 @@ class InstallTests(TestCase):
|
||||
point.refresh_from_db()
|
||||
self.assertEqual(point.status, Point.Status.READY)
|
||||
self.assertEqual(point.panel_api_token, 'token')
|
||||
self.assertEqual(runner.playbooks('10.0.0.1'), ['bootstrap', 'xui', 'facts'])
|
||||
self.assertEqual(point.panel_inbound_id, 7)
|
||||
self.assertEqual(runner.playbooks('10.0.0.1'), ['bootstrap', 'xui', 'xui_inbound', 'facts'])
|
||||
self.assertEqual(runner.playbooks('10.0.0.2'), ['bootstrap', 'proxy', 'facts'])
|
||||
|
||||
host, extravars = runner.call('bootstrap', '10.0.0.1')
|
||||
@@ -111,6 +121,7 @@ class InstallTests(TestCase):
|
||||
self.assertEqual((host.port, host.known_hosts), (panel.ssh_port, 'kh 10.0.0.1'))
|
||||
self.assertEqual(extravars['domain'], 'example.com')
|
||||
self.assertEqual(extravars['panel_port'], point.panel_port)
|
||||
self.assertEqual((extravars['sub_port'], extravars['sub_path']), (point.sub_port, point.sub_path))
|
||||
|
||||
_, extravars = runner.call('proxy', '10.0.0.2')
|
||||
self.assertEqual(extravars['target_ip'], '10.0.0.1')
|
||||
@@ -120,7 +131,7 @@ class InstallTests(TestCase):
|
||||
runner = FakeRunner()
|
||||
with patch_runner(runner):
|
||||
services.install_point(point)
|
||||
self.assertEqual([playbook for playbook, _, _ in runner.calls], ['bootstrap', 'xui', 'facts'])
|
||||
self.assertEqual([playbook for playbook, _, _ in runner.calls], ['bootstrap', 'xui', 'xui_inbound', 'facts'])
|
||||
point.refresh_from_db()
|
||||
self.assertEqual(point.status, Point.Status.READY)
|
||||
|
||||
|
||||
Reference in new issue
Block a user