Compare commits

...

3 Commits

Author SHA1 Message Date
83ff791169 Disable vision 2026-10-28 19:32:22 +04:00
d7252da3c7 Add xray 2026-10-28 18:36:47 +04:00
489e888ad2 Fixes 2026-10-28 18:20:21 +04:00
13 changed files with 352 additions and 89 deletions

3
.gitignore vendored
View File

@@ -18,6 +18,9 @@ gitea/config/*
matrix/data/* matrix/data/*
matrix/db matrix/db
xray/config/config.json
xray/config/*.tmp
cloud/db cloud/db
cloud/data cloud/data
cloud/config cloud/config

View File

@@ -41,6 +41,8 @@
- **BitTorrent клиент Transmission** с веб-интерфейсом - **BitTorrent клиент Transmission** с веб-интерфейсом
- **SSH туннелирование через AutoSSH** - **SSH туннелирование через AutoSSH**
- Можно пробросить порт на удалённый VPS, автоматически переподключается при потере соединения - Можно пробросить порт на удалённый VPS, автоматически переподключается при потере соединения
- **VPN-сервер Xray (VLESS + Reality)** для безопасного доступа к домашней сети извне
- Трафик маскируется под обычный HTTPS, не требует отдельного домена и сертификата
- **Nginx reverse proxy** для маршрутизации трафика к сервисам - **Nginx reverse proxy** для маршрутизации трафика к сервисам
- Автоматическое получение и обновление SSL сертификатов через Let's Encrypt - Автоматическое получение и обновление SSL сертификатов через Let's Encrypt
- Автоматическое обновление DNS записей у регистратора [рег.ру](https://www.reg.ru) - Автоматическое обновление DNS записей у регистратора [рег.ру](https://www.reg.ru)
@@ -121,3 +123,7 @@
#### NextCloud #### NextCloud
![NextCloud Interface](./screenshots/nextcloud.png) ![NextCloud Interface](./screenshots/nextcloud.png)
### Xray VPN
Сервис не имеет веб-интерфейса. После запуска `xray/setup.sh` выведет ссылку `vless://...` для подключения из клиента (v2rayN, NekoBox, Hiddify, sing-box и т.п.). Порт по умолчанию — `2053`.

View File

@@ -255,3 +255,87 @@ vim default.conf
podman-compose -f init-compose.yml down podman-compose -f init-compose.yml down
podman-compose up -d --build podman-compose up -d --build
``` ```
---
## 1️⃣3️⃣ Xray — VPN-доступ к домашней сети (VLESS + Reality)
> VLESS + Reality маскирует трафик под обычный HTTPS. Отдельный домен и SSL-сертификат не нужны, а порт можно пробросить снаружи. Подходит для доступа к домашней сети и как личный VPN.
```bash
cd xray
cp .env.example .env && vim .env
chmod +x setup.sh
./setup.sh
podman-compose up -d
```
`setup.sh` автоматически:
- генерирует UUID, ключевую пару X25519 и Short ID;
- создаёт `config/config.json` из шаблона;
- проверяет конфигурацию через `xray run -test`;
- выводит готовую ссылку `vless://...` для клиента.
> ⚠️ Файлы `.env` и `config/config.json` содержат секреты и исключены из Git. Ссылка подключения тоже является секретом — не публикуйте её.
> В конфигурации отключён XTLS Vision (`flow` не задаётся), чтобы работал UDP — это нужно для игр и Minecraft Bedrock. REALITY/TLS продолжают шифровать трафик. Если UDP не нужен и важна максимальная защита от TLS-in-TLS DPI, Vision можно вернуть, добавив `"flow": "xtls-rprx-vision"` в клиент и `flow=xtls-rprx-vision` в ссылку.
> После изменения `XRAY_PORT`, `XRAY_DEST` или `XRAY_SERVER_NAME` в `.env` запустите `./setup.sh` заново — он пересоберёт `config/config.json` и проверит его. Секреты при этом сохраняются.
### Проброс порта
В OPNsense: **Межсетевой экран > NAT > Переадресация портов**
- **Interface:** WAN
- **Protocol:** TCP
- **Destination:** WAN address
- **Destination port range:** `2053` (или ваш `XRAY_PORT`)
- **Redirect target IP:** IP вашего сервера с Podman
- **Redirect target port:** `2053` (или ваш `XRAY_PORT`)
- **Filter rule association:** Add associated filter rule
> В rootless Podman нельзя слушать порты ниже 1024, поэтому по умолчанию используется `2053`. Не занимайте `8443` — он уже используется nginx-proxy. При необходимости внешний порт можно сделать другим, но тогда клиент должен подключаться именно к внешнему порту.
> Предупреждение Xray о порте не-443 и выбранном `dest` относится к блокировкам GFW (Китай). Для домашнего сервера в России это не критично, но `dest` лучше выбирать из доступных вам крупных сайтов с TLS 1.3.
Если нужен стабильный адрес вместо IP, добавьте поддомен `vpn` в `proxy/ddns/domains.txt` и укажите в `.env`:
```ini
XRAY_PUBLIC_HOST=vpn.domain.ru
```
### Доступ к домашней сети
Сервер Xray не навязывает маршруты клиенту — маршрутизацию настраиваем в клиенте:
- **Только домашняя сеть:** оставьте интернет напрямую, а подсеть `192.168.0.0/16` (или `geoip:private`) направьте в proxy.
- **Полный туннель:** весь трафик (`0.0.0.0/0`) направьте в proxy.
> Xray — это proxy, а не L2 VPN. Broadcast/multicast (обнаружение устройств и LAN-серверов в играх) не переносится. Для игр используйте прямое подключение по IP; для полноценного LAN-обнаружения нужен ZeroTier/SoftEther.
Для доступа по именам добавьте локальные записи в клиент или используйте DNS домашней сети (например, AdGuard/OPNsense), направив DNS-запросы через туннель.
### Добавление устройства
Для каждого устройства лучше использовать отдельный UUID. Добавьте свой объект в `clients` внутри `config/config.json`:
```json
{
"id": "новый-uuid"
}
```
После изменения перезапустите контейнер:
```bash
podman-compose restart xray
```
Сгенерировать новый UUID можно командой:
```bash
podman run --rm ghcr.io/xtls/xray-core:latest uuid
```
Ссылка для нового устройства строится по той же схеме, что вывел `setup.sh`: меняется только UUID в начале ссылки.

View File

@@ -1,13 +0,0 @@
NEXTCLOUD_DATABASE=nextcloud-db
DATABASE_USER=nextcloud
DATABASE_PASSWORD=password
DATABASE_ROOT_PASSWORD=another_password
PATH_TO_FOLDER=/path/to/folder/
NEXTCLOUD_ADMIN=admin
NEXTCLOUD_PASSWORD=password
NEXTCLOUD_DOMAIN=cloud.domain.ru
COLLABORA_ADMIN=admin
COLLABORA_PASSWORD=password
COLLABORA_DOMAIN=office.domain.ru

View File

@@ -1,71 +0,0 @@
services:
nextcloud-db:
image: docker.io/library/mariadb:11
restart: always
command: --transaction-isolation=READ-COMMITTED --binlog-format=ROW
container_name: nextcloud-db
volumes:
- ./db:/var/lib/mysql:z
environment:
- MARIADB_DATABASE=${NEXTCLOUD_DATABASE:?}
- MARIADB_USER=${DATABASE_USER:?}
- MARIADB_PASSWORD=${DATABASE_PASSWORD:?}
- MARIADB_ROOT_PASSWORD=${DATABASE_ROOT_PASSWORD:?}
networks:
- private_cloud_network
nextcloud-redis:
image: docker.io/library/redis:alpine
restart: always
container_name: nextcloud-redis
networks:
- private_cloud_network
nextcloud:
image: docker.io/library/nextcloud:latest
restart: always
container_name: nextcloud
volumes:
- ${PATH_TO_FOLDER:?}:/var/www/external_storage:z
- ./data:/var/www/html/data:z
- ./config:/var/www/html/config:z
- ./apps:/var/www/html/custom_apps:z
#environment:
# - MYSQL_PASSWORD=${DATABASE_PASSWORD:?}
# - MYSQL_DATABASE=${NEXTCLOUD_DATABASE:?}
# - MYSQL_USER=${DATABASE_USER:?}
# - MYSQL_HOST=nextcloud-db
# - REDIS_HOST=nextcloud-redis
#
# - NEXTCLOUD_ADMIN_USER=${NEXTCLOUD_ADMIN:?}
# - NEXTCLOUD_ADMIN_PASSWORD=${NEXTCLOUD_PASSWORD:?}
# - NEXTCLOUD_TRUSTED_DOMAINS=${NEXTCLOUD_DOMAIN:?}
depends_on:
- nextcloud-db
- nextcloud-redis
networks:
- cloud_network
- private_cloud_network
nextcloud-collabora:
image: docker.io/collabora/code:latest
container_name: nextcloud-collabora
restart: always
environment:
- aliasgroup1=https://${NEXTCLOUD_DOMAIN:?}
- DONT_GEN_SSL_CERT=true
- server_name=${COLLABORA_DOMAIN:?}
- extra_params=--o:ssl.enable=false --o:ssl.termination=true --o:net.post_allow.host[0]=.*
- username=${COLLABORA_ADMIN:?}
- password=${COLLABORA_PASSWORD:?}
networks:
- cloud_network
- private_cloud_network
networks:
cloud_network:
name: cloud_network
driver: bridge
private_cloud_network:
name: private_cloud_network
driver: bridge

View File

@@ -6,3 +6,5 @@ cloud
matrix matrix
chat chat
todo todo
download
vpn

View File

@@ -27,7 +27,9 @@ services:
- matrix_network - matrix_network
- memos_network - memos_network
- homepage_network - homepage_network
- cloud_network #- cloud_network
#- rss_network
- cashflow_network
certbot: certbot:
image: docker.io/certbot/certbot image: docker.io/certbot/certbot
@@ -86,6 +88,12 @@ networks:
homepage_network: homepage_network:
external: true external: true
name: homepage_network name: homepage_network
cloud_network: #cloud_network:
# external: true
# name: cloud_network
#rss_network:
# external: true
# name: rss_network
cashflow_network:
external: true external: true
name: cloud_network name: cashflow_network

Binary file not shown.

20
xray/.env.example Normal file
View File

@@ -0,0 +1,20 @@
# Порт Xray на хосте и в контейнере (для rootless Podman должен быть > 1024).
# По умолчанию 2053, потому что 443/8443 уже заняты nginx-proxy.
XRAY_PORT=2053
# Уровень логирования Xray: debug, info, warning, error
XRAY_LOG_LEVEL=warning
# Маскировочный сайт (dest) и SNI. Должны совпадать.
# Выбирайте крупный сайт с TLS 1.3, доступный с вашего сервера.
XRAY_DEST=www.cloudflare.com:443
XRAY_SERVER_NAME=www.cloudflare.com
# Внешний адрес сервера для ссылки подключения: белый IP или домен (без https:// и без порта)
XRAY_PUBLIC_HOST=vpn.example.com
# Генерируются автоматически командой ./setup.sh — не редактируйте вручную
XRAY_UUID=
XRAY_PRIVATE_KEY=
XRAY_PUBLIC_KEY=
XRAY_SHORT_ID=

View File

@@ -0,0 +1,69 @@
{
"log": {
"loglevel": "__LOG_LEVEL__",
"access": "none"
},
"inbounds": [
{
"tag": "vless-reality-in",
"listen": "0.0.0.0",
"port": __PORT__,
"protocol": "vless",
"settings": {
"clients": [
{
"id": "__UUID__"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "tcp",
"security": "reality",
"realitySettings": {
"show": false,
"dest": "__DEST__",
"xver": 0,
"serverNames": [
"__SERVER_NAME__"
],
"privateKey": "__PRIVATE_KEY__",
"shortIds": [
"__SHORT_ID__"
]
}
},
"sniffing": {
"enabled": true,
"destOverride": [
"http",
"tls",
"quic"
],
"routeOnly": true
}
}
],
"outbounds": [
{
"tag": "direct",
"protocol": "freedom"
},
{
"tag": "block",
"protocol": "blackhole"
}
],
"routing": {
"domainStrategy": "AsIs",
"rules": [
{
"type": "field",
"ip": [
"geoip:private"
],
"outboundTag": "direct"
}
]
}
}

27
xray/docker-compose.yml Normal file
View File

@@ -0,0 +1,27 @@
services:
xray:
image: ghcr.io/xtls/xray-core:latest
container_name: xray
restart: always
ports:
# Порт должен совпадать с XRAY_PORT в config/config.json (генерируется setup.sh)
- "${XRAY_PORT:?}:${XRAY_PORT:?}/tcp"
volumes:
# Монтируем каталог целиком, чтобы не использовать конфиги по умолчанию из образа
- ./config:/usr/local/etc/xray:ro,Z
environment:
- TZ=${TZ:-Europe/Samara}
deploy:
resources:
limits:
cpus: '0.5'
memory: 128M
reservations:
memory: 64M
networks:
- xray_network
networks:
xray_network:
name: xray_network
driver: bridge

128
xray/setup.sh Normal file
View File

@@ -0,0 +1,128 @@
#!/bin/sh
set -eu
cd "$(dirname "$0")"
ENGINE="${CONTAINER_ENGINE:-podman}"
IMAGE="${XRAY_IMAGE:-ghcr.io/xtls/xray-core:latest}"
if ! command -v "$ENGINE" >/dev/null 2>&1; then
echo "Ошибка: не найден $ENGINE. Установите Podman или задайте CONTAINER_ENGINE=docker." >&2
exit 1
fi
if [ ! -f .env ]; then
cp .env.example .env
echo "Создан .env из .env.example"
fi
# Убираем CRLF, если файл редактировали в Windows
sed -i 's/\r$//' .env 2>/dev/null || true
# shellcheck disable=SC1091
. ./.env
: "${XRAY_PORT:?Укажите XRAY_PORT в .env}"
: "${XRAY_DEST:?Укажите XRAY_DEST в .env}"
: "${XRAY_SERVER_NAME:?Укажите XRAY_SERVER_NAME в .env}"
LOG_LEVEL="${XRAY_LOG_LEVEL:-warning}"
PUBLIC_HOST="${XRAY_PUBLIC_HOST:-YOUR_SERVER_IP}"
set_env() {
key="$1"
value="$2"
awk -v k="$key" -v v="$value" '
BEGIN { FS = "="; OFS = "=" }
$1 == k { print k, v; next }
{ print }
' .env > .env.tmp
mv .env.tmp .env
}
gen_uuid() {
out="$("$ENGINE" run --rm "$IMAGE" uuid)" || return 1
printf '%s' "$out" | tr -d '\r\n'
}
gen_x25519() {
out="$("$ENGINE" run --rm "$IMAGE" x25519)" || return 1
printf '%s' "$out" | tr -d '\r'
}
gen_short_id() {
if command -v openssl >/dev/null 2>&1; then
openssl rand -hex 8 | tr -d '\r\n'
else
od -An -N8 -tx1 /dev/urandom | tr -d ' \n'
fi
}
if [ -z "${XRAY_UUID:-}" ]; then
echo "Генерация UUID..."
XRAY_UUID="$(gen_uuid)"
set_env XRAY_UUID "$XRAY_UUID"
fi
if [ -z "${XRAY_PRIVATE_KEY:-}" ]; then
echo "Генерация ключевой пары X25519..."
KEYS="$(gen_x25519)"
XRAY_PRIVATE_KEY="$(printf '%s\n' "$KEYS" | sed -n -E 's/^(PrivateKey|Private key):[[:space:]]*//p' | head -n 1)"
XRAY_PUBLIC_KEY="$(printf '%s\n' "$KEYS" | sed -n -E 's/^(Password \(PublicKey\)|PublicKey|Public key):[[:space:]]*//p' | head -n 1)"
if [ -z "$XRAY_PRIVATE_KEY" ] || [ -z "$XRAY_PUBLIC_KEY" ]; then
echo "Не удалось разобрать вывод xray x25519:" >&2
printf '%s\n' "$KEYS" >&2
exit 1
fi
set_env XRAY_PRIVATE_KEY "$XRAY_PRIVATE_KEY"
set_env XRAY_PUBLIC_KEY "$XRAY_PUBLIC_KEY"
elif [ -z "${XRAY_PUBLIC_KEY:-}" ]; then
echo "Вычисление публичного ключа..."
KEYS="$("$ENGINE" run --rm "$IMAGE" x25519 -i "$XRAY_PRIVATE_KEY")" || {
echo "Не удалось вычислить публичный ключ из XRAY_PRIVATE_KEY." >&2
exit 1
}
KEYS="$(printf '%s' "$KEYS" | tr -d '\r')"
XRAY_PUBLIC_KEY="$(printf '%s\n' "$KEYS" | sed -n -E 's/^(Password \(PublicKey\)|PublicKey|Public key):[[:space:]]*//p' | head -n 1)"
if [ -z "$XRAY_PUBLIC_KEY" ]; then
echo "Не удалось вычислить публичный ключ из XRAY_PRIVATE_KEY." >&2
exit 1
fi
set_env XRAY_PUBLIC_KEY "$XRAY_PUBLIC_KEY"
fi
if [ -z "${XRAY_SHORT_ID:-}" ]; then
echo "Генерация Short ID..."
XRAY_SHORT_ID="$(gen_short_id)"
set_env XRAY_SHORT_ID "$XRAY_SHORT_ID"
fi
mkdir -p config
sed \
-e "s|__LOG_LEVEL__|$LOG_LEVEL|g" \
-e "s|__PORT__|$XRAY_PORT|g" \
-e "s|__UUID__|$XRAY_UUID|g" \
-e "s|__DEST__|$XRAY_DEST|g" \
-e "s|__SERVER_NAME__|$XRAY_SERVER_NAME|g" \
-e "s|__PRIVATE_KEY__|$XRAY_PRIVATE_KEY|g" \
-e "s|__SHORT_ID__|$XRAY_SHORT_ID|g" \
config/config.json.example > config/config.json.tmp
mv config/config.json.tmp config/config.json
echo "Проверка конфигурации..."
"$ENGINE" run --rm -v "$(pwd)/config:/usr/local/etc/xray:ro,Z" "$IMAGE" run -test -confdir /usr/local/etc/xray
LINK="vless://${XRAY_UUID}@${PUBLIC_HOST}:${XRAY_PORT}?encryption=none&security=reality&sni=${XRAY_SERVER_NAME}&fp=chrome&pbk=${XRAY_PUBLIC_KEY}&sid=${XRAY_SHORT_ID}&type=tcp&headerType=none&spx=%2F#Xray-Home"
cat <<EOF
Готово.
Конфиг: config/config.json
Публичный ключ: $XRAY_PUBLIC_KEY
Short ID: $XRAY_SHORT_ID
Ссылка для клиента (замените $PUBLIC_HOST на реальный адрес, если нужно):
$LINK
Не публикуйте эту ссылку: она даёт доступ к вашему серверу.
EOF