README: SVG architecture diagram replaces mermaid; embed + text fallback; assets/readme added
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
!install.sh
|
!install.sh
|
||||||
!scripts/**
|
!scripts/**
|
||||||
!config/**
|
!config/**
|
||||||
|
!assets/**
|
||||||
*.sql.gz
|
*.sql.gz
|
||||||
*.tar.gz
|
*.tar.gz
|
||||||
.env
|
.env
|
||||||
|
|||||||
@@ -25,28 +25,21 @@ MySQL слушает TCP и ловит сканеры портянкой aborted
|
|||||||
|
|
||||||
## Архитектура
|
## Архитектура
|
||||||
|
|
||||||
```mermaid
|
<p align="center">
|
||||||
flowchart TB
|
<img src="./assets/readme/architecture.svg" width="100%" alt="Схема: браузер по HTTPS заходит на nginx; nginx через unix-сокет передаёт php-fpm; php-fpm работает с Percona MySQL только локально и с двумя memcached; cron выполняет агентов и бэкапы; CrowdSec и fail2ban читают журналы и блокируют атакующих; TCP у MySQL закрыт">
|
||||||
U["Браузер"] -->|"HTTPS :443"| NG
|
</p>
|
||||||
|
|
||||||
subgraph VPS["Ubuntu 24.04, без Docker"]
|
<details>
|
||||||
NG["nginx<br/>TLS · статика · urlrewrite<br/>limit_req zones"]
|
<summary>Текстовая версия схемы</summary>
|
||||||
|
|
||||||
NG -->|"fastcgi, unix-сокет<br/>/run/php/bitrix-fpm.sock"| PHP["php-fpm pool 'bitrix'<br/>dynamic, max 30, max_requests 500"]
|
- Браузер → HTTPS :443 → nginx (TLS, статика, urlrewrite, limit_req)
|
||||||
PHP -->|"localhost = сокет"| DB[("Percona MySQL 8.0<br/>bind 127.0.0.1 only")]
|
- nginx → fastcgi unix-сокет → php-fpm pool «bitrix» (dynamic, max 30)
|
||||||
PHP --> MC1[("memcached cache<br/>127.0.0.1:11211")]
|
- php-fpm → `localhost` = сокет → Percona MySQL 8.0; TCP :3306/:33060 закрыты
|
||||||
PHP --> MC2[("memcached sessions<br/>127.0.0.1:11212")]
|
- php-fpm → memcached :11211 (кэш) и :11212 (сессии)
|
||||||
|
- cron CLI php → агенты каждую минуту, обмен 1С каждые 5 минут, бэкапы в `/var/backups/bitrix`
|
||||||
|
- nginx access.log → CrowdSec + firewall-bouncer; sshd → fail2ban; оба банят источники атак
|
||||||
|
|
||||||
CRON["cron (CLI php)<br/>agents · exchange · backup"]
|
</details>
|
||||||
CRON --> PHP
|
|
||||||
CRON --> DB
|
|
||||||
end
|
|
||||||
|
|
||||||
LOGS["nginx access.log (combined)"] -.->|parse| CS["CrowdSec + firewall-bouncer"]
|
|
||||||
SSHD["sshd journal"] -.-> F2B["fail2ban"]
|
|
||||||
CRON --> BAK["backups:<br/>mysqldump + code tarball<br/>retention 14 дней"]
|
|
||||||
CS -.->|"блокирует"| U
|
|
||||||
```
|
|
||||||
|
|
||||||
Ключевые отличия от «Bitrix VM по умолчанию»:
|
Ключевые отличия от «Bitrix VM по умолчанию»:
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="760" viewBox="0 0 1200 760" role="img" aria-labelledby="diagTitle diagDesc">
|
||||||
|
<title id="diagTitle">bitrix-vps-infra: схема взаимодействия сервисов</title>
|
||||||
|
<desc id="diagDesc">Браузер обращается к nginx по HTTPS; nginx передаёт PHP в php-fpm через unix-сокет; php-fpm работает с Percona MySQL только по локальному сокету и с двумя инстансами memcached; cron на CLI php выполняет агентов, обмен с 1С и бэкапы; nginx отдаёт логи CrowdSec и fail2ban, которые блокируют атакующих. TCP у MySQL закрыт.</desc>
|
||||||
|
|
||||||
|
<!-- ===== палитра проекта ===== -->
|
||||||
|
<defs>
|
||||||
|
<marker id="arrB" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10 z" fill="#58a6ff"/></marker>
|
||||||
|
<marker id="arrG" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10 z" fill="#3fb950"/></marker>
|
||||||
|
<marker id="arrN" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10 z" fill="#8d9cb0"/></marker>
|
||||||
|
<marker id="arrA" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10 z" fill="#d29922"/></marker>
|
||||||
|
<marker id="arrR" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto-start-reverse"><path d="M0 0 L10 5 L0 10 z" fill="#f85149"/></marker>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<rect width="1200" height="760" rx="20" fill="#0b0e14"/>
|
||||||
|
|
||||||
|
<g font-family="-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,Arial,sans-serif">
|
||||||
|
|
||||||
|
<!-- ===== заголовок модуля ===== -->
|
||||||
|
<g id="heading">
|
||||||
|
<text x="48" y="66" font-size="30" font-weight="700" fill="#e6edf3">Архитектура стека</text>
|
||||||
|
<text x="48" y="94" font-size="18" fill="#8d9cb0">один VPS, нативные пакеты, без Docker</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== браузер ===== -->
|
||||||
|
<g id="browser">
|
||||||
|
<rect x="40" y="200" width="188" height="96" rx="10" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<text x="134" y="242" text-anchor="middle" font-size="22" font-weight="700" fill="#e6edf3">Браузер</text>
|
||||||
|
<text x="134" y="268" text-anchor="middle" font-size="17" fill="#8d9cb0">посетители</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== рамка VPS ===== -->
|
||||||
|
<g id="vps-frame">
|
||||||
|
<rect x="300" y="44" width="856" height="672" rx="14" fill="#0f131a" stroke="#2a3341"/>
|
||||||
|
<text x="320" y="78" font-size="16" letter-spacing="2" fill="#8d9cb0">VPS · UBUNTU 24.04 · НАТИВНО, БЕЗ DOCKER</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== nginx ===== -->
|
||||||
|
<g id="nginx-card">
|
||||||
|
<rect x="324" y="100" width="808" height="84" rx="10" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<rect x="324" y="100" width="5" height="84" rx="2" fill="#58a6ff"/>
|
||||||
|
<text x="346" y="136" font-size="26" font-weight="700" fill="#e6edf3">nginx</text>
|
||||||
|
<text x="346" y="166" font-size="19" fill="#8d9cb0">TLS · статика без PHP · urlrewrite · limit_req · gzip</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== php-fpm ===== -->
|
||||||
|
<g id="php-card">
|
||||||
|
<rect x="324" y="248" width="396" height="132" rx="10" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<rect x="324" y="248" width="5" height="132" rx="2" fill="#3fb950"/>
|
||||||
|
<text x="346" y="282" font-size="24" font-weight="700" fill="#e6edf3">php-fpm 8.4</text>
|
||||||
|
<text x="346" y="310" font-size="19" fill="#8d9cb0">pool «bitrix»: dynamic · max_children 30</text>
|
||||||
|
<text x="346" y="336" font-family="ui-monospace,SFMono-Regular,Consolas,monospace" font-size="17" fill="#8d9cb0">listen /run/php/bitrix-fpm.sock</text>
|
||||||
|
<text x="346" y="362" font-size="18" fill="#8d9cb0">max_requests 500 · slowlog > 5s</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== memcached x2 ===== -->
|
||||||
|
<g id="memcached-cards">
|
||||||
|
<rect x="792" y="248" width="340" height="62" rx="10" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<text x="812" y="276" font-size="20" font-weight="700" fill="#e6edf3">memcached · кэш</text>
|
||||||
|
<text x="812" y="300" font-family="ui-monospace,SFMono-Regular,Consolas,monospace" font-size="17" fill="#8d9cb0">127.0.0.1:11211 · igbinary</text>
|
||||||
|
|
||||||
|
<rect x="792" y="330" width="340" height="62" rx="10" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<text x="812" y="358" font-size="20" font-weight="700" fill="#e6edf3">memcached · сессии</text>
|
||||||
|
<text x="812" y="382" font-family="ui-monospace,SFMono-Regular,Consolas,monospace" font-size="17" fill="#8d9cb0">127.0.0.1:11212 · отдельно от кэша</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== mysql ===== -->
|
||||||
|
<g id="mysql-card">
|
||||||
|
<rect x="324" y="452" width="396" height="140" rx="10" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<rect x="324" y="452" width="5" height="140" rx="2" fill="#3fb950"/>
|
||||||
|
<text x="346" y="486" font-size="24" font-weight="700" fill="#e6edf3">Percona MySQL 8.0</text>
|
||||||
|
<text x="346" y="514" font-size="19" fill="#8d9cb0">innodb_buffer_pool_size = 4G</text>
|
||||||
|
<rect x="346" y="532" width="158" height="32" rx="8" fill="#f851491f" stroke="#f85149"/>
|
||||||
|
<text x="358" y="553" font-family="ui-monospace,SFMono-Regular,Consolas,monospace" font-size="16" fill="#f85149">✕ внешние :3306</text>
|
||||||
|
<rect x="516" y="532" width="182" height="32" rx="8" fill="#f851491f" stroke="#f85149"/>
|
||||||
|
<text x="528" y="553" font-family="ui-monospace,SFMono-Regular,Consolas,monospace" font-size="16" fill="#f85149">✕ только unix-сокет</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== cron и бэкапы ===== -->
|
||||||
|
<g id="cron-backup">
|
||||||
|
<rect x="792" y="452" width="340" height="64" rx="10" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<text x="812" y="480" font-size="20" font-weight="700" fill="#e6edf3">cron · CLI php</text>
|
||||||
|
<text x="812" y="504" font-size="17" fill="#8d9cb0">агенты раз в минуту · exchange */5</text>
|
||||||
|
|
||||||
|
<rect x="792" y="532" width="340" height="60" rx="10" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<text x="812" y="558" font-size="19" font-weight="700" fill="#e6edf3">бэкапы</text>
|
||||||
|
<text x="812" y="582" font-family="ui-monospace,SFMono-Regular,Consolas,monospace" font-size="16" fill="#8d9cb0">/var/backups/bitrix · 14 дней</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== периметр защиты ===== -->
|
||||||
|
<g id="security-band">
|
||||||
|
<rect x="324" y="612" width="808" height="88" rx="10" fill="#10151c" stroke="#2a3341" stroke-dasharray="6 5"/>
|
||||||
|
<text x="344" y="638" font-size="16" letter-spacing="2" fill="#8d9cb0">ЗАЩИТА ПЕРИМЕТРА</text>
|
||||||
|
|
||||||
|
<rect x="344" y="650" width="374" height="40" rx="8" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<text x="358" y="676" font-size="19" font-weight="700" fill="#e6edf3">CrowdSec</text>
|
||||||
|
<text x="486" y="676" font-size="17" fill="#8d9cb0">access.log · баны в nftables</text>
|
||||||
|
|
||||||
|
<rect x="742" y="650" width="366" height="40" rx="8" fill="#141a23" stroke="#2a3341"/>
|
||||||
|
<text x="756" y="676" font-size="19" font-weight="700" fill="#e6edf3">fail2ban</text>
|
||||||
|
<text x="862" y="676" font-size="17" fill="#8d9cb0">sshd jail · ban 1h</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== связи: запросы (синие) ===== -->
|
||||||
|
<g id="flows-http" stroke-width="2">
|
||||||
|
<path d="M228 248 H264 V142 H318" fill="none" stroke="#58a6ff" marker-end="url(#arrB)"/>
|
||||||
|
<text x="272" y="224" font-size="18" font-family="ui-monospace,SFMono-Regular,Consolas,monospace" fill="#58a6ff">HTTPS :443</text>
|
||||||
|
|
||||||
|
<path d="M500 186 V242" fill="none" stroke="#58a6ff" marker-end="url(#arrB)"/>
|
||||||
|
<text x="514" y="212" font-size="18" fill="#8d9cb0">fastcgi</text>
|
||||||
|
<text x="514" y="232" font-family="ui-monospace,SFMono-Regular,Consolas,monospace" font-size="16" fill="#8d9cb0">unix socket</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== связи: данные (зелёные / серые) ===== -->
|
||||||
|
<g id="flows-data" stroke-width="2">
|
||||||
|
<path d="M480 380 V446" fill="none" stroke="#3fb950" marker-end="url(#arrG)"/>
|
||||||
|
<text x="494" y="406" font-size="18" fill="#3fb950">localhost = сокет</text>
|
||||||
|
|
||||||
|
<path d="M720 290 H784" fill="none" stroke="#8d9cb0" marker-end="url(#arrN)"/>
|
||||||
|
<path d="M720 330 H784 V361 H784" fill="none" stroke="#8d9cb0"/>
|
||||||
|
|
||||||
|
<path d="M962 516 V526" fill="none" stroke="#8d9cb0" marker-end="url(#arrN)"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- ===== связь: логи в защиту (янтарная пунктирная) ===== -->
|
||||||
|
<path d="M1136 146 H1152 V660 H1138" fill="none" stroke="#d29922" stroke-width="2" stroke-dasharray="7 5" marker-end="url(#arrA)"/>
|
||||||
|
|
||||||
|
<!-- ===== связь: блокировка (красная пунктирная) ===== -->
|
||||||
|
<path d="M322 656 H214 V304" fill="none" stroke="#f85149" stroke-width="2" stroke-dasharray="7 5" marker-end="url(#arrR)"/>
|
||||||
|
<text transform="rotate(-90 196 470)" x="196" y="470" text-anchor="middle" font-size="17" fill="#f85149">отбой атакующих</text>
|
||||||
|
|
||||||
|
<!-- ===== легенда ===== -->
|
||||||
|
<g id="legend" font-size="17" fill="#8d9cb0">
|
||||||
|
<path d="M120 744 H148" stroke="#58a6ff" stroke-width="2"/><text x="156" y="750">HTTP-запросы</text>
|
||||||
|
<path d="M330 744 H358" stroke="#3fb950" stroke-width="2"/><text x="366" y="750">сокеты и данные</text>
|
||||||
|
<path d="M580 744 H608" stroke="#d29922" stroke-width="2" stroke-dasharray="7 5"/><text x="616" y="750">журналы</text>
|
||||||
|
<path d="M790 744 H818" stroke="#f85149" stroke-width="2" stroke-dasharray="7 5"/><text x="826" y="750">блокировка</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 9.4 KiB |
@@ -0,0 +1,17 @@
|
|||||||
|
import io, collections, re
|
||||||
|
import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
|
t = io.open('assets/readme/architecture.svg', encoding='utf-8').read()
|
||||||
|
ET.fromstring(t)
|
||||||
|
for bad in ('<script', 'foreignObject', '@import', 'http://fonts', 'https://fonts'):
|
||||||
|
assert bad not in t, 'sanitizer risk: ' + bad
|
||||||
|
print('SVG well-formed:', len(t), 'bytes; no script/foreignObject/remote fonts')
|
||||||
|
|
||||||
|
sizes = collections.Counter(re.findall(r'font-size="(\d+)"', t))
|
||||||
|
print('font sizes:', dict(sorted(sizes.items(), key=lambda kv: -int(kv[0]))))
|
||||||
|
|
||||||
|
r = io.open('README.md', encoding='utf-8').read()
|
||||||
|
assert 'mermaid' not in r, 'mermaid still present'
|
||||||
|
assert './assets/readme/architecture.svg' in r, 'embed missing'
|
||||||
|
assert 'Текстовая версия схемы' in r, 'fallback missing'
|
||||||
|
print('README ok: embed + text fallback, mermaid removed')
|
||||||
Reference in New Issue
Block a user