first add

This commit is contained in:
mm644706215
2025-09-30 20:45:51 +08:00
commit 2c10b421b4
15 changed files with 625 additions and 0 deletions

View File

@@ -0,0 +1,36 @@
# ========= Stage 1: (可选)你的前端静态资源构建 =========
FROM node:lts-alpine AS ui-builder
WORKDIR /src
COPY headscale-ui/package*.json ./
RUN npm ci
COPY headscale-ui/ .
RUN npm run build
# ========= Stage 2: 用 Go 1.25 (Alpine) 构建 Caddy + 插件 =========
FROM golang:1.25.0-alpine3.22 AS caddy-builder
RUN echo "https://mirrors.aliyun.com/alpine/v3.22/main" > /etc/apk/repositories && \
echo "https://mirrors.aliyun.com/alpine/v3.22/community" >> /etc/apk/repositories && \
apk update && \
apk add --no-cache git build-base ca-certificates
ENV GOPROXY=https://goproxy.cn,direct
ENV GOTOOLCHAIN=local
RUN go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
# 用 xcaddy 构建 (保持与用户提供版本一致/可调整)
RUN /go/bin/xcaddy build v2.10.1 \
--output /usr/local/bin/caddy \
--with github.com/caddy-dns/alidns@v1.0.26 \
--with github.com/caddy-dns/cloudflare@v0.2.1 \
--with github.com/mholt/caddy-l4
# ========= Stage 3: 运行时镜像Alpine =========
FROM caddy:2.10.0-alpine
COPY --from=caddy-builder /usr/local/bin/caddy /usr/bin/caddy
COPY --from=ui-builder /src/build /usr/src/www
RUN apk update && \
apk add --no-cache bind-tools netcat-openbsd jq curl && \
mkdir -p /data/log/headscale

54
server/caddy/Caddyfile Normal file
View File

@@ -0,0 +1,54 @@
{
email {env.ACME_EMAIL}
# 如果你想强制使用 Cloudflare/阿里云 DNS 验证,可在每个站点 tls 块中指定 `dns`。
}
# 主站点HTTPS 复用 443/ray 提供 VLESS-WS 反代)
molecular.eu.org {
log {
output stdout
format console
}
# 若你需要 http->https 强制跳转,可加:
# redir https://<YOUR_DOMAIN>{uri} permanent
encode zstd gzip
@vless_ws path /ray /ray/*
handle @vless_ws {
reverse_proxy v2ray:10000 {
header_up -Origin
}
}
# VLESS gRPC over h2c at /grpc
@vless_grpc path /grpc /grpc/*
handle @vless_grpc {
reverse_proxy {
transport http {
versions h2c
}
to v2ray:10001
}
}
# 健康检查
handle_path /_health* {
respond 200
}
# 静态站点或你的网站反代
handle {
root * /usr/src/www
file_server
}
# 证书:可根据你的 DNS 服务商二选一(或只保留其一)
tls {
dns cloudflare {env.CF_API_TOKEN}
#dns alidns {
# access_key_id {env.ALIYUN_ACCESS_KEY_ID}
# access_key_secret {env.ALIYUN_ACCESS_KEY_SECRET}
#}
}
}

10
server/caddy/env/caddy.env vendored Normal file
View File

@@ -0,0 +1,10 @@
# ===== Caddy / ACME / DNS (Cloudflare) =====
# 用于在 Let's Encrypt/ZeroSSL 注册证书的邮箱
ACME_EMAIL=pylyzeng@gmail.com
# 你的站点域名(当前 Caddyfile 已直接写死为 molecular.eu.org此变量仅作记录
DOMAIN=molecular.eu.org
# Cloudflare API Token至少授予Zone:Read 与 Zone:DNS:Edit
# 在 Caddyfile 中通过 {env.CF_API_TOKEN} 使用
CF_API_TOKEN=ofvUY4Wo9-VN__AMglXHf8fVM1xtBRFfGN_Bsd-C

14
server/caddy/env/caddy.env.example vendored Normal file
View File

@@ -0,0 +1,14 @@
# ===== Caddy / ACME / DNS =====
ACME_EMAIL=<ACME_EMAIL>
DOMAIN=<YOUR_DOMAIN>
# Cloudflare (可选其一)
CLOUDFLARE_API_TOKEN=<CLOUDFLARE_API_TOKEN>
CF_API_TOKEN=<CLOUDFLARE_API_TOKEN>
# 阿里云 (可选其一)
ALIYUN_ACCESS_KEY_ID=<ALIYUN_ACCESS_KEY_ID>
ALIYUN_ACCESS_KEY_SECRET=<ALIYUN_ACCESS_KEY_SECRET>
# (L4 模式可选) 指定需要直达 L4 转发的 SNI
DERPER_HOST=<DERPER_HOSTNAME>

73
server/caddy/l4.json Normal file
View File

@@ -0,0 +1,73 @@
{
"apps": {
"layer4": {
"servers": {
"tcp80": {
"listen": [
"tcp/:80"
],
"routes": [
{
"handle": [
{
"handler": "proxy",
"upstreams": [
{
"dial": [
"caddy-http:80"
]
}
]
}
]
}
]
},
"tcp443": {
"listen": [
"tcp/:443"
],
"routes": [
{
"match": [
{
"tls": {
"sni": [
"<DERPER_HOSTNAME>"
]
}
}
],
"handle": [
{
"handler": "proxy",
"upstreams": [
{
"dial": [
"derper:443"
]
}
]
}
]
},
{
"handle": [
{
"handler": "proxy",
"upstreams": [
{
"dial": [
"caddy-http:8443"
]
}
]
}
]
}
]
}
}
}
}
}

View File

@@ -0,0 +1,3 @@
<!doctype html>
<html><head><meta charset="utf-8"><title>OK</title></head>
<body><h1>It works.</h1><p>HTTPS is up. VLESS-WS is on <code>/ray</code>.</p></body></html>

View File

@@ -0,0 +1,49 @@
version: "3.9"
services:
caddy-l4:
image: caddy-l4:latest
build:
context: .
dockerfile: Dockerfile.caddy-l4
container_name: caddy-l4
restart: unless-stopped
ports:
- "80:80/tcp"
- "443:443/tcp"
command: ["caddy","run","--config","/etc/caddy/caddy.json"]
env_file:
- ./caddy/env/caddy.env
volumes:
- ./caddy/l4.json:/etc/caddy/caddy.json:ro
- ./caddy/log:/data/log
depends_on:
- caddy-http
caddy-http:
image: caddy-l4:latest
container_name: caddy-http
restart: unless-stopped
expose:
- "8443/tcp"
- "80/tcp"
environment:
- CADDY_LISTEN_HTTPS=:8443
- CADDY_LISTEN_HTTP=:80
- CADDY_ADMIN=:2019
env_file:
- ./caddy/env/caddy.env
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy/site:/usr/src/www:ro
- ./caddy/ssl:/data/caddy/certificates
- ./caddy/log:/data/log
depends_on:
- v2ray
v2ray:
image: v2fly/v2fly-core:latest
container_name: v2ray
restart: unless-stopped
volumes:
- ./v2ray/config.json:/etc/v2ray/config.json:ro

32
server/docker-compose.yml Normal file
View File

@@ -0,0 +1,32 @@
version: "3.9"
services:
caddy-http:
image: caddy-l4:latest
build:
context: .
dockerfile: Dockerfile.caddy-l4
container_name: caddy-http
restart: unless-stopped
ports:
- "80:80/tcp"
- "443:443/tcp"
environment:
- CADDY_ADMIN=:2019
env_file:
- ./caddy/env/caddy.env
volumes:
- ./caddy/Caddyfile:/etc/caddy/Caddyfile:ro
- ./caddy/site:/usr/src/www:ro
- ./caddy/ssl:/data/caddy/certificates
- ./caddy/log:/data/log
depends_on:
- v2ray
v2ray:
image: v2fly/v2fly-core:latest
container_name: v2ray
restart: unless-stopped
volumes:
- ./v2ray/config.json:/etc/v2ray/config.json:ro
command: ["run", "-c", "/etc/v2ray/config.json"]

52
server/v2ray/config.json Normal file
View File

@@ -0,0 +1,52 @@
{
"log": {
"loglevel": "warning"
},
"inbounds": [
{
"port": 10000,
"listen": "0.0.0.0",
"protocol": "vless",
"settings": {
"clients": [
{
"id": "8f5e3b57-5a3d-4a3e-9f3c-9c6c2d6a9f1e"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "ws",
"wsSettings": {
"path": "/ray"
}
}
},
{
"port": 10001,
"listen": "0.0.0.0",
"protocol": "vless",
"settings": {
"clients": [
{
"id": "8f5e3b57-5a3d-4a3e-9f3c-9c6c2d6a9f1e"
}
],
"decryption": "none"
},
"streamSettings": {
"network": "grpc",
"grpcSettings": {
"serviceName": "grpc",
"multiMode": true
}
}
}
],
"outbounds": [
{
"protocol": "freedom",
"settings": {}
}
]
}