Author SHA1 Message Date
kislovdm 5442723e12 README
Docker Hub / build-and-push (push) Failing after 14s
2026-06-22 09:39:23 +03:00
Your Name b44b57f1a4 Merge branch 'master' of github.com:monster1025/wg2outline
Docker Hub / build-and-push (push) Failing after 15s
2026-06-10 17:15:29 +00:00
Your Name 3ef1f5ca07 migrate to one service 2026-06-10 17:15:11 +00:00
9 changed files with 183 additions and 58 deletions
+5 -17
View File
@@ -2,29 +2,17 @@ FROM golang:1.24-bookworm AS builder
RUN apt-get update && apt-get install -y git RUN apt-get update && apt-get install -y git
# Клонируем репозиторий и собираем приложение
WORKDIR /outline-sdk WORKDIR /outline-sdk
RUN git clone https://github.com/OutlineFoundation/outline-sdk.git . RUN git clone https://github.com/OutlineFoundation/outline-sdk.git .
WORKDIR /outline-sdk/x/examples/outline-cli WORKDIR /outline-sdk/x/examples/outline-cli
RUN CGO_ENABLED=1 go build -o /outline-cli -ldflags="-extldflags=-static" . RUN CGO_ENABLED=1 go build -o /outline-cli -ldflags="-extldflags=-static" .
FROM debian:bookworm FROM lscr.io/linuxserver/wireguard:latest
RUN apt-get update && apt-get install -y curl wget
# Устанавливаем необходимые утилиты для работы сети и создаем пустой resolv.conf.head
RUN apt-get update && apt-get install -y iproute2 iptables && \
rm -rf /var/lib/apt/lists/* && \
touch /etc/resolv.conf.head
# Копируем собранный бинарный файл из этапа сборки
COPY --from=builder /outline-cli /usr/local/bin/outline-cli COPY --from=builder /outline-cli /usr/local/bin/outline-cli
COPY root/ /
# Обертка-энтрипоинт: готовит DNS и запускает outline-cli RUN chmod +x /usr/local/bin/outline-cli \
COPY entrypoint.sh /usr/local/bin/outline-entrypoint.sh /custom-cont-init.d/* \
/etc/s6-overlay/s6-rc.d/svc-outline/run
# Делаем бинарный файл исполняемым
RUN chmod +x /usr/local/bin/outline-cli /usr/local/bin/outline-entrypoint.sh
# Точка входа
ENTRYPOINT ["/usr/local/bin/outline-entrypoint.sh"]
+76
View File
@@ -1 +1,77 @@
# wg2outline # wg2outline
WireGuard поверх Outline — Docker-образ, который поднимает WireGuard VPN-сервер и маршрутизирует весь его трафик через Outline (Shadowsocks) туннель.
## Как это работает
[WireGuard Peer] --UDP:51820--> [wg2outline: WireGuard + Outline CLI] --Shadowsocks--> [Outline Server] -- Internet
Outline CLI создаёт зашифрованный Shadowsocks-туннель, через который проходит весь трафик WireGuard-клиентов. Это позволяет обходить блокировки WireGuard'а — Outline трафик выглядит как обычный HTTPS.
## Требования
- Docker + Docker Compose
- Сервер с публичным IP
- Ключ доступа Outline (строка вида `ss://...` из Outline Manager)
- WireGuard модуль ядра (или встроенная поддержка)
## Быстрый старт
```bash
cp .env.sample .env
# Отредактируйте .env:
# OUTLINE_TRANSPORT — ключ доступа Outline
# SERVERURL — публичный IP вашего сервера
# PEERS — количество клиентских конфигов
docker compose up -d
```
Конфигурационные файлы WireGuard для клиентов появятся в `./wireguard-config/peer1/`, `peer2/` и т.д.
## Переменные окружения
| Переменная | Описание |
|---|---|
| `OUTLINE_TRANSPORT` | Shadowsocks access URL от Outline сервера |
| `SERVERURL` | Публичный IP/DNS сервера |
| `PEERS` | Количество генерируемых клиентских конфигов (по умолч. 1) |
| `INTERNAL_SUBNET` | Подсеть WireGuard (по умолч. `10.13.13.0`) |
## Тестирование
```bash
./test-wg-client.sh ./wireguard-config/peer1/peer1.conf
```
Скрипт поднимает временный Alpine-контейнер, подключается к VPN и проверяет доступность внешних ресурсов через Outline.
## Сборка
```bash
docker compose build
```
Или вручную:
```bash
docker build -t wg2outline .
```
## Cтруктура проекта
```
├── Dockerfile # Мульти-стадийная сборка (outline-cli + linuxserver/wireguard)
├── docker-compose.yml # Docker Compose конфигурация
├── entrypoint.sh # Альтернативный entrypoint (без s6-overlay)
├── root/
│ ├── custom-cont-init.d/ # Инициализация (фикс DNS)
│ └── etc/s6-overlay/ # s6 сервисы (svc-outline, svc-wireguard)
├── wireguard-config/
│ └── templates/ # Шаблоны конфигов WireGuard
├── test-wg-client.sh # Интеграционный тест
└── .env.sample # Пример переменных окружения
```
## CI/CD
При пуше в `main` или создании тега `v*` GitHub Actions автоматически собирает образ и публикует его на Docker Hub.
+13 -41
View File
@@ -1,19 +1,27 @@
services: services:
outline-client: wg2outline:
build: . build: .
container_name: outline-client container_name: wg2outline
privileged: true privileged: true
# WireGuard UDP port is published from this service because
# the wireguard container shares this network namespace.
ports: ports:
- "51821:51820/udp" - "51821:51820/udp"
environment: environment:
# Replace with your Outline access key.
OUTLINE_TRANSPORT: "${OUTLINE_TRANSPORT}" OUTLINE_TRANSPORT: "${OUTLINE_TRANSPORT}"
INTERNAL_SUBNET: "${INTERNAL_SUBNET}" INTERNAL_SUBNET: "${INTERNAL_SUBNET}"
PUID: 1000
PGID: 1000
TZ: Etc/UTC
SERVERURL: ${SERVERURL}
SERVERPORT: 51821
PEERS: ${PEERS:-1}
PEERDNS: 1.1.1.1
PERSISTENTKEEPALIVE_PEERS: 25
MTU: 1280
LOG_CONFS: true
cap_add: cap_add:
- NET_ADMIN - NET_ADMIN
- SYS_ADMIN - SYS_ADMIN
- SYS_MODULE
devices: devices:
- /dev/net/tun:/dev/net/tun - /dev/net/tun:/dev/net/tun
security_opt: security_opt:
@@ -26,45 +34,9 @@ services:
- net.ipv6.conf.all.forwarding=0 - net.ipv6.conf.all.forwarding=0
- net.ipv6.conf.all.disable_ipv6=1 - net.ipv6.conf.all.disable_ipv6=1
- net.ipv6.conf.default.disable_ipv6=1 - net.ipv6.conf.default.disable_ipv6=1
restart: unless-stopped
logging:
driver: json-file
options:
max-size: "50m"
max-file: "5"
wireguard:
image: lscr.io/linuxserver/wireguard:latest
container_name: wg2outline
depends_on:
- outline-client
# Important: share network stack with outline-client so all
# traffic from WG peers leaves through Outline routing/policy.
network_mode: "service:outline-client"
cap_add:
- NET_ADMIN
- SYS_MODULE
environment:
- PUID=1000
- PGID=1000
- TZ=Etc/UTC
# Public IP or domain of this server for peers.
- SERVERURL=${SERVERURL}
- SERVERPORT=51821
- PEERS=${PEERS:-1}
- PEERDNS=1.1.1.1
- INTERNAL_SUBNET=${INTERNAL_SUBNET}
# Keepalive helps on NAT/mobile networks.
- PERSISTENTKEEPALIVE_PEERS=25
# Optional for enterprise networks that block MTU/fragmentation.
- MTU=1280
# Firewall/NAT is handled by the host/namespace setup.
- LOG_CONFS=true
volumes: volumes:
- ./wireguard-config:/config - ./wireguard-config:/config
- /lib/modules:/lib/modules:ro - /lib/modules:/lib/modules:ro
sysctls:
- net.ipv4.conf.all.src_valid_mark=1
restart: unless-stopped restart: unless-stopped
logging: logging:
driver: json-file driver: json-file
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/with-contenv bash
# Some distros mount /etc/resolv.conf as a file from the host.
# Ensure DNS works reliably inside the VPN network namespace.
umount /etc/resolv.conf 2>/dev/null || true
[ -f /etc/resolv.conf ] || printf 'nameserver 1.1.1.1\n' > /etc/resolv.conf
@@ -0,0 +1,82 @@
#!/usr/bin/with-contenv bash
set -eu
if [ -z "${OUTLINE_TRANSPORT:-}" ]; then
echo "ERROR: OUTLINE_TRANSPORT is required (ss://...)" >&2
exit 1
fi
if [ -z "${INTERNAL_SUBNET:-}" ]; then
echo "ERROR: INTERNAL_SUBNET is required (e.g. 10.13.13.0)" >&2
exit 1
fi
WG_FWMARK=0x51820
WG_SUBNET="${INTERNAL_SUBNET}/24"
apply_bypass_rules() {
# WireGuard UDP replies go to the peer's public IP, not the WG subnet.
# Mark and bypass Outline policy routing for those packets.
ip rule add pref 50 fwmark "${WG_FWMARK}" lookup main 2>/dev/null || true
# Inner IP traffic to WG peers must use the main table (replies via wg0).
ip rule add pref 100 to "${WG_SUBNET}" lookup main 2>/dev/null || true
# Docker/custom bridge subnets differ per host (e.g. 172.17.0.0/16, 10.200.0.0/16).
local pref=101
local cidr
for cidr in $(ip -4 route show proto kernel scope link 2>/dev/null | awk '$2 == "dev" { print $1 }'); do
ip rule add pref "${pref}" to "${cidr}" lookup main 2>/dev/null || true
pref=$((pref + 1))
done
}
setup_wg_mangle() {
local wg_port="$1"
if iptables -t mangle -C OUTPUT -p udp --sport "${wg_port}" -j MARK --set-mark "${WG_FWMARK}" 2>/dev/null; then
return 0
fi
iptables -t mangle -A OUTPUT -p udp --sport "${wg_port}" -j MARK --set-mark "${WG_FWMARK}"
}
wait_for_outline_routing() {
local i
for i in $(seq 1 30); do
if ip link show outline233 >/dev/null 2>&1 && ip rule show | grep -q 'lookup 233'; then
return 0
fi
sleep 1
done
echo "WARN: Outline routing not ready after 30s, applying bypass rules anyway" >&2
return 1
}
/usr/local/bin/outline-cli -transport "${OUTLINE_TRANSPORT}" &
OUTLINE_PID=$!
wait_for_outline_routing || true
apply_bypass_rules
setup_wg_mangle "${SERVERPORT:-51820}"
# wg0 starts after this service; refresh rules once the interface and listen port are known.
(
for _ in $(seq 1 60); do
if ip link show wg0 >/dev/null 2>&1; then
wg_port="$(wg show wg0 listen-port 2>/dev/null || echo "${SERVERPORT:-51820}")"
setup_wg_mangle "${wg_port}"
apply_bypass_rules
break
fi
sleep 2
done
) &
# Ensure exactly one NAT rule for traffic leaving via Outline interface.
if iptables -t nat -C POSTROUTING -o outline+ -j MASQUERADE 2>/dev/null; then
while iptables -t nat -C POSTROUTING -o outline+ -j MASQUERADE 2>/dev/null; do
iptables -t nat -D POSTROUTING -o outline+ -j MASQUERADE || true
done
fi
iptables -t nat -A POSTROUTING -o outline+ -j MASQUERADE
wait "$OUTLINE_PID"
@@ -0,0 +1 @@
longrun