Showing preview only (204K chars total). Download the full file or copy to clipboard to get everything.
Repository: hwdsl2/docker-ipsec-vpn-server
Branch: master
Commit: e67276c9d876
Files: 29
Total size: 194.5 KB
Directory structure:
gitextract_e54mtk_x/
├── .dockerignore
├── .github/
│ ├── ISSUE_TEMPLATE/
│ │ ├── 00-bug-report.md
│ │ ├── 10-bug-report-zh.md
│ │ ├── 20-enhancement-request.md
│ │ └── 30-enhancement-request-zh.md
│ └── workflows/
│ ├── buildx.yml
│ ├── check_update.yml
│ ├── cron-alpine.yml
│ ├── cron-debian.yml
│ ├── main-alpine.yml
│ ├── main-debian.yml
│ ├── shellcheck.yml
│ └── vpn_test.yml
├── .gitignore
├── Dockerfile
├── Dockerfile.debian
├── LICENSE.md
├── README-ru.md
├── README-zh-Hant.md
├── README-zh.md
├── README.md
├── docker-compose.yml
├── docs/
│ ├── advanced-usage-zh.md
│ ├── advanced-usage.md
│ ├── vpn-book-zh-Hant.md
│ ├── vpn-book-zh.md
│ └── vpn-book.md
├── run.sh
└── vpn.env.example
================================================
FILE CONTENTS
================================================
================================================
FILE: .dockerignore
================================================
.git
.github
.gitignore
docker-compose.yml
LICENSE.md
README.md
README-zh.md
vpn.env
vpn.env.example
================================================
FILE: .github/ISSUE_TEMPLATE/00-bug-report.md
================================================
---
name: Bug report
about: Tell us about a problem you are experiencing
title: ''
labels: ''
assignees: ''
---
**Checklist**
- [ ] I read the [README](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README.md)
- [ ] I read the [Important notes](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README.md#important-notes)
- [ ] I followed instructions to [configure VPN clients](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README.md#next-steps)
- [ ] I checked [IKEv1 troubleshooting](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#ikev1-troubleshooting), [IKEv2 troubleshooting](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto.md#ikev2-troubleshooting), [enabled logs](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/docs/advanced-usage.md#enable-libreswan-logs) and checked [VPN status](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#check-logs-and-vpn-status)
- [ ] I searched existing [Issues](https://github.com/hwdsl2/docker-ipsec-vpn-server/issues?q=is%3Aissue)
- [ ] This bug is about the IPsec VPN server Docker image, and not IPsec VPN itself
<!---
If you found a reproducible bug for the IPsec VPN, open a bug report at https://github.com/libreswan/libreswan. Ask VPN-related questions on the [Libreswan](https://lists.libreswan.org) or [strongSwan](https://lists.strongswan.org) users mailing list, or search e.g. [Stack Overflow](https://stackoverflow.com/questions/tagged/vpn).
--->
**Describe the issue**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. ...
2. ...
**Expected behavior**
A clear and concise description of what you expected to happen.
**Logs**
[Enable logs](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/docs/advanced-usage.md#enable-libreswan-logs), check [VPN status](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#check-logs-and-vpn-status), and add error logs to help explain the problem, if applicable.
**Server (please complete the following information)**
- Docker host OS: [e.g. Ubuntu 24.04]
- Hosting provider (if applicable): [e.g. GCP, AWS]
**Client (please complete the following information)**
- Device: [e.g. iPhone 12]
- OS: [e.g. iOS 15]
- VPN mode: [IPsec/L2TP, IPsec/XAuth ("Cisco IPsec") or IKEv2]
**Additional context**
Add any other context about the problem here.
================================================
FILE: .github/ISSUE_TEMPLATE/10-bug-report-zh.md
================================================
---
name: 错误报告
about: 请使用这个模板来提交 bug
title: ''
labels: ''
assignees: ''
---
**任务列表**
- [ ] 我已阅读[自述文件](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README-zh.md)
- [ ] 我已阅读[重要提示](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README-zh.md#重要提示)
- [ ] 我已按照说明[配置 VPN 客户端](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README-zh.md#下一步)
- [ ] 我检查了 [IKEv1 故障排除](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#ikev1-故障排除),[IKEv2 故障排除](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto-zh.md#ikev2-故障排除),[启用日志](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/docs/advanced-usage-zh.md#启用-libreswan-日志)并查看了 [VPN 状态](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#检查日志及-vpn-状态)
- [ ] 我搜索了已有的 [Issues](https://github.com/hwdsl2/docker-ipsec-vpn-server/issues?q=is%3Aissue)
- [ ] 这个 bug 是关于 IPsec VPN 服务器 Docker 镜像,而不是 IPsec VPN 本身
<!---
如果你发现了 IPsec VPN 的一个可重复的程序漏洞,请在 https://github.com/libreswan/libreswan 提交一个错误报告。VPN 的相关问题可在 [Libreswan](https://lists.libreswan.org) 或 [strongSwan](https://lists.strongswan.org) 用户邮件列表提问,或者搜索比如 [Stack Overflow](https://stackoverflow.com/questions/tagged/vpn) 等网站。
--->
**问题描述**
使用清楚简明的语言描述这个 bug。
**重现步骤**
重现该 bug 的步骤:
1. ...
2. ...
**期待的正确结果**
简要地描述你期望的正确结果。
**日志**
[启用日志](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/docs/advanced-usage-zh.md#启用-libreswan-日志),检查 [VPN 状态](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#检查日志及-vpn-状态),并且添加错误日志以帮助解释该问题(如果适用)。
**服务器信息(请填写以下信息)**
- Docker 主机操作系统: [比如 Ubuntu 24.04]
- 服务提供商(如果适用): [比如 GCP, AWS]
**客户端信息(请填写以下信息)**
- 设备: [比如 iPhone 12]
- 操作系统: [比如 iOS 15]
- VPN 模式: [IPsec/L2TP, IPsec/XAuth ("Cisco IPsec") 或 IKEv2]
**其它信息**
添加关于该 bug 的其它信息。
================================================
FILE: .github/ISSUE_TEMPLATE/20-enhancement-request.md
================================================
---
name: Enhancement request
about: Suggest an improvement for this project
title: ''
labels: ''
assignees: ''
---
**Checklist**
- [ ] I searched existing [Issues](https://github.com/hwdsl2/docker-ipsec-vpn-server/issues?q=is%3Aissue), and did not find a similar enhancement request
- [ ] This enhancement request is about the IPsec VPN server Docker image, and not IPsec VPN itself
- [ ] I read the [README](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README.md)
- [ ] I read the [Important notes](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README.md#important-notes)
- [ ] I followed instructions to [configure VPN clients](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README.md#next-steps)
- [ ] I checked [IKEv1 troubleshooting](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#ikev1-troubleshooting), [IKEv2 troubleshooting](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto.md#ikev2-troubleshooting), [enabled logs](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/docs/advanced-usage.md#enable-libreswan-logs) and checked [VPN status](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#check-logs-and-vpn-status)
**Describe the enhancement request**
A clear and concise description of your enhancement request.
**Is your enhancement request related to a problem? Please describe.**
(If applicable) A clear and concise description of what the problem is.
**Additional context**
Add any other context about the enhancement request here.
================================================
FILE: .github/ISSUE_TEMPLATE/30-enhancement-request-zh.md
================================================
---
name: 改进建议
about: 请使用这个模板来提交改进建议
title: ''
labels: ''
assignees: ''
---
**任务列表**
- [ ] 我搜索了已有的 [Issues](https://github.com/hwdsl2/docker-ipsec-vpn-server/issues?q=is%3Aissue),没有找到类似的改进建议
- [ ] 这个改进建议是关于 IPsec VPN 服务器 Docker 镜像,而不是 IPsec VPN 本身
- [ ] 我已阅读[自述文件](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README-zh.md)
- [ ] 我已阅读[重要提示](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README-zh.md#重要提示)
- [ ] 我已按照说明[配置 VPN 客户端](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/README-zh.md#下一步)
- [ ] 我检查了 [IKEv1 故障排除](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#ikev1-故障排除),[IKEv2 故障排除](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto-zh.md#ikev2-故障排除),[启用日志](https://github.com/hwdsl2/docker-ipsec-vpn-server/blob/master/docs/advanced-usage-zh.md#启用-libreswan-日志)并查看了 [VPN 状态](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#检查日志及-vpn-状态)
**描述改进建议**
使用清楚简明的语言描述你的改进建议。
**你的改进建议与遇到的问题有关吗?请描述。**
(如果适用)清楚,简洁地说明问题所在。
**其它信息**
添加关于该改进建议的其它信息。
================================================
FILE: .github/workflows/buildx.yml
================================================
#
# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
name: buildx
on:
workflow_call:
inputs:
os_type:
required: true
type: string
secrets:
CACHE_NAME:
required: true
DOCKER_TOKEN:
required: true
QUAY_USER:
required: true
QUAY_TOKEN:
required: true
BUILD_ONLY:
required: true
jobs:
buildx:
runs-on: ubuntu-24.04
if: github.repository_owner == 'hwdsl2'
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Cache
uses: actions/cache@v5
with:
path: |
${{ runner.temp }}/.buildx-bin
${{ runner.temp }}/.buildx-cache
${{ runner.temp }}/.docker-images
key: ${{ secrets.CACHE_NAME }}-${{ github.sha }}-${{ github.run_id }}
restore-keys: |
${{ secrets.CACHE_NAME }}-
- name: Set up Buildx
env:
RUNNER_TEMP: ${{ runner.temp }}
run: |
[ ! -x /usr/bin/docker ] && exit 1
if [ ! -x /usr/bin/wget ] || [ ! -x /usr/bin/jq ]; then
export DEBIAN_FRONTEND=noninteractive
sudo apt-get -yq update
sudo apt-get -yq install wget jq
fi
BUILDX_VER=$(wget -t 3 -T 30 -qO- "https://api.github.com/repos/docker/buildx/releases/latest" | jq -r ".tag_name")
[ -z "${BUILDX_VER}" ] && exit 1
echo "Buildx version: ${BUILDX_VER}"
DOCKER_DIR="${HOME}/.docker/cli-plugins"
CACHE_DIR="${RUNNER_TEMP}/.buildx-bin"
mkdir -p "${DOCKER_DIR}"
if [ -s "${CACHE_DIR}/docker-buildx-${BUILDX_VER}" ]; then
echo "Using buildx binary from cache..."
cp -f "${CACHE_DIR}/docker-buildx-${BUILDX_VER}" "${DOCKER_DIR}/docker-buildx"
else
echo "Downloading buildx..."
BUILDX_URL="https://github.com/docker/buildx/releases/download/${BUILDX_VER}/buildx-${BUILDX_VER}.linux-amd64"
wget -t 3 -T 30 -nv -O "${DOCKER_DIR}/docker-buildx" "${BUILDX_URL}"
[ ! -s "${DOCKER_DIR}/docker-buildx" ] && exit 1
/bin/rm -rf "${CACHE_DIR}"
mkdir -p "${CACHE_DIR}"
/bin/cp -f "${DOCKER_DIR}/docker-buildx" "${CACHE_DIR}/docker-buildx-${BUILDX_VER}"
fi
chmod a+x "${DOCKER_DIR}/docker-buildx"
docker info
docker buildx version
image_cache_dir="${RUNNER_TEMP}/.docker-images"
for image in "quay.io/hwdsl2/qemu-user-static:latest" "moby/buildkit:buildx-stable-1"; do
image_file=$(printf '%s' "${image}.tar.gz" | tr '/' '-' | tr ':' '-')
if [ -s "${image_cache_dir}/${image_file}" ]; then
echo "Using ${image} from cache..."
docker load -i "${image_cache_dir}/${image_file}"
else
echo "Downloading ${image}..."
docker pull -q "${image}"
mkdir -p "${image_cache_dir}"
docker save "${image}" | gzip > "${image_cache_dir}/${image_file}"
fi
done
docker run --rm --privileged quay.io/hwdsl2/qemu-user-static:latest --reset -p yes --credential yes
docker buildx create --name "builder-${GITHUB_SHA::8}" --driver docker-container --use
docker buildx inspect --bootstrap
- name: Docker Hub Login
env:
DOCKER_USER: ${{ github.repository_owner }}
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
run: echo "$DOCKER_TOKEN" | docker login --username "$DOCKER_USER" --password-stdin 2>&1
- name: Quay.io Login
env:
QUAY_USER: ${{ secrets.QUAY_USER }}
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
run: echo "$QUAY_TOKEN" | docker login quay.io --username "$QUAY_USER" --password-stdin 2>&1
- name: Docker Buildx
env:
DOCKER_USER: ${{ github.repository_owner }}
BUILD_ONLY: ${{ secrets.BUILD_ONLY }}
RUNNER_TEMP: ${{ runner.temp }}
OS_TYPE: ${{ inputs.os_type }}
run: |
cd "$GITHUB_WORKSPACE"
pwd
ls -ld vpn.env.example
[ "$BUILD_ONLY" = "true" ] && BUILD_ONLY=""
ts_now=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
if [ "$OS_TYPE" = "alpine" ]; then
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/arm64,linux/arm/v7 \
--build-arg BUILD_DATE=$ts_now \
--build-arg VCS_REF=${GITHUB_SHA::8} \
--build-arg VERSION=alpine-latest \
--cache-from type=local,src="${RUNNER_TEMP}/.buildx-cache" \
--cache-to type=local,dest="${RUNNER_TEMP}/.buildx-cache-new" \
-t "$DOCKER_USER/ipsec-vpn-server:latest" \
--pull \
${BUILD_ONLY:+--push} \
.
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/arm64,linux/arm/v7 \
--build-arg BUILD_DATE=$ts_now \
--build-arg VCS_REF=${GITHUB_SHA::8} \
--build-arg VERSION=alpine-latest \
--cache-from type=local,src="${RUNNER_TEMP}/.buildx-cache-new" \
-t "quay.io/$DOCKER_USER/ipsec-vpn-server:latest" \
--pull \
${BUILD_ONLY:+--push} \
.
elif [ "$OS_TYPE" = "debian" ]; then
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/arm64,linux/arm/v7 \
--build-arg BUILD_DATE=$ts_now \
--build-arg VCS_REF=${GITHUB_SHA::8} \
--build-arg VERSION=debian-latest \
--cache-from type=local,src="${RUNNER_TEMP}/.buildx-cache" \
--cache-to type=local,dest="${RUNNER_TEMP}/.buildx-cache-new" \
-f Dockerfile.debian \
-t "$DOCKER_USER/ipsec-vpn-server:debian" \
--pull \
${BUILD_ONLY:+--push} \
.
docker buildx build \
--progress plain \
--platform=linux/amd64,linux/arm64,linux/arm/v7 \
--build-arg BUILD_DATE=$ts_now \
--build-arg VCS_REF=${GITHUB_SHA::8} \
--build-arg VERSION=debian-latest \
--cache-from type=local,src="${RUNNER_TEMP}/.buildx-cache-new" \
-f Dockerfile.debian \
-t "quay.io/$DOCKER_USER/ipsec-vpn-server:debian" \
--pull \
${BUILD_ONLY:+--push} \
.
else
exit 1
fi
/bin/rm -rf "${RUNNER_TEMP}/.buildx-cache"
/bin/mv -f "${RUNNER_TEMP}/.buildx-cache-new" "${RUNNER_TEMP}/.buildx-cache"
- name: Clear
if: always()
run: |
shred -u "${HOME}/.docker/config.json"
rm -f "${HOME}/.docker/config.json"
docker buildx rm "builder-${GITHUB_SHA::8}" || true
================================================
FILE: .github/workflows/check_update.yml
================================================
#
# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
name: check_update
on:
workflow_call:
inputs:
os_type:
required: true
type: string
secrets:
CACHE_NAME:
required: true
outputs:
should_test:
value: ${{ jobs.check_update.outputs.should_test }}
should_update:
value: ${{ jobs.check_update.outputs.should_update }}
jobs:
check_update:
runs-on: ubuntu-24.04
if: github.repository_owner == 'hwdsl2'
env:
DOCKER_USER: ${{ github.repository_owner }}
OS_TYPE: ${{ inputs.os_type }}
outputs:
should_test: ${{ steps.check.outputs.should_test }}
should_update: ${{ steps.check.outputs.should_update }}
steps:
- name: Cache
uses: actions/cache@v5
with:
path: |
${{ runner.temp }}/.buildx-bin
${{ runner.temp }}/.buildx-cache
${{ runner.temp }}/.docker-images
key: ${{ secrets.CACHE_NAME }}-${{ github.sha }}-${{ github.run_id }}-check
restore-keys: |
${{ secrets.CACHE_NAME }}-
- name: Prepare
run: |
if [ "$OS_TYPE" = "alpine" ]; then
docker pull alpine:3.23
docker pull "$DOCKER_USER/ipsec-vpn-server"
elif [ "$OS_TYPE" = "debian" ]; then
docker pull debian:bookworm-slim
docker pull "$DOCKER_USER/ipsec-vpn-server:debian"
else
exit 1
fi
- name: Check
id: check
run: |
BASE_UPDATED=false
if [ "$OS_TYPE" = "alpine" ]; then
base_ts=$(docker inspect --format='{{.Created}}' alpine:3.23)
image_ts=$(docker inspect --format='{{.Created}}' "$DOCKER_USER/ipsec-vpn-server")
elif [ "$OS_TYPE" = "debian" ]; then
base_ts=$(docker inspect --format='{{.Created}}' debian:bookworm-slim)
image_ts=$(docker inspect --format='{{.Created}}' "$DOCKER_USER/ipsec-vpn-server:debian")
else
exit 1
fi
if [ -n "$base_ts" ] && [ -n "$image_ts" ]; then
base_ts_s=$(date -d "$base_ts" +%s)
image_ts_s=$(date -d "$image_ts" +%s)
ts_now=$(date -u +'%Y-%m-%dT%H:%M:%SZ')
ts_now_s=$(date -d "$ts_now" +%s)
diff_s=$((ts_now_s - base_ts_s))
diff=$(printf '%dd %dh:%dm:%ds\n' $(($diff_s/86400)) $(($diff_s%86400/3600)) $(($diff_s%3600/60)) $(($diff_s%60)))
echo "Base update time: $base_ts"
echo "Image update time: $image_ts"
echo "Current time: $ts_now"
echo "Time diff (cur-base): $diff (${diff_s}s)"
if [ -n "$base_ts_s" ] && [ -n "$image_ts_s" ] \
&& [ "$base_ts_s" -ge "$image_ts_s" ] \
&& [ "$diff_s" -ge 14400 ]; then
echo "Starting build..."
BASE_UPDATED=true
else
echo "Not starting build."
fi
fi
echo "should_test=${BASE_UPDATED}" >> "$GITHUB_OUTPUT"
echo "should_update=${BASE_UPDATED}" >> "$GITHUB_OUTPUT"
================================================
FILE: .github/workflows/cron-alpine.yml
================================================
#
# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
name: build cron
on:
schedule:
- cron: '40 2,14 * * *'
jobs:
check_update:
if: github.repository_owner == 'hwdsl2'
uses: ./.github/workflows/check_update.yml
with:
os_type: alpine
secrets:
CACHE_NAME: ${{ secrets.CACHE_NAME2 }}
vpn_test:
needs: check_update
if: needs.check_update.outputs.should_test == 'true'
uses: ./.github/workflows/vpn_test.yml
with:
os_type: alpine
buildx:
needs: [check_update, vpn_test]
if: needs.check_update.outputs.should_update == 'true'
uses: ./.github/workflows/buildx.yml
with:
os_type: alpine
secrets:
CACHE_NAME: ${{ secrets.CACHE_NAME2 }}
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
QUAY_USER: ${{ secrets.QUAY_USER }}
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
BUILD_ONLY: ${{ secrets.BUILD_ONLY }}
================================================
FILE: .github/workflows/cron-debian.yml
================================================
#
# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
name: build debian cron
on:
schedule:
- cron: '25 2,14 * * *'
jobs:
check_update:
if: github.repository_owner == 'hwdsl2'
uses: ./.github/workflows/check_update.yml
with:
os_type: debian
secrets:
CACHE_NAME: ${{ secrets.CACHE_NAME }}
vpn_test:
needs: check_update
if: needs.check_update.outputs.should_test == 'true'
uses: ./.github/workflows/vpn_test.yml
with:
os_type: debian
buildx:
needs: [check_update, vpn_test]
if: needs.check_update.outputs.should_update == 'true'
uses: ./.github/workflows/buildx.yml
with:
os_type: debian
secrets:
CACHE_NAME: ${{ secrets.CACHE_NAME }}
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
QUAY_USER: ${{ secrets.QUAY_USER }}
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
BUILD_ONLY: ${{ secrets.BUILD_ONLY }}
================================================
FILE: .github/workflows/main-alpine.yml
================================================
#
# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
name: build
on:
push:
branches: [master]
paths:
- '.github/workflows/main-alpine.yml'
- '.github/workflows/buildx.yml'
- '.github/workflows/shellcheck.yml'
- '.github/workflows/vpn_test.yml'
- '.dockerignore'
- 'Dockerfile'
- 'run.sh'
jobs:
shellcheck:
if: github.repository_owner == 'hwdsl2'
uses: ./.github/workflows/shellcheck.yml
vpn_test:
if: github.repository_owner == 'hwdsl2'
uses: ./.github/workflows/vpn_test.yml
with:
os_type: alpine
buildx:
needs: [shellcheck, vpn_test]
uses: ./.github/workflows/buildx.yml
with:
os_type: alpine
secrets:
CACHE_NAME: ${{ secrets.CACHE_NAME2 }}
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
QUAY_USER: ${{ secrets.QUAY_USER }}
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
BUILD_ONLY: ${{ secrets.BUILD_ONLY }}
================================================
FILE: .github/workflows/main-debian.yml
================================================
#
# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
name: build debian
on:
push:
branches: [master]
paths:
- '.github/workflows/main-debian.yml'
- '.github/workflows/buildx.yml'
- '.github/workflows/shellcheck.yml'
- '.github/workflows/vpn_test.yml'
- '.dockerignore'
- 'Dockerfile.debian'
- 'run.sh'
jobs:
shellcheck:
if: github.repository_owner == 'hwdsl2'
uses: ./.github/workflows/shellcheck.yml
vpn_test:
if: github.repository_owner == 'hwdsl2'
uses: ./.github/workflows/vpn_test.yml
with:
os_type: debian
buildx:
needs: [shellcheck, vpn_test]
uses: ./.github/workflows/buildx.yml
with:
os_type: debian
secrets:
CACHE_NAME: ${{ secrets.CACHE_NAME }}
DOCKER_TOKEN: ${{ secrets.DOCKER_TOKEN }}
QUAY_USER: ${{ secrets.QUAY_USER }}
QUAY_TOKEN: ${{ secrets.QUAY_TOKEN }}
BUILD_ONLY: ${{ secrets.BUILD_ONLY }}
================================================
FILE: .github/workflows/shellcheck.yml
================================================
#
# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
name: shellcheck
on: workflow_call
jobs:
shellcheck:
runs-on: ubuntu-24.04
if: github.repository_owner == 'hwdsl2'
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Check
run: |
if [ ! -x /usr/bin/shellcheck ]; then
export DEBIAN_FRONTEND=noninteractive
sudo apt-get -yqq update
sudo apt-get -yqq install shellcheck
fi
cd "$GITHUB_WORKSPACE"
pwd
ls -ld vpn.env.example
export SHELLCHECK_OPTS="-e SC1090,SC1091"
shellcheck --version
shopt -s globstar
ls -ld -- **/*.sh
shellcheck **/*.sh
================================================
FILE: .github/workflows/vpn_test.yml
================================================
#
# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
name: vpn_test
on:
workflow_call:
inputs:
os_type:
required: true
type: string
jobs:
vpn_test:
runs-on: ubuntu-24.04
if: github.repository_owner == 'hwdsl2'
strategy:
matrix:
test_id: [no-env, with-env]
fail-fast: false
env:
OS_TYPE: ${{ inputs.os_type }}
TEST_ID: ${{ matrix.test_id }}
steps:
- uses: actions/checkout@v6
with:
persist-credentials: false
- name: Test
run: |
[ ! -x /usr/bin/docker ] && exit 1
cd "$GITHUB_WORKSPACE"
pwd
ls -ld vpn.env.example
sed -i '/ swan_ver_latest=/s/^/#/' run.sh
sed -i \
-e '/VPN_IPSEC_PSK/s/# //' \
-e '/VPN_USER/s/# //' \
-e '/VPN_PASSWORD/s/# //' \
-e '/VPN_DNS_NAME/s/# //' \
-e '/VPN_CLIENT_NAME/s/# //' \
-e '/VPN_DNS_SRV1/s/# //' \
-e '/VPN_DNS_SRV2/s/# //' \
vpn.env.example
set -x
if [ "$OS_TYPE" = "alpine" ]; then
docker build -t vpn-test .
elif [ "$OS_TYPE" = "debian" ]; then
docker build -f Dockerfile.debian -t vpn-test .
else
exit 1
fi
if [ "$TEST_ID" = "with-env" ]; then
docker run \
--name "$TEST_ID" \
--env-file ./vpn.env.example \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-p 500:500/udp \
-p 4500:4500/udp \
-d --cap-add=NET_ADMIN \
--device=/dev/ppp \
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv4.conf.all.accept_redirects=0 \
--sysctl net.ipv4.conf.all.send_redirects=0 \
--sysctl net.ipv4.conf.all.rp_filter=0 \
--sysctl net.ipv4.conf.default.accept_redirects=0 \
--sysctl net.ipv4.conf.default.send_redirects=0 \
--sysctl net.ipv4.conf.default.rp_filter=0 \
vpn-test
elif [ "$TEST_ID" = "no-env" ]; then
docker run \
--name "$TEST_ID" \
--restart=always \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
vpn-test
fi
sleep 30
docker ps | grep "$TEST_ID"
docker logs "$TEST_ID"
docker exec "$TEST_ID" netstat -anpu | grep pluto
docker exec "$TEST_ID" netstat -anpu | grep xl2tpd
docker exec "$TEST_ID" iptables -nvL
docker exec "$TEST_ID" iptables -nvL | grep -q 'ppp+'
docker exec "$TEST_ID" iptables -nvL | grep -q '192\.168\.43\.0/24'
docker exec "$TEST_ID" iptables -nvL -t nat
docker exec "$TEST_ID" iptables -nvL -t nat | grep -q '192\.168\.42\.0/24'
docker exec "$TEST_ID" iptables -nvL -t nat | grep -q '192\.168\.43\.0/24'
docker exec "$TEST_ID" ipsec status
docker exec "$TEST_ID" ipsec status | grep -q l2tp-psk
docker exec "$TEST_ID" ipsec status | grep -q xauth-psk
if [ "$TEST_ID" = "with-env" ]; then
source ./vpn.env.example
docker exec "$TEST_ID" grep "$VPN_IPSEC_PSK" /etc/ipsec.secrets
docker exec "$TEST_ID" grep "$VPN_USER" /etc/ppp/chap-secrets
docker exec "$TEST_ID" grep "$VPN_PASSWORD" /etc/ppp/chap-secrets
docker exec "$TEST_ID" grep "$VPN_USER" /etc/ipsec.d/passwd
docker exec "$TEST_ID" ipsec status | grep -q ikev2-cp
docker exec "$TEST_ID" grep 'modecfgdns="1.1.1.1 1.0.0.1"' /etc/ipsec.conf
docker exec "$TEST_ID" grep 'ms-dns 1.1.1.1' /etc/ppp/options.xl2tpd
docker exec "$TEST_ID" grep 'ms-dns 1.0.0.1' /etc/ppp/options.xl2tpd
docker exec "$TEST_ID" grep 'leftid=@vpn.example.com' /etc/ipsec.d/ikev2.conf
docker exec "$TEST_ID" grep 'modecfgdns="1.1.1.1 1.0.0.1"' /etc/ipsec.d/ikev2.conf
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/your_client_name.mobileconfig
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/your_client_name.sswan
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/your_client_name.p12
docker exec "$TEST_ID" grep 'vpn.example.com' /etc/ipsec.d/your_client_name.mobileconfig
docker exec "$TEST_ID" grep 'vpn.example.com' /etc/ipsec.d/your_client_name.sswan
docker exec "$TEST_ID" ikev2.sh --addclient vpnclient2
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/vpnclient2.mobileconfig
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/vpnclient2.sswan
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/vpnclient2.p12
docker exec "$TEST_ID" rm -f /etc/ipsec.d/vpnclient2*
docker exec "$TEST_ID" ikev2.sh --exportclient vpnclient2
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/vpnclient2.mobileconfig
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/vpnclient2.sswan
docker exec "$TEST_ID" ls -ld /etc/ipsec.d/vpnclient2.p12
docker exec "$TEST_ID" ikev2.sh --listclients
echo "y" | docker exec -i "$TEST_ID" ikev2.sh --revokeclient vpnclient2
else
docker exec "$TEST_ID" ipsec status | grep -q ikev2-cp && exit 1
fi
docker restart "$TEST_ID"
sleep 10
docker ps | grep "$TEST_ID"
docker logs "$TEST_ID"
docker exec "$TEST_ID" netstat -anpu | grep pluto
docker exec "$TEST_ID" netstat -anpu | grep xl2tpd
docker exec "$TEST_ID" iptables -nvL
docker exec "$TEST_ID" iptables -nvL | grep -q 'ppp+'
docker exec "$TEST_ID" iptables -nvL | grep -q '192\.168\.43\.0/24'
docker exec "$TEST_ID" iptables -nvL -t nat
docker exec "$TEST_ID" iptables -nvL -t nat | grep -q '192\.168\.42\.0/24'
docker exec "$TEST_ID" iptables -nvL -t nat | grep -q '192\.168\.43\.0/24'
docker exec "$TEST_ID" ipsec status
docker exec "$TEST_ID" ipsec status | grep -q l2tp-psk
docker exec "$TEST_ID" ipsec status | grep -q xauth-psk
if [ "$TEST_ID" = "with-env" ]; then
docker exec "$TEST_ID" ipsec status | grep -q ikev2-cp
echo "y" | docker exec -i "$TEST_ID" ikev2.sh --removeikev2
sleep 3
fi
docker exec "$TEST_ID" ipsec status | grep -q ikev2-cp && exit 1
exit 0
- name: Clear
if: always()
run: |
docker rm -f "$TEST_ID" || true
docker rmi vpn-test || true
================================================
FILE: .gitignore
================================================
vpn.env
================================================
FILE: Dockerfile
================================================
#
# Copyright (C) 2021-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
FROM alpine:3.23
ENV SWAN_VER=5.3
WORKDIR /opt/src
RUN set -x \
&& apk add --no-cache \
bash bind-tools coreutils openssl uuidgen wget xl2tpd iptables iptables-legacy ip6tables \
iproute2 libcap-ng libcurl libevent linux-pam musl nspr nss nss-tools openrc \
bison flex gcc make libc-dev bsd-compat-headers linux-pam-dev \
nss-dev libcap-ng-dev libevent-dev curl-dev nspr-dev \
&& cd /sbin \
&& for fn in iptables iptables-save iptables-restore \
ip6tables ip6tables-save ip6tables-restore; do \
ln -fs xtables-legacy-multi "$fn"; done \
&& cd /opt/src \
&& wget -t 3 -T 30 -nv -O libreswan.tar.gz "https://github.com/libreswan/libreswan/archive/v${SWAN_VER}.tar.gz" \
|| wget -t 3 -T 30 -nv -O libreswan.tar.gz "https://download.libreswan.org/libreswan-${SWAN_VER}.tar.gz" \
&& tar xzf libreswan.tar.gz \
&& rm -f libreswan.tar.gz \
&& cd "libreswan-${SWAN_VER}" \
&& printf 'WERROR_CFLAGS=-w -s\nUSE_DNSSEC=false\nUSE_DH2=true\n' > Makefile.inc.local \
&& printf 'FINALNSSDIR=/etc/ipsec.d\nNSSDIR=/etc/ipsec.d\n' >> Makefile.inc.local \
&& make -s base \
&& make -s install-base \
&& cd /opt/src \
&& mkdir -p /run/openrc \
&& touch /run/openrc/softlevel \
&& rm -rf "/opt/src/libreswan-${SWAN_VER}" \
&& apk del --no-cache \
bison flex gcc make libc-dev bsd-compat-headers linux-pam-dev \
nss-dev libcap-ng-dev libevent-dev curl-dev nspr-dev
RUN wget -t 3 -T 30 -nv -O /opt/src/ikev2.sh https://github.com/hwdsl2/setup-ipsec-vpn/raw/5414cdfb71b0d02aea04c124ba71e3d7916fa984/extras/ikev2setup.sh \
&& chmod +x /opt/src/ikev2.sh \
&& ln -s /opt/src/ikev2.sh /usr/bin
COPY ./run.sh /opt/src/run.sh
RUN chmod 755 /opt/src/run.sh
EXPOSE 500/udp 4500/udp
CMD ["/opt/src/run.sh"]
ARG BUILD_DATE
ARG VERSION
ARG VCS_REF
ENV IMAGE_VER=$BUILD_DATE
LABEL maintainer="Lin Song <linsongui@gmail.com>" \
org.opencontainers.image.created="$BUILD_DATE" \
org.opencontainers.image.version="$VERSION" \
org.opencontainers.image.revision="$VCS_REF" \
org.opencontainers.image.authors="Lin Song <linsongui@gmail.com>" \
org.opencontainers.image.title="IPsec VPN Server on Docker" \
org.opencontainers.image.description="Docker image to run an IPsec VPN server, with IPsec/L2TP, Cisco IPsec and IKEv2." \
org.opencontainers.image.url="https://github.com/hwdsl2/docker-ipsec-vpn-server" \
org.opencontainers.image.source="https://github.com/hwdsl2/docker-ipsec-vpn-server" \
org.opencontainers.image.documentation="https://github.com/hwdsl2/docker-ipsec-vpn-server"
================================================
FILE: Dockerfile.debian
================================================
#
# Copyright (C) 2016-2026 Lin Song <linsongui@gmail.com>
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
FROM debian:bookworm-slim
ENV SWAN_VER=5.3
WORKDIR /opt/src
RUN apt-get -yqq update \
&& DEBIAN_FRONTEND=noninteractive \
apt-get -yqq --no-install-recommends install \
wget dnsutils openssl ca-certificates kmod iproute2 \
gawk net-tools iptables bsdmainutils libcurl3-nss \
libnss3-tools libevent-dev uuid-runtime xl2tpd \
libnss3-dev libnspr4-dev pkg-config libpam0g-dev \
libcap-ng-dev libcap-ng-utils libselinux1-dev \
libcurl4-nss-dev flex bison gcc make \
&& wget -t 3 -T 30 -nv -O libreswan.tar.gz "https://github.com/libreswan/libreswan/archive/v${SWAN_VER}.tar.gz" \
|| wget -t 3 -T 30 -nv -O libreswan.tar.gz "https://download.libreswan.org/libreswan-${SWAN_VER}.tar.gz" \
&& tar xzf libreswan.tar.gz \
&& rm -f libreswan.tar.gz \
&& cd "libreswan-${SWAN_VER}" \
&& printf 'WERROR_CFLAGS=-w -s\nUSE_DNSSEC=false\nUSE_SYSTEMD_WATCHDOG=false\n' > Makefile.inc.local \
&& printf 'USE_DH2=true\nUSE_NSS_KDF=false\nFINALNSSDIR=/etc/ipsec.d\nNSSDIR=/etc/ipsec.d\n' >> Makefile.inc.local \
&& make -s base \
&& make -s install-base \
&& cd /opt/src \
&& rm -rf "/opt/src/libreswan-${SWAN_VER}" \
&& apt-get -yqq remove \
libnss3-dev libnspr4-dev pkg-config libpam0g-dev \
libcap-ng-dev libcap-ng-utils libselinux1-dev \
libcurl4-nss-dev flex bison gcc make \
&& apt-get -yqq autoremove \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/* \
&& rm -rf /var/log/* \
&& update-alternatives --set iptables /usr/sbin/iptables-legacy \
&& update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy
RUN wget -t 3 -T 30 -nv -O /opt/src/ikev2.sh https://github.com/hwdsl2/setup-ipsec-vpn/raw/5414cdfb71b0d02aea04c124ba71e3d7916fa984/extras/ikev2setup.sh \
&& chmod +x /opt/src/ikev2.sh \
&& ln -s /opt/src/ikev2.sh /usr/bin
COPY ./run.sh /opt/src/run.sh
RUN chmod 755 /opt/src/run.sh
EXPOSE 500/udp 4500/udp
CMD ["/opt/src/run.sh"]
ARG BUILD_DATE
ARG VERSION
ARG VCS_REF
ENV IMAGE_VER=$BUILD_DATE
LABEL maintainer="Lin Song <linsongui@gmail.com>" \
org.opencontainers.image.created="$BUILD_DATE" \
org.opencontainers.image.version="$VERSION" \
org.opencontainers.image.revision="$VCS_REF" \
org.opencontainers.image.authors="Lin Song <linsongui@gmail.com>" \
org.opencontainers.image.title="IPsec VPN Server on Docker" \
org.opencontainers.image.description="Docker image to run an IPsec VPN server, with IPsec/L2TP, Cisco IPsec and IKEv2." \
org.opencontainers.image.url="https://github.com/hwdsl2/docker-ipsec-vpn-server" \
org.opencontainers.image.source="https://github.com/hwdsl2/docker-ipsec-vpn-server" \
org.opencontainers.image.documentation="https://github.com/hwdsl2/docker-ipsec-vpn-server"
================================================
FILE: LICENSE.md
================================================
### Creative Commons Attribution-ShareAlike 3.0 Unported License
Link to license summary: https://creativecommons.org/licenses/by-sa/3.0/
Copyright (C) 2016-2026 [Lin Song](https://github.com/hwdsl2)
Based on [the work of Thomas Sarlandie](https://github.com/sarfata/voodooprivacy) (Copyright 2012)
<p>THE WORK (AS DEFINED BELOW) IS PROVIDED UNDER THE TERMS
OF THIS CREATIVE COMMONS PUBLIC LICENSE ("CCPL" OR
"LICENSE"). THE WORK IS PROTECTED BY COPYRIGHT AND/OR OTHER
APPLICABLE LAW. ANY USE OF THE WORK OTHER THAN AS
AUTHORIZED UNDER THIS LICENSE OR COPYRIGHT LAW IS
PROHIBITED.</p>
<p>BY EXERCISING ANY RIGHTS TO THE WORK PROVIDED HERE, YOU
ACCEPT AND AGREE TO BE BOUND BY THE TERMS OF THIS LICENSE.
TO THE EXTENT THIS LICENSE MAY BE CONSIDERED TO BE A
CONTRACT, THE LICENSOR GRANTS YOU THE RIGHTS CONTAINED HERE
IN CONSIDERATION OF YOUR ACCEPTANCE OF SUCH TERMS AND
CONDITIONS.</p>
<p><strong>1. Definitions</strong></p>
<ol type="a">
<li><strong>"Adaptation"</strong> means a work based upon
the Work, or upon the Work and other pre-existing works,
such as a translation, adaptation, derivative work,
arrangement of music or other alterations of a literary
or artistic work, or phonogram or performance and
includes cinematographic adaptations or any other form in
which the Work may be recast, transformed, or adapted
including in any form recognizably derived from the
original, except that a work that constitutes a
Collection will not be considered an Adaptation for the
purpose of this License. For the avoidance of doubt,
where the Work is a musical work, performance or
phonogram, the synchronization of the Work in
timed-relation with a moving image ("synching") will be
considered an Adaptation for the purpose of this
License.</li>
<li><strong>"Collection"</strong> means a collection of
literary or artistic works, such as encyclopedias and
anthologies, or performances, phonograms or broadcasts,
or other works or subject matter other than works listed
in Section 1(f) below, which, by reason of the selection
and arrangement of their contents, constitute
intellectual creations, in which the Work is included in
its entirety in unmodified form along with one or more
other contributions, each constituting separate and
independent works in themselves, which together are
assembled into a collective whole. A work that
constitutes a Collection will not be considered an
Adaptation (as defined below) for the purposes of this
License.</li>
<li><strong>"Creative Commons Compatible
License"</strong> means a license that is listed at
https://creativecommons.org/compatiblelicenses that has
been approved by Creative Commons as being essentially
equivalent to this License, including, at a minimum,
because that license: (i) contains terms that have the
same purpose, meaning and effect as the License Elements
of this License; and, (ii) explicitly permits the
relicensing of adaptations of works made available under
that license under this License or a Creative Commons
jurisdiction license with the same License Elements as
this License.</li>
<li><strong>"Distribute"</strong> means to make available
to the public the original and copies of the Work or
Adaptation, as appropriate, through sale or other
transfer of ownership.</li>
<li><strong>"License Elements"</strong> means the
following high-level license attributes as selected by
Licensor and indicated in the title of this License:
Attribution, ShareAlike.</li>
<li><strong>"Licensor"</strong> means the individual,
individuals, entity or entities that offer(s) the Work
under the terms of this License.</li>
<li><strong>"Original Author"</strong> means, in the case
of a literary or artistic work, the individual,
individuals, entity or entities who created the Work or
if no individual or entity can be identified, the
publisher; and in addition (i) in the case of a
performance the actors, singers, musicians, dancers, and
other persons who act, sing, deliver, declaim, play in,
interpret or otherwise perform literary or artistic works
or expressions of folklore; (ii) in the case of a
phonogram the producer being the person or legal entity
who first fixes the sounds of a performance or other
sounds; and, (iii) in the case of broadcasts, the
organization that transmits the broadcast.</li>
<li><strong>"Work"</strong> means the literary and/or
artistic work offered under the terms of this License
including without limitation any production in the
literary, scientific and artistic domain, whatever may be
the mode or form of its expression including digital
form, such as a book, pamphlet and other writing; a
lecture, address, sermon or other work of the same
nature; a dramatic or dramatico-musical work; a
choreographic work or entertainment in dumb show; a
musical composition with or without words; a
cinematographic work to which are assimilated works
expressed by a process analogous to cinematography; a
work of drawing, painting, architecture, sculpture,
engraving or lithography; a photographic work to which
are assimilated works expressed by a process analogous to
photography; a work of applied art; an illustration, map,
plan, sketch or three-dimensional work relative to
geography, topography, architecture or science; a
performance; a broadcast; a phonogram; a compilation of
data to the extent it is protected as a copyrightable
work; or a work performed by a variety or circus
performer to the extent it is not otherwise considered a
literary or artistic work.</li>
<li><strong>"You"</strong> means an individual or entity
exercising rights under this License who has not
previously violated the terms of this License with
respect to the Work, or who has received express
permission from the Licensor to exercise rights under
this License despite a previous violation.</li>
<li><strong>"Publicly Perform"</strong> means to perform
public recitations of the Work and to communicate to the
public those public recitations, by any means or process,
including by wire or wireless means or public digital
performances; to make available to the public Works in
such a way that members of the public may access these
Works from a place and at a place individually chosen by
them; to perform the Work to the public by any means or
process and the communication to the public of the
performances of the Work, including by public digital
performance; to broadcast and rebroadcast the Work by any
means including signs, sounds or images.</li>
<li><strong>"Reproduce"</strong> means to make copies of
the Work by any means including without limitation by
sound or visual recordings and the right of fixation and
reproducing fixations of the Work, including storage of a
protected performance or phonogram in digital form or
other electronic medium.</li>
</ol>
<p><strong>2. Fair Dealing Rights.</strong> Nothing in this
License is intended to reduce, limit, or restrict any uses
free from copyright or rights arising from limitations or
exceptions that are provided for in connection with the
copyright protection under copyright law or other
applicable laws.</p>
<p><strong>3. License Grant.</strong> Subject to the terms
and conditions of this License, Licensor hereby grants You
a worldwide, royalty-free, non-exclusive, perpetual (for
the duration of the applicable copyright) license to
exercise the rights in the Work as stated below:</p>
<ol type="a">
<li>to Reproduce the Work, to incorporate the Work into
one or more Collections, and to Reproduce the Work as
incorporated in the Collections;</li>
<li>to create and Reproduce Adaptations provided that any
such Adaptation, including any translation in any medium,
takes reasonable steps to clearly label, demarcate or
otherwise identify that changes were made to the original
Work. For example, a translation could be marked "The
original work was translated from English to Spanish," or
a modification could indicate "The original work has been
modified.";</li>
<li>to Distribute and Publicly Perform the Work including
as incorporated in Collections; and,</li>
<li>to Distribute and Publicly Perform Adaptations.</li>
<li>
<p>For the avoidance of doubt:</p>
<ol type="i">
<li><strong>Non-waivable Compulsory License
Schemes</strong>. In those jurisdictions in which the
right to collect royalties through any statutory or
compulsory licensing scheme cannot be waived, the
Licensor reserves the exclusive right to collect such
royalties for any exercise by You of the rights
granted under this License;</li>
<li><strong>Waivable Compulsory License
Schemes</strong>. In those jurisdictions in which the
right to collect royalties through any statutory or
compulsory licensing scheme can be waived, the
Licensor waives the exclusive right to collect such
royalties for any exercise by You of the rights
granted under this License; and,</li>
<li><strong>Voluntary License Schemes</strong>. The
Licensor waives the right to collect royalties,
whether individually or, in the event that the
Licensor is a member of a collecting society that
administers voluntary licensing schemes, via that
society, from any exercise by You of the rights
granted under this License.</li>
</ol>
</li>
</ol>
<p>The above rights may be exercised in all media and
formats whether now known or hereafter devised. The above
rights include the right to make such modifications as are
technically necessary to exercise the rights in other media
and formats. Subject to Section 8(f), all rights not
expressly granted by Licensor are hereby reserved.</p>
<p><strong>4. Restrictions.</strong> The license granted in
Section 3 above is expressly made subject to and limited by
the following restrictions:</p>
<ol type="a">
<li>You may Distribute or Publicly Perform the Work only
under the terms of this License. You must include a copy
of, or the Uniform Resource Identifier (URI) for, this
License with every copy of the Work You Distribute or
Publicly Perform. You may not offer or impose any terms
on the Work that restrict the terms of this License or
the ability of the recipient of the Work to exercise the
rights granted to that recipient under the terms of the
License. You may not sublicense the Work. You must keep
intact all notices that refer to this License and to the
disclaimer of warranties with every copy of the Work You
Distribute or Publicly Perform. When You Distribute or
Publicly Perform the Work, You may not impose any
effective technological measures on the Work that
restrict the ability of a recipient of the Work from You
to exercise the rights granted to that recipient under
the terms of the License. This Section 4(a) applies to
the Work as incorporated in a Collection, but this does
not require the Collection apart from the Work itself to
be made subject to the terms of this License. If You
create a Collection, upon notice from any Licensor You
must, to the extent practicable, remove from the
Collection any credit as required by Section 4(c), as
requested. If You create an Adaptation, upon notice from
any Licensor You must, to the extent practicable, remove
from the Adaptation any credit as required by Section
4(c), as requested.</li>
<li>You may Distribute or Publicly Perform an Adaptation
only under the terms of: (i) this License; (ii) a later
version of this License with the same License Elements as
this License; (iii) a Creative Commons jurisdiction
license (either this or a later license version) that
contains the same License Elements as this License (e.g.,
Attribution-ShareAlike 3.0 US)); (iv) a Creative Commons
Compatible License. If you license the Adaptation under
one of the licenses mentioned in (iv), you must comply
with the terms of that license. If you license the
Adaptation under the terms of any of the licenses
mentioned in (i), (ii) or (iii) (the "Applicable
License"), you must comply with the terms of the
Applicable License generally and the following
provisions: (I) You must include a copy of, or the URI
for, the Applicable License with every copy of each
Adaptation You Distribute or Publicly Perform; (II) You
may not offer or impose any terms on the Adaptation that
restrict the terms of the Applicable License or the
ability of the recipient of the Adaptation to exercise
the rights granted to that recipient under the terms of
the Applicable License; (III) You must keep intact all
notices that refer to the Applicable License and to the
disclaimer of warranties with every copy of the Work as
included in the Adaptation You Distribute or Publicly
Perform; (IV) when You Distribute or Publicly Perform the
Adaptation, You may not impose any effective
technological measures on the Adaptation that restrict
the ability of a recipient of the Adaptation from You to
exercise the rights granted to that recipient under the
terms of the Applicable License. This Section 4(b)
applies to the Adaptation as incorporated in a
Collection, but this does not require the Collection
apart from the Adaptation itself to be made subject to
the terms of the Applicable License.</li>
<li>If You Distribute, or Publicly Perform the Work or
any Adaptations or Collections, You must, unless a
request has been made pursuant to Section 4(a), keep
intact all copyright notices for the Work and provide,
reasonable to the medium or means You are utilizing: (i)
the name of the Original Author (or pseudonym, if
applicable) if supplied, and/or if the Original Author
and/or Licensor designate another party or parties (e.g.,
a sponsor institute, publishing entity, journal) for
attribution ("Attribution Parties") in Licensor's
copyright notice, terms of service or by other reasonable
means, the name of such party or parties; (ii) the title
of the Work if supplied; (iii) to the extent reasonably
practicable, the URI, if any, that Licensor specifies to
be associated with the Work, unless such URI does not
refer to the copyright notice or licensing information
for the Work; and (iv) , consistent with Ssection 3(b),
in the case of an Adaptation, a credit identifying the
use of the Work in the Adaptation (e.g., "French
translation of the Work by Original Author," or
"Screenplay based on original Work by Original Author").
The credit required by this Section 4(c) may be
implemented in any reasonable manner; provided, however,
that in the case of a Adaptation or Collection, at a
minimum such credit will appear, if a credit for all
contributing authors of the Adaptation or Collection
appears, then as part of these credits and in a manner at
least as prominent as the credits for the other
contributing authors. For the avoidance of doubt, You may
only use the credit required by this Section for the
purpose of attribution in the manner set out above and,
by exercising Your rights under this License, You may not
implicitly or explicitly assert or imply any connection
with, sponsorship or endorsement by the Original Author,
Licensor and/or Attribution Parties, as appropriate, of
You or Your use of the Work, without the separate,
express prior written permission of the Original Author,
Licensor and/or Attribution Parties.</li>
<li>Except as otherwise agreed in writing by the Licensor
or as may be otherwise permitted by applicable law, if
You Reproduce, Distribute or Publicly Perform the Work
either by itself or as part of any Adaptations or
Collections, You must not distort, mutilate, modify or
take other derogatory action in relation to the Work
which would be prejudicial to the Original Author's honor
or reputation. Licensor agrees that in those
jurisdictions (e.g. Japan), in which any exercise of the
right granted in Section 3(b) of this License (the right
to make Adaptations) would be deemed to be a distortion,
mutilation, modification or other derogatory action
prejudicial to the Original Author's honor and
reputation, the Licensor will waive or not assert, as
appropriate, this Section, to the fullest extent
permitted by the applicable national law, to enable You
to reasonably exercise Your right under Section 3(b) of
this License (right to make Adaptations) but not
otherwise.</li>
</ol>
<p><strong>5. Representations, Warranties and
Disclaimer</strong></p>
<p>UNLESS OTHERWISE MUTUALLY AGREED TO BY THE PARTIES IN
WRITING, LICENSOR OFFERS THE WORK AS-IS AND MAKES NO
REPRESENTATIONS OR WARRANTIES OF ANY KIND CONCERNING THE
WORK, EXPRESS, IMPLIED, STATUTORY OR OTHERWISE, INCLUDING,
WITHOUT LIMITATION, WARRANTIES OF TITLE, MERCHANTIBILITY,
FITNESS FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, OR THE
ABSENCE OF LATENT OR OTHER DEFECTS, ACCURACY, OR THE
PRESENCE OF ABSENCE OF ERRORS, WHETHER OR NOT DISCOVERABLE.
SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OF IMPLIED
WARRANTIES, SO SUCH EXCLUSION MAY NOT APPLY TO YOU.</p>
<p><strong>6. Limitation on Liability.</strong> EXCEPT TO
THE EXTENT REQUIRED BY APPLICABLE LAW, IN NO EVENT WILL
LICENSOR BE LIABLE TO YOU ON ANY LEGAL THEORY FOR ANY
SPECIAL, INCIDENTAL, CONSEQUENTIAL, PUNITIVE OR EXEMPLARY
DAMAGES ARISING OUT OF THIS LICENSE OR THE USE OF THE WORK,
EVEN IF LICENSOR HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.</p>
<p><strong>7. Termination</strong></p>
<ol type="a">
<li>This License and the rights granted hereunder will
terminate automatically upon any breach by You of the
terms of this License. Individuals or entities who have
received Adaptations or Collections from You under this
License, however, will not have their licenses terminated
provided such individuals or entities remain in full
compliance with those licenses. Sections 1, 2, 5, 6, 7,
and 8 will survive any termination of this License.</li>
<li>Subject to the above terms and conditions, the
license granted here is perpetual (for the duration of
the applicable copyright in the Work). Notwithstanding
the above, Licensor reserves the right to release the
Work under different license terms or to stop
distributing the Work at any time; provided, however that
any such election will not serve to withdraw this License
(or any other license that has been, or is required to
be, granted under the terms of this License), and this
License will continue in full force and effect unless
terminated as stated above.</li>
</ol>
<p><strong>8. Miscellaneous</strong></p>
<ol type="a">
<li>Each time You Distribute or Publicly Perform the Work
or a Collection, the Licensor offers to the recipient a
license to the Work on the same terms and conditions as
the license granted to You under this License.</li>
<li>Each time You Distribute or Publicly Perform an
Adaptation, Licensor offers to the recipient a license to
the original Work on the same terms and conditions as the
license granted to You under this License.</li>
<li>If any provision of this License is invalid or
unenforceable under applicable law, it shall not affect
the validity or enforceability of the remainder of the
terms of this License, and without further action by the
parties to this agreement, such provision shall be
reformed to the minimum extent necessary to make such
provision valid and enforceable.</li>
<li>No term or provision of this License shall be deemed
waived and no breach consented to unless such waiver or
consent shall be in writing and signed by the party to be
charged with such waiver or consent.</li>
<li>This License constitutes the entire agreement between
the parties with respect to the Work licensed here. There
are no understandings, agreements or representations with
respect to the Work not specified here. Licensor shall
not be bound by any additional provisions that may appear
in any communication from You. This License may not be
modified without the mutual written agreement of the
Licensor and You.</li>
<li>The rights granted under, and the subject matter
referenced, in this License were drafted utilizing the
terminology of the Berne Convention for the Protection of
Literary and Artistic Works (as amended on September 28,
1979), the Rome Convention of 1961, the WIPO Copyright
Treaty of 1996, the WIPO Performances and Phonograms
Treaty of 1996 and the Universal Copyright Convention (as
revised on July 24, 1971). These rights and subject
matter take effect in the relevant jurisdiction in which
the License terms are sought to be enforced according to
the corresponding provisions of the implementation of
those treaty provisions in the applicable national law.
If the standard suite of rights granted under applicable
copyright law includes additional rights not granted
under this License, such additional rights are deemed to
be included in the License; this License is not intended
to restrict the license of any rights under applicable
law.</li>
</ol>
================================================
FILE: README-ru.md
================================================
[English](README.md) | [简体中文](README-zh.md) | [繁體中文](README-zh-Hant.md) | [Русский](README-ru.md)
# IPsec VPN сервер на Docker
[](https://github.com/hwdsl2/docker-ipsec-vpn-server/actions/workflows/main-alpine.yml) [](https://github.com/hwdsl2/docker-ipsec-vpn-server/stargazers) [](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/) [](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/)
Docker-образ для запуска сервера IPsec VPN с поддержкой IPsec/L2TP, Cisco IPsec и IKEv2.
Основан на Alpine 3.23 или Debian 12 с использованием [Libreswan](https://libreswan.org) (программное обеспечение IPsec VPN) и [xl2tpd](https://github.com/xelerance/xl2tpd) (демон L2TP).
IPsec VPN шифрует сетевой трафик, поэтому никто между вами и VPN-сервером не сможет перехватывать ваши данные во время их передачи через Интернет. Это особенно полезно при использовании незащищённых сетей, например в кофейнях, аэропортах или гостиничных номерах.
**[» :book: Книга: Privacy Tools in the Age of AI](docs/vpn-book.md) [Build Your Own VPN Server](docs/vpn-book.md)**
## Быстрый старт
Используйте эту команду для настройки сервера IPsec VPN в Docker:
```
docker run \
--name ipsec-vpn-server \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
Данные для входа в VPN будут сгенерированы случайным образом. См. раздел [Получение данных для входа в VPN](#получение-данных-для-входа-в-vpn).
В качестве альтернативы вы можете [настроить IPsec VPN без Docker](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/README-ru.md). Чтобы узнать больше о том, как использовать этот образ, прочитайте разделы ниже.
## Возможности
- Поддержка IKEv2 с мощными и быстрыми шифрами (например, AES-GCM)
- Генерация VPN-профилей для автоматической настройки устройств iOS, macOS и Android
- Поддержка Windows, macOS, iOS, Android, Chrome OS и Linux в качестве VPN-клиентов
- Включает вспомогательный скрипт для управления пользователями и сертификатами IKEv2
## Установка Docker
Сначала [установите Docker](https://docs.docker.com/engine/install/) на ваш Linux-сервер. Вы также можете использовать [Podman](https://podman.io) для запуска этого образа после [создания псевдонима](https://podman.io/whatis.html) для `docker`.
Продвинутые пользователи могут использовать этот образ на macOS с помощью [Docker for Mac](https://docs.docker.com/docker-for-mac/). Перед использованием режима IPsec/L2TP может потребоваться один раз перезапустить контейнер Docker с помощью `docker restart ipsec-vpn-server`. Этот образ не поддерживает Docker for Windows.
## Загрузка
Получите доверенную сборку из [реестра Docker Hub](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/):
```
docker pull hwdsl2/ipsec-vpn-server
```
В качестве альтернативы можно скачать из [Quay.io](https://quay.io/repository/hwdsl2/ipsec-vpn-server):
```
docker pull quay.io/hwdsl2/ipsec-vpn-server
docker image tag quay.io/hwdsl2/ipsec-vpn-server hwdsl2/ipsec-vpn-server
```
Поддерживаемые платформы: `linux/amd64`, `linux/arm64` и `linux/arm/v7`.
Продвинутые пользователи могут [собрать образ из исходного кода](docs/advanced-usage.md#build-from-source-code) на GitHub.
### Сравнение образов
Доступны два предварительно собранных образа. Образ на базе Alpine используется по умолчанию и имеет размер всего около ~19 MB.
| | На базе Alpine | На базе Debian |
| ----------------- | ------------------------- | ------------------------------ |
| Имя образа | hwdsl2/ipsec-vpn-server | hwdsl2/ipsec-vpn-server:debian |
| Сжатый размер | ~ 19 MB | ~ 62 MB |
| Базовый образ | Alpine Linux 3.23 | Debian Linux 12 |
| Платформы | amd64, arm64, arm/v7 | amd64, arm64, arm/v7 |
| Версия Libreswan | 5.3 | 5.3 |
| IPsec/L2TP | ✅ | ✅ |
| Cisco IPsec | ✅ | ✅ |
| IKEv2 | ✅ | ✅ |
**Примечание:** Чтобы использовать образ на базе Debian, замените каждое `hwdsl2/ipsec-vpn-server` на `hwdsl2/ipsec-vpn-server:debian` в этом README. В настоящее время эти образы несовместимы с системами Synology NAS.
<details>
<summary>
Я хочу использовать более старую версию Libreswan 4.
</summary>
Обычно рекомендуется использовать последнюю версию [Libreswan](https://libreswan.org/) 5, которая является версией по умолчанию в этом проекте. Однако если вы хотите использовать более старую версию Libreswan 4, вы можете собрать Docker-образ из исходного кода:
```
git clone https://github.com/hwdsl2/docker-ipsec-vpn-server
cd docker-ipsec-vpn-server
# Указать версию Libreswan 4
sed -i 's/SWAN_VER=5\..*/SWAN_VER=4.15/' Dockerfile Dockerfile.debian
# Сборка образа на базе Alpine
docker build -t hwdsl2/ipsec-vpn-server .
# Сборка образа на базе Debian
docker build -f Dockerfile.debian -t hwdsl2/ipsec-vpn-server:debian .
```
</details>
## Как использовать этот образ
### Переменные окружения
**Примечание:** Все переменные для этого образа являются необязательными, что означает, что вам не нужно указывать ни одну переменную — и вы сможете получить работающий сервер IPsec VPN «из коробки»! Для этого создайте пустой файл `env` с помощью `touch vpn.env`, а затем перейдите к следующему разделу.
Этот Docker-образ использует следующие переменные, которые можно объявить в файле `env` (см. [пример](vpn.env.example)):
```
VPN_IPSEC_PSK=your_ipsec_pre_shared_key
VPN_USER=your_vpn_username
VPN_PASSWORD=your_vpn_password
```
Это создаст учетную запись пользователя для входа в VPN, которую можно использовать на нескольких ваших устройствах[\*](#важные-замечания). IPsec PSK (предварительно общий ключ) задаётся переменной окружения `VPN_IPSEC_PSK`. Имя пользователя VPN задаётся в `VPN_USER`, а пароль VPN указывается в `VPN_PASSWORD`.
Поддерживаются дополнительные пользователи VPN, и их можно при желании объявить в вашем файле `env` следующим образом. Имена пользователей и пароли должны быть разделены пробелами, а имена пользователей не могут повторяться. Все пользователи VPN будут использовать один и тот же IPsec PSK.
```
VPN_ADDL_USERS=additional_username_1 additional_username_2
VPN_ADDL_PASSWORDS=additional_password_1 additional_password_2
```
Переменные выше используются только для режимов IPsec/L2TP и IPsec/XAuth («Cisco IPsec»). Для IKEv2 см. [Настройка и использование IKEv2 VPN](#настройка-и-использование-ikev2-vpn).
**Примечание:** В файле `env` НЕ помещайте `""` или `''` вокруг значений и не добавляйте пробелы вокруг `=`. НЕ используйте внутри значений следующие специальные символы: `\ " '`. Надёжный IPsec PSK должен состоять как минимум из 20 случайных символов.
**Примечание:** Если вы измените файл `env` после того, как Docker-контейнер уже создан, необходимо удалить и создать контейнер заново, чтобы изменения вступили в силу. См. раздел [Обновление Docker-образа](#обновление-docker-образа).
### Дополнительные переменные окружения
Продвинутые пользователи могут при желании указать DNS-имя, имя клиента и/или собственные DNS-серверы.
<details>
<summary>
Узнайте, как указать DNS-имя, имя клиента и/или собственные DNS-серверы.
</summary>
Продвинутые пользователи могут при желании указать DNS-имя для адреса сервера IKEv2. DNS-имя должно быть полным доменным именем (FQDN). Пример:
```
VPN_DNS_NAME=vpn.example.com
```
Вы можете указать имя для первого клиента IKEv2. Используйте только одно слово, без специальных символов, кроме `-` и `_`. По умолчанию используется `vpnclient`, если имя не указано.
```
VPN_CLIENT_NAME=your_client_name
```
По умолчанию клиенты используют [Google Public DNS](https://developers.google.com/speed/public-dns/) при активном VPN. Вы можете указать собственные DNS-серверы для всех режимов VPN. Пример:
```
VPN_DNS_SRV1=1.1.1.1
VPN_DNS_SRV2=1.0.0.1
```
Для получения дополнительных сведений и списка популярных публичных DNS-провайдеров см. [Использование альтернативных DNS-серверов](docs/advanced-usage.md).
По умолчанию пароль не требуется при импорте конфигурации клиента IKEv2. Вы можете защитить файлы конфигурации клиента случайным паролем.
```
VPN_PROTECT_CONFIG=yes
```
**Примечание:** Переменные выше не влияют на режим IKEv2, если IKEv2 уже настроен в Docker-контейнере. В этом случае вы можете удалить IKEv2 и настроить его снова с пользовательскими параметрами. См. [Настройка и использование IKEv2 VPN](#настройка-и-использование-ikev2-vpn).
</details>
### Запуск сервера IPsec VPN
Создайте новый Docker-контейнер из этого образа (замените `./vpn.env` на ваш собственный файл `env`):
```
docker run \
--name ipsec-vpn-server \
--env-file ./vpn.env \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
В этой команде используется параметр `-v` команды `docker run`, чтобы создать новый [Docker volume](https://docs.docker.com/storage/volumes/) с именем `ikev2-vpn-data` и смонтировать его в `/etc/ipsec.d` внутри контейнера. Данные, связанные с IKEv2 (такие как сертификаты и ключи), будут сохраняться в этом томе. Позже, если вам потребуется заново создать Docker-контейнер, просто укажите тот же том снова.
Рекомендуется включить IKEv2 при использовании этого образа. Однако если вы предпочитаете не использовать IKEv2 и подключаться к VPN только через режимы IPsec/L2TP и IPsec/XAuth («Cisco IPsec»), удалите первый параметр `-v` из команды `docker run`, приведённой выше.
**Примечание:** Продвинутые пользователи также могут [запускать контейнер без привилегированного режима](docs/advanced-usage.md#run-without-privileged-mode).
### Получение данных для входа в VPN
Если вы не указали файл `env` в команде `docker run` выше, значение `VPN_USER` по умолчанию будет `vpnuser`, а `VPN_IPSEC_PSK` и `VPN_PASSWORD` будут сгенерированы случайным образом. Чтобы получить их, просмотрите журналы контейнера:
```
docker logs ipsec-vpn-server
```
Найдите в выводе следующие строки:
```
Connect to your new VPN with these details:
Server IP: your_vpn_server_ip
IPsec PSK: your_ipsec_pre_shared_key
Username: your_vpn_username
Password: your_vpn_password
```
Вывод также будет содержать сведения для режима IKEv2, если он включён.
(Необязательно) Сохраните сгенерированные данные для входа в VPN (если они есть) в текущий каталог:
```
docker cp ipsec-vpn-server:/etc/ipsec.d/vpn-gen.env ./
```
## Следующие шаги
*Прочитать на других языках: [English](README.md#next-steps), [简体中文](README-zh.md#下一步), [繁體中文](README-zh-Hant.md#下一步), [Русский](README-ru.md#следующие-шаги).*
Настройте ваш компьютер или устройство для использования VPN. Пожалуйста, обратитесь к следующим инструкциям (на английском языке):
**[Настройка и использование IKEv2 VPN (рекомендуется)](#настройка-и-использование-ikev2-vpn)**
**[Настройка клиентов IPsec/L2TP VPN](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md)**
**[Настройка клиентов IPsec/XAuth («Cisco IPsec»)](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-xauth.md)**
**Прочитайте [:book: книгу о VPN](docs/vpn-book.md), чтобы получить доступ к [дополнительному контенту](https://ko-fi.com/post/Support-this-project-and-get-access-to-supporter-o-O5O7FVF8J).**
Наслаждайтесь собственным VPN! :sparkles::tada::rocket::sparkles:
## Важные замечания
**Пользователи Windows**: для режима IPsec/L2TP требуется [одноразовое изменение реестра](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#windows-error-809), если VPN-сервер или клиент находится за NAT (например, домашним роутером).
Одна и та же учетная запись VPN может использоваться на нескольких ваших устройствах. Однако из-за ограничения IPsec/L2TP, если вы хотите подключить несколько устройств из-за одного NAT (например, домашнего роутера), необходимо использовать режим [IKEv2](#настройка-и-использование-ikev2-vpn) или [IPsec/XAuth](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-xauth.md).
Если вы хотите добавить, изменить или удалить учетные записи пользователей VPN, сначала обновите файл `env`, затем удалите и заново создайте Docker-контейнер, следуя инструкциям из [следующего раздела](#обновление-docker-образа). Продвинутые пользователи могут использовать [bind mount](docs/advanced-usage.md#bind-mount-the-env-file) для файла `env`.
Для серверов с внешним файрволом (например, [EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)/[GCE](https://cloud.google.com/vpc/docs/firewalls)) откройте UDP-порты 500 и 4500 для VPN. Пользователям Aliyun см. [#433](https://github.com/hwdsl2/setup-ipsec-vpn/issues/433).
Клиенты настроены использовать [Google Public DNS](https://developers.google.com/speed/public-dns/) при активном VPN. Если вы предпочитаете другого DNS-провайдера, прочитайте [этот раздел](docs/advanced-usage.md#use-alternative-dns-servers).
## Обновление Docker-образа
Чтобы обновить Docker-образ и контейнер, сначала [загрузите](#загрузка) последнюю версию:
```
docker pull hwdsl2/ipsec-vpn-server
```
Если Docker-образ уже обновлён, вы увидите:
```
Status: Image is up to date for hwdsl2/ipsec-vpn-server:latest
```
В противном случае будет загружена последняя версия. Чтобы обновить Docker-контейнер, сначала запишите все ваши [данные для входа в VPN](#получение-данных-для-входа-в-vpn). Затем удалите контейнер Docker с помощью `docker rm -f ipsec-vpn-server`. После этого создайте его заново, следуя инструкциям из раздела [Как использовать этот образ](#как-использовать-этот-образ).
## Настройка и использование IKEv2 VPN
Режим IKEv2 имеет преимущества по сравнению с IPsec/L2TP и IPsec/XAuth («Cisco IPsec») и не требует IPsec PSK, имени пользователя или пароля. Подробнее можно прочитать [здесь](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto.md).
Сначала проверьте журналы контейнера, чтобы увидеть сведения о IKEv2:
```bash
docker logs ipsec-vpn-server
```
**Примечание:** Если вы не можете найти сведения о IKEv2, возможно, IKEv2 не включён в контейнере. Попробуйте обновить Docker-образ и контейнер, следуя инструкциям из раздела [Обновление Docker-образа](#обновление-docker-образа).
Во время настройки IKEv2 создаётся клиент IKEv2 (с именем по умолчанию `vpnclient`), а его конфигурация экспортируется в `/etc/ipsec.d` **внутри контейнера**. Чтобы скопировать файл(ы) конфигурации на Docker-хост:
```
# Проверить содержимое /etc/ipsec.d в контейнере
docker exec -it ipsec-vpn-server ls -l /etc/ipsec.d
# Пример: копирование файла конфигурации клиента из контейнера
# в текущий каталог на Docker-хосте
docker cp ipsec-vpn-server:/etc/ipsec.d/vpnclient.p12 ./
```
**Следующие шаги:** [Настройте ваши устройства](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto.md) для использования IKEv2 VPN.
<details>
<summary>
Узнайте, как управлять клиентами IKEv2.
</summary>
Вы можете управлять клиентами IKEv2 с помощью вспомогательного скрипта. Ниже приведены примеры. Чтобы настроить параметры клиента, запустите скрипт без аргументов.
```bash
# Добавить нового клиента (с параметрами по умолчанию)
docker exec -it ipsec-vpn-server ikev2.sh --addclient [имя клиента]
# Экспортировать конфигурацию для существующего клиента
docker exec -it ipsec-vpn-server ikev2.sh --exportclient [имя клиента]
# Показать список существующих клиентов
docker exec -it ipsec-vpn-server ikev2.sh --listclients
# Показать справку
docker exec -it ipsec-vpn-server ikev2.sh -h
```
**Примечание:** Если возникает ошибка «executable file not found», замените `ikev2.sh` выше на `/opt/src/ikev2.sh`.
</details>
<details>
<summary>
Узнайте, как изменить адрес сервера IKEv2.
</summary>
В некоторых случаях может потребоваться изменить адрес сервера IKEv2. Например, чтобы перейти на использование DNS-имени или после изменения IP-адреса сервера. Чтобы изменить адрес сервера IKEv2, сначала [откройте bash-оболочку внутри контейнера](docs/advanced-usage.md#bash-shell-inside-container), затем [следуйте этим инструкциям](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto.md#change-ikev2-server-address). Обратите внимание, что журналы контейнера не будут показывать новый адрес сервера IKEv2, пока вы не перезапустите Docker-контейнер.
</details>
<details>
<summary>
Удаление IKEv2 и повторная настройка с пользовательскими параметрами.
</summary>
В некоторых случаях может потребоваться удалить IKEv2 и настроить его заново с пользовательскими параметрами.
**Предупреждение:** Вся конфигурация IKEv2, включая сертификаты и ключи, будет **безвозвратно удалена**. Это **нельзя отменить**!
**Вариант 1:** Удалить IKEv2 и настроить его снова с помощью вспомогательного скрипта.
Обратите внимание, что это переопределит переменные, указанные в файле `env`, такие как `VPN_DNS_NAME` и `VPN_CLIENT_NAME`, а журналы контейнера больше не будут показывать актуальную информацию для IKEv2.
```bash
# Удалить IKEv2 и удалить всю конфигурацию IKEv2
docker exec -it ipsec-vpn-server ikev2.sh --removeikev2
# Настроить IKEv2 снова с пользовательскими параметрами
docker exec -it ipsec-vpn-server ikev2.sh
```
**Вариант 2:** Удалить `ikev2-vpn-data` и создать контейнер заново.
1. Запишите все ваши [данные для входа в VPN](#получение-данных-для-входа-в-vpn).
1. Удалите Docker-контейнер: `docker rm -f ipsec-vpn-server`.
1. Удалите том `ikev2-vpn-data`: `docker volume rm ikev2-vpn-data`.
1. Обновите файл `env` и добавьте пользовательские параметры IKEv2, такие как `VPN_DNS_NAME` и `VPN_CLIENT_NAME`, затем создайте контейнер заново. См. раздел [Как использовать этот образ](#как-использовать-этот-образ).
</details>
## Расширенное использование
См. [Расширенное использование](docs/advanced-usage.md) (на английском языке).
- [Использование альтернативных DNS-серверов](docs/advanced-usage.md#use-alternative-dns-servers)
- [Запуск без привилегированного режима](docs/advanced-usage.md#run-without-privileged-mode)
- [Выбор режимов VPN](docs/advanced-usage.md#select-vpn-modes)
- [Доступ к другим контейнерам на Docker-хосте](docs/advanced-usage.md#access-other-containers-on-the-docker-host)
- [Указание публичного IP-адреса VPN-сервера](docs/advanced-usage.md#specify-vpn-servers-public-ip)
- [Назначение статических IP-адресов клиентам VPN](docs/advanced-usage.md#assign-static-ips-to-vpn-clients)
- [Настройка подсетей VPN](docs/advanced-usage.md#customize-vpn-subnets)
- [Поддержка IPv6](docs/advanced-usage.md#ipv6-support)
- [Раздельная маршрутизация (Split tunneling)](docs/advanced-usage.md#split-tunneling)
- [О режиме сетевого хоста](docs/advanced-usage.md#about-host-network-mode)
- [Включение журналов Libreswan](docs/advanced-usage.md#enable-libreswan-logs)
- [Проверка состояния сервера](docs/advanced-usage.md#check-server-status)
- [Сборка из исходного кода](docs/advanced-usage.md#build-from-source-code)
- [Bash-оболочка внутри контейнера](docs/advanced-usage.md#bash-shell-inside-container)
- [Bind mount файла env](docs/advanced-usage.md#bind-mount-the-env-file)
- [Развёртывание алгоритма управления перегрузкой Google BBR](docs/advanced-usage.md#deploy-google-bbr-congestion-control)
## Технические детали
Запущены два сервиса: `Libreswan (pluto)` для IPsec VPN и `xl2tpd` для поддержки L2TP.
Конфигурация IPsec по умолчанию поддерживает:
* IPsec/L2TP с PSK
* IKEv1 с PSK и XAuth («Cisco IPsec»)
* IKEv2
Порты, которые должны быть открыты для работы контейнера:
* 4500/udp и 500/udp для IPsec
## Лицензия
**Примечание:** Компоненты программного обеспечения внутри предварительно собранного образа (например, Libreswan и xl2tpd) распространяются по лицензиям, выбранным их правообладателями. Как и в случае использования любого готового образа, пользователь образа несёт ответственность за то, чтобы использование этого образа соответствовало всем применимым лицензиям для программного обеспечения, содержащегося внутри.
Copyright (C) 2016-2026 [Lin Song](https://github.com/hwdsl2) [](https://www.linkedin.com/in/linsongui)
Основано на [работе Thomas Sarlandie](https://github.com/sarfata/voodooprivacy) (Copyright 2012)
[](http://creativecommons.org/licenses/by-sa/3.0/)
Эта работа распространяется по лицензии [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/)
Требуется указание авторства: пожалуйста, указывайте моё имя в любых производных работах и сообщайте мне, как вы её улучшили!
================================================
FILE: README-zh-Hant.md
================================================
[English](README.md) | [简体中文](README-zh.md) | [繁體中文](README-zh-Hant.md) | [Русский](README-ru.md)
# Docker 上的 IPsec VPN 伺服器
[](https://github.com/hwdsl2/docker-ipsec-vpn-server/actions/workflows/main-alpine.yml) [](https://github.com/hwdsl2/docker-ipsec-vpn-server/stargazers) [](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/) [](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/)
使用此 Docker 映像快速架設 IPsec VPN 伺服器。支援 IPsec/L2TP、Cisco IPsec 和 IKEv2 協議。
本映像以 Alpine 3.23 或 Debian 12 為基礎,並使用 [Libreswan](https://libreswan.org)(IPsec VPN 軟體)和 [xl2tpd](https://github.com/xelerance/xl2tpd)(L2TP 服務程序)。
IPsec VPN 可以加密你的網路流量,以防止在透過網際網路傳送時,你與 VPN 伺服器之間的任何人未經授權存取你的資料。在使用不安全的網路時,這一點特別有用,例如在咖啡廳、機場或旅館房間。
**[» :book: Book: Privacy Tools in the Age of AI](docs/vpn-book-zh-Hant.md) [架設自己的 VPN 伺服器](docs/vpn-book-zh-Hant.md)**
## 快速開始
使用以下命令在 Docker 上快速架設 IPsec VPN 伺服器:
```
docker run \
--name ipsec-vpn-server \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
你的 VPN 登入憑證將會自動隨機生成。請參見[取得 VPN 登入資訊](#取得-vpn-登入資訊)。
另外,你也可以在不使用 Docker 的情況下[安裝 IPsec VPN](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/README-zh-Hant.md)。若要了解更多關於如何使用本映像的資訊,請繼續閱讀以下部分。
## 功能特性
- 支援具有強大且快速加密演算法(例如 AES-GCM)的 IKEv2 模式
- 生成 VPN 設定檔以自動設定 iOS、macOS 和 Android 裝置
- 支援 Windows、macOS、iOS、Android、Chrome OS 和 Linux 客戶端
- 包含輔助腳本以管理 IKEv2 使用者與憑證
## 安裝 Docker
首先在你的 Linux 伺服器上[安裝 Docker](https://docs.docker.com/engine/install/)。另外你也可以使用 [Podman](https://podman.io) 執行本映像,需要先為 `docker` 命令[建立一個別名](https://podman.io/whatis.html)。
進階使用者可以在 macOS 上透過安裝 [Docker for Mac](https://docs.docker.com/docker-for-mac/) 使用本映像。在使用 IPsec/L2TP 模式之前,你可能需要執行 `docker restart ipsec-vpn-server` 重新啟動一次 Docker 容器。本映像不支援 Docker for Windows。
## 下載
預先建構的可信任映像可在 [Docker Hub registry](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/) 下載:
```
docker pull hwdsl2/ipsec-vpn-server
```
或者,你也可以從 [Quay.io](https://quay.io/repository/hwdsl2/ipsec-vpn-server) 下載:
```
docker pull quay.io/hwdsl2/ipsec-vpn-server
docker image tag quay.io/hwdsl2/ipsec-vpn-server hwdsl2/ipsec-vpn-server
```
支援以下架構系統:`linux/amd64`、`linux/arm64` 和 `linux/arm/v7`。
進階使用者可以自行從 GitHub [編譯原始碼](docs/advanced-usage-zh.md#从源代码构建)。
### 映像對照表
有兩個預先建構的映像可用。預設的基於 Alpine 的映像大小僅約 ~19 MB。
| | 基於 Alpine | 基於 Debian |
| --------------- | ------------------------ | ------------------------------ |
| 映像名稱 | hwdsl2/ipsec-vpn-server | hwdsl2/ipsec-vpn-server:debian |
| 壓縮後大小 | ~ 19 MB | ~ 62 MB |
| 基礎映像 | Alpine Linux 3.23 | Debian Linux 12 |
| 系統架構 | amd64, arm64, arm/v7 | amd64, arm64, arm/v7 |
| Libreswan 版本 | 5.3 | 5.3 |
| IPsec/L2TP | ✅ | ✅ |
| Cisco IPsec | ✅ | ✅ |
| IKEv2 | ✅ | ✅ |
**註:** 若要使用基於 Debian 的映像,請將本自述文件中所有的 `hwdsl2/ipsec-vpn-server` 替換為 `hwdsl2/ipsec-vpn-server:debian`。這些映像目前與 Synology NAS 系統不相容。
<details>
<summary>
我需要使用較舊版本的 Libreswan 版本 4。
</summary>
一般建議使用最新的 [Libreswan](https://libreswan.org/) 版本 5,它是本專案的預設版本。不過,如果你想要使用較舊版本的 Libreswan 版本 4,你可以從原始碼建構 Docker 映像:
```
git clone https://github.com/hwdsl2/docker-ipsec-vpn-server
cd docker-ipsec-vpn-server
# Specify Libreswan version 4
sed -i 's/SWAN_VER=5\..*/SWAN_VER=4.15/' Dockerfile Dockerfile.debian
# To build Alpine-based image
docker build -t hwdsl2/ipsec-vpn-server .
# To build Debian-based image
docker build -f Dockerfile.debian -t hwdsl2/ipsec-vpn-server:debian .
```
</details>
## 如何使用本映像
### 環境變數
**註:** 所有這些變數對於本映像都是可選的,也就是說即使不定義它們也可以架設 IPsec VPN 伺服器。你可以執行 `touch vpn.env` 建立一個空的 `env` 檔案,然後跳到下一節。
此 Docker 映像使用以下幾個變數,可以在一個 `env` 檔案中定義(參見[範例](vpn.env.example)):
```
VPN_IPSEC_PSK=your_ipsec_pre_shared_key
VPN_USER=your_vpn_username
VPN_PASSWORD=your_vpn_password
```
這將建立一個用於 VPN 登入的使用者帳戶,它可以在你的多個裝置上使用[\*](#重要提示)。IPsec PSK(預共享金鑰)由 `VPN_IPSEC_PSK` 環境變數指定。VPN 使用者名稱與密碼分別在 `VPN_USER` 和 `VPN_PASSWORD` 中定義。
支援建立額外的 VPN 使用者,如果需要,可以像下面這樣在你的 `env` 檔案中定義。使用者名稱與密碼必須分別使用空格分隔,並且使用者名稱不能重複。所有 VPN 使用者將共用同一個 IPsec PSK。
```
VPN_ADDL_USERS=additional_username_1 additional_username_2
VPN_ADDL_PASSWORDS=additional_password_1 additional_password_2
```
以上變數僅適用於 IPsec/L2TP 和 IPsec/XAuth("Cisco IPsec")模式。對於 IKEv2,請參見[設定並使用 IKEv2 VPN](#設定並使用-ikev2-vpn)。
**註:** 在你的 `env` 檔案中,**不要**為變數值加入 `""` 或 `''`,或在 `=` 兩側加入空格。**不要**在值中使用這些字元: `\ " '`。一個安全的 IPsec PSK 應至少包含 20 個隨機字元。
**註:** 如果在建立 Docker 容器後修改 `env` 檔案,則必須刪除並重新建立容器才能讓變更生效。請參見[更新 Docker 映像](#更新-docker-映像)。
### 其他環境變數
進階使用者可以指定一個網域名稱、客戶端名稱和/或其他 DNS 伺服器。這是可選的。
<details>
<summary>
了解如何指定一個網域名稱、客戶端名稱和/或其他 DNS 伺服器。
</summary>
進階使用者可以指定一個網域名稱作為 IKEv2 伺服器位址。這是可選的。該網域名稱必須是完整網域名稱 (FQDN)。示例如下:
```
VPN_DNS_NAME=vpn.example.com
```
你可以指定第一個 IKEv2 客戶端的名稱。該名稱不能包含空格或除 `-` `_` 之外的任何特殊字元。如果未指定,則使用預設值 `vpnclient`。
```
VPN_CLIENT_NAME=your_client_name
```
在 VPN 已連線時,客戶端預設設定為使用 [Google Public DNS](https://developers.google.com/speed/public-dns/)。你可以為所有 VPN 模式指定其他 DNS 伺服器。示例如下:
```
VPN_DNS_SRV1=1.1.1.1
VPN_DNS_SRV2=1.0.0.1
```
有關詳細資訊以及一些常見的公共 DNS 提供商列表,請參見[使用其他 DNS 伺服器](docs/advanced-usage-zh.md)。
預設情況下,匯入 IKEv2 客戶端設定時不需要密碼。你可以選擇使用隨機密碼保護客戶端設定檔。
```
VPN_PROTECT_CONFIG=yes
```
**註:** 如果在 Docker 容器中已經設定 IKEv2,則以上變數對 IKEv2 模式無效。在這種情況下,你可以移除 IKEv2 並使用自訂選項重新設定它。請參見[設定並使用 IKEv2 VPN](#設定並使用-ikev2-vpn)。
</details>
### 執行 IPsec VPN 伺服器
使用本映像建立一個新的 Docker 容器(將 `./vpn.env` 替換為你自己的 `env` 檔案):
```
docker run \
--name ipsec-vpn-server \
--env-file ./vpn.env \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
在此命令中,我們使用 `docker run` 的 `-v` 選項來建立一個名為 `ikev2-vpn-data` 的新 [Docker 卷](https://docs.docker.com/storage/volumes/),並將它掛載到容器內的 `/etc/ipsec.d` 目錄下。IKEv2 的相關資料(例如憑證與金鑰)會保存在該卷中,之後當你需要重新建立 Docker 容器時,只需指定同一個卷。
建議在使用本映像時啟用 IKEv2。如果你不想啟用 IKEv2,而只使用 IPsec/L2TP 和 IPsec/XAuth("Cisco IPsec")模式連線到 VPN,可以移除上面 `docker run` 命令中的第一個 `-v` 選項。
**註:** 進階使用者也可以[在不啟用 privileged 模式下執行](docs/advanced-usage-zh.md#不启用-privileged-模式运行)。
### 取得 VPN 登入資訊
如果你在上述 `docker run` 命令中沒有指定 `env` 檔案,`VPN_USER` 會預設為 `vpnuser`,並且 `VPN_IPSEC_PSK` 和 `VPN_PASSWORD` 會自動隨機生成。要取得這些登入資訊,可以查看容器的日誌:
```
docker logs ipsec-vpn-server
```
在命令輸出中尋找以下幾行:
```
Connect to your new VPN with these details:
Server IP: 你的VPN伺服器IP
IPsec PSK: 你的IPsec預共享金鑰
Username: 你的VPN使用者名稱
Password: 你的VPN密碼
```
命令輸出中也會包含 IKEv2 設定資訊(如果已啟用)。
(可選步驟)將自動生成的 VPN 登入資訊(如果有)備份到目前目錄:
```
docker cp ipsec-vpn-server:/etc/ipsec.d/vpn-gen.env ./
```
## 下一步
*其他語言版本: [English](README.md#next-steps), [简体中文](README-zh.md#下一步), [繁體中文](README-zh-Hant.md#下一步), [Русский](README-ru.md#следующие-шаги)。*
設定你的電腦或其他裝置使用 VPN。請參見以下連結(簡體中文):
**[設定並使用 IKEv2 VPN(推薦)](#設定並使用-ikev2-vpn)**
**[設定 IPsec/L2TP VPN 客戶端](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md)**
**[設定 IPsec/XAuth ("Cisco IPsec") VPN 客戶端](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-xauth-zh.md)**
**閱讀 [:book: VPN book](docs/vpn-book-zh-Hant.md) 以存取[額外內容](https://ko-fi.com/post/Support-this-project-and-get-access-to-supporter-o-X8X5FVFZC)。**
開始使用自己的專屬 VPN! :sparkles::tada::rocket::sparkles:
## 重要提示
**Windows 使用者** 對於 IPsec/L2TP 模式,在首次連線之前需要[修改登錄檔](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#windows-错误-809),以解決 VPN 伺服器或客戶端與 NAT(例如家用路由器)的相容問題。
同一個 VPN 帳戶可以在你的多個裝置上使用。但由於 IPsec/L2TP 的限制,如果需要連線到同一個 NAT(例如家用路由器)後面的多個裝置,你必須使用 [IKEv2](#設定並使用-ikev2-vpn) 或 [IPsec/XAuth](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-xauth-zh.md) 模式。
如需新增、修改或刪除 VPN 使用者帳戶,請先更新你的 `env` 檔案,然後必須依照[下一節](#更新-docker-映像)的說明刪除並重新建立 Docker 容器。進階使用者可以[綁定掛載](docs/advanced-usage-zh.md#绑定挂载-env-文件) `env` 檔案。
對於有外部防火牆的伺服器(例如 [EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)/[GCE](https://cloud.google.com/vpc/docs/firewalls)),請為 VPN 開啟 UDP 連接埠 500 和 4500。阿里雲使用者請參見 [#433](https://github.com/hwdsl2/setup-ipsec-vpn/issues/433)。
在 VPN 已連線時,客戶端設定為使用 [Google Public DNS](https://developers.google.com/speed/public-dns/)。如果偏好其他的網域解析服務,請參見[這裡](docs/advanced-usage-zh.md#使用其他的-dns-服务器)。
## 更新 Docker 映像
要更新 Docker 映像與容器,請先[下載](#下載)最新版本:
```
docker pull hwdsl2/ipsec-vpn-server
```
如果 Docker 映像已經是最新版本,你會看到提示:
```
Status: Image is up to date for hwdsl2/ipsec-vpn-server:latest
```
否則將會下載最新版本。要更新你的 Docker 容器,請先在紙上記下所有的 [VPN 登入資訊](#取得-vpn-登入資訊)。然後刪除 Docker 容器:`docker rm -f ipsec-vpn-server`。最後依照[如何使用本映像](#如何使用本映像)的說明重新建立它。
## 設定並使用 IKEv2 VPN
IKEv2 模式是比 IPsec/L2TP 和 IPsec/XAuth("Cisco IPsec")更好的連線模式,該模式不需要 IPsec PSK、使用者名稱或密碼。更多資訊請參見[這裡](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto-zh.md)。
首先,查看容器的日誌以取得 IKEv2 設定資訊:
```bash
docker logs ipsec-vpn-server
```
**註:** 如果你找不到 IKEv2 設定資訊,IKEv2 可能未在容器中啟用。請嘗試依照[更新 Docker 映像](#更新-docker-映像)一節的說明更新 Docker 映像與容器。
在 IKEv2 安裝過程中會建立一個 IKEv2 客戶端(預設名稱為 `vpnclient`),並將其設定匯出到 **容器內** 的 `/etc/ipsec.d` 目錄下。你可以將設定檔複製到 Docker 主機:
```bash
# 查看容器內的 /etc/ipsec.d 目錄檔案
docker exec -it ipsec-vpn-server ls -l /etc/ipsec.d
# 範例:將一個客戶端設定檔從容器複製到 Docker 主機目前目錄
docker cp ipsec-vpn-server:/etc/ipsec.d/vpnclient.p12 ./
```
**下一步:** [設定你的裝置](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto-zh.md)以使用 IKEv2 VPN。
<details>
<summary>
了解如何管理 IKEv2 客戶端。
</summary>
你可以使用輔助腳本管理 IKEv2 客戶端。示例如下。如需自訂客戶端選項,可以在不加入參數的情況下執行腳本。
```bash
# 新增一個客戶端(使用預設選項)
docker exec -it ipsec-vpn-server ikev2.sh --addclient [client name]
# 匯出一個已有客戶端的設定
docker exec -it ipsec-vpn-server ikev2.sh --exportclient [client name]
# 列出已有的客戶端
docker exec -it ipsec-vpn-server ikev2.sh --listclients
# 顯示使用說明
docker exec -it ipsec-vpn-server ikev2.sh -h
```
**註:** 如果你遇到錯誤 "executable file not found",將上面的 `ikev2.sh` 改成 `/opt/src/ikev2.sh`。
</details>
<details>
<summary>
了解如何更改 IKEv2 伺服器位址。
</summary>
在某些情況下,你可能需要更改 IKEv2 伺服器位址。例如切換為使用網域名稱,或是在伺服器 IP 變更之後。要更改 IKEv2 伺服器位址,請先[在容器中執行 Bash shell](docs/advanced-usage-zh.md#在容器中运行-bash-shell),然後[依照這裡的說明操作](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto-zh.md#更改-ikev2-服务器地址)。請注意,在你重新啟動 Docker 容器之前,容器日誌不會顯示新的 IKEv2 伺服器位址。
</details>
<details>
<summary>
移除 IKEv2 並使用自訂選項重新設定。
</summary>
在某些情況下,你可能需要移除 IKEv2 並使用自訂選項重新設定它。
**警告:** 這將**永久刪除**所有 IKEv2 設定(包括憑證與金鑰),並且**無法復原**!
**選項 1:** 使用輔助腳本移除 IKEv2 並重新設定。
請注意,這將覆蓋你在 `env` 檔案中指定的變數,例如 `VPN_DNS_NAME` 和 `VPN_CLIENT_NAME`,而且容器日誌將不再顯示 IKEv2 的最新資訊。
```bash
# 移除 IKEv2 並刪除所有 IKEv2 設定
docker exec -it ipsec-vpn-server ikev2.sh --removeikev2
# 使用自訂選項重新設定 IKEv2
docker exec -it ipsec-vpn-server ikev2.sh
```
**選項 2:** 移除 `ikev2-vpn-data` 並重新建立容器。
1. 在紙上記下所有的 [VPN 登入資訊](#取得-vpn-登入資訊)。
1. 刪除 Docker 容器:`docker rm -f ipsec-vpn-server`。
1. 刪除 `ikev2-vpn-data` 卷:`docker volume rm ikev2-vpn-data`。
1. 更新你的 `env` 檔案並加入自訂 IKEv2 選項,例如 `VPN_DNS_NAME` 和 `VPN_CLIENT_NAME`,然後重新建立容器。請參見[如何使用本映像](#如何使用本映像)。
</details>
## 進階用法
請參見[進階用法](docs/advanced-usage-zh.md)(簡體中文)。
- [使用其他 DNS 伺服器](docs/advanced-usage-zh.md#使用其他的-dns-服务器)
- [不啟用 privileged 模式執行](docs/advanced-usage-zh.md#不启用-privileged-模式运行)
- [選擇 VPN 模式](docs/advanced-usage-zh.md#选择-vpn-模式)
- [存取 Docker 主機上的其他容器](docs/advanced-usage-zh.md#访问-docker-主机上的其它容器)
- [指定 VPN 伺服器的公有 IP](docs/advanced-usage-zh.md#指定-vpn-服务器的公有-ip)
- [為 VPN 客戶端指定靜態 IP](docs/advanced-usage-zh.md#为-vpn-客户端指定静态-ip)
- [自訂 VPN 子網](docs/advanced-usage-zh.md#自定义-vpn-子网)
- [IPv6 支援](docs/advanced-usage-zh.md#ipv6-支持)
- [VPN 分流](docs/advanced-usage-zh.md#vpn-分流)
- [關於 host network 模式](docs/advanced-usage-zh.md#关于-host-network-模式)
- [啟用 Libreswan 日誌](docs/advanced-usage-zh.md#启用-libreswan-日志)
- [查看伺服器狀態](docs/advanced-usage-zh.md#查看服务器状态)
- [從原始碼建構](docs/advanced-usage-zh.md#从源代码构建)
- [在容器中執行 Bash shell](docs/advanced-usage-zh.md#在容器中运行-bash-shell)
- [綁定掛載 env 檔案](docs/advanced-usage-zh.md#绑定挂载-env-文件)
- [部署 Google BBR 壅塞控制](docs/advanced-usage-zh.md#部署-google-bbr-拥塞控制)
## 技術細節
需要執行以下兩個服務:`Libreswan (pluto)` 提供 IPsec VPN,`xl2tpd` 提供 L2TP 支援。
預設的 IPsec 設定支援以下協定:
* IPsec/L2TP with PSK
* IKEv1 with PSK and XAuth ("Cisco IPsec")
* IKEv2
為了讓 VPN 伺服器正常運作,將會開啟以下連接埠:
* 4500/udp 和 500/udp 用於 IPsec
## 授權條款
**註:** 預先建構映像中的軟體元件(例如 Libreswan 和 xl2tpd)依其各自版權持有人所選擇的授權條款發布。對於任何預先建構映像的使用,使用者有責任確保其使用方式符合映像中所有軟體的相關授權條款。
版權所有 (C) 2016-2026 [Lin Song](https://github.com/hwdsl2) [](https://www.linkedin.com/in/linsongui)
基於 [Thomas Sarlandie 的工作](https://github.com/sarfata/voodooprivacy)(版權所有 2012)
[](http://creativecommons.org/licenses/by-sa/3.0/)
本專案採用 [Creative Commons 姓名標示-相同方式分享 3.0](http://creativecommons.org/licenses/by-sa/3.0/) 授權條款。
必須署名:請在任何衍生作品中包含我的名字,並讓我知道你是如何改進它的!
================================================
FILE: README-zh.md
================================================
[English](README.md) | [简体中文](README-zh.md) | [繁體中文](README-zh-Hant.md) | [Русский](README-ru.md)
# Docker 上的 IPsec VPN 服务器
[](https://github.com/hwdsl2/docker-ipsec-vpn-server/actions/workflows/main-alpine.yml) [](https://github.com/hwdsl2/docker-ipsec-vpn-server/stargazers) [](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/) [](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/)
使用这个 Docker 镜像快速搭建 IPsec VPN 服务器。支持 IPsec/L2TP,Cisco IPsec 和 IKEv2 协议。
本镜像以 Alpine 3.23 或 Debian 12 为基础,并使用 [Libreswan](https://libreswan.org) (IPsec VPN 软件) 和 [xl2tpd](https://github.com/xelerance/xl2tpd) (L2TP 服务进程)。
IPsec VPN 可以加密你的网络流量,以防止在通过因特网传送时,你和 VPN 服务器之间的任何人对你的数据的未经授权的访问。在使用不安全的网络时,这是特别有用的,例如在咖啡厅,机场或旅馆房间。
**[» :book: Book: Privacy Tools in the Age of AI](docs/vpn-book-zh.md) [搭建自己的 VPN 服务器](docs/vpn-book-zh.md)**
## 快速开始
使用以下命令在 Docker 上快速搭建 IPsec VPN 服务器:
```
docker run \
--name ipsec-vpn-server \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
你的 VPN 登录凭证将会被自动随机生成。请参见[获取 VPN 登录信息](#获取-vpn-登录信息)。
另外,你也可以在不使用 Docker 的情况下[安装 IPsec VPN](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/README-zh.md)。要了解更多有关如何使用本镜像的信息,请继续阅读以下部分。
## 功能特性
- 支持具有强大和快速加密算法(例如 AES-GCM)的 IKEv2 模式
- 生成 VPN 配置文件以自动配置 iOS, macOS 和 Android 设备
- 支持 Windows, macOS, iOS, Android, Chrome OS 和 Linux 客户端
- 包括辅助脚本以管理 IKEv2 用户和证书
## 安装 Docker
首先在你的 Linux 服务器上[安装 Docker](https://docs.docker.com/engine/install/)。另外你也可以使用 [Podman](https://podman.io) 运行本镜像,需要首先为 `docker` 命令[创建一个别名](https://podman.io/whatis.html)。
高级用户可以在 macOS 上通过安装 [Docker for Mac](https://docs.docker.com/docker-for-mac/) 使用本镜像。在使用 IPsec/L2TP 模式之前,你可能需要运行 `docker restart ipsec-vpn-server` 重启一次 Docker 容器。本镜像不支持 Docker for Windows。
## 下载
预构建的可信任镜像可在 [Docker Hub registry](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/) 下载:
```
docker pull hwdsl2/ipsec-vpn-server
```
或者,你也可以从 [Quay.io](https://quay.io/repository/hwdsl2/ipsec-vpn-server) 下载:
```
docker pull quay.io/hwdsl2/ipsec-vpn-server
docker image tag quay.io/hwdsl2/ipsec-vpn-server hwdsl2/ipsec-vpn-server
```
支持以下架构系统:`linux/amd64`, `linux/arm64` 和 `linux/arm/v7`。
高级用户可以自己从 GitHub [编译源代码](docs/advanced-usage-zh.md#从源代码构建)。
### 镜像对照表
有两个预构建的镜像可用。默认的基于 Alpine 的镜像大小仅 ~19 MB。
| | 基于 Alpine | 基于 Debian |
| --------------- | ------------------------ | ------------------------------ |
| 镜像名称 | hwdsl2/ipsec-vpn-server | hwdsl2/ipsec-vpn-server:debian |
| 压缩后大小 | ~ 19 MB | ~ 62 MB |
| 基础镜像 | Alpine Linux 3.23 | Debian Linux 12 |
| 系统架构 | amd64, arm64, arm/v7 | amd64, arm64, arm/v7 |
| Libreswan 版本 | 5.3 | 5.3 |
| IPsec/L2TP | ✅ | ✅ |
| Cisco IPsec | ✅ | ✅ |
| IKEv2 | ✅ | ✅ |
**注:** 要使用基于 Debian 的镜像,请将本自述文件中所有的 `hwdsl2/ipsec-vpn-server` 替换为 `hwdsl2/ipsec-vpn-server:debian`。这些镜像当前与 Synology NAS 系统不兼容。
<details>
<summary>
我需要使用较旧版本的 Libreswan 版本 4。
</summary>
一般建议使用最新的 [Libreswan](https://libreswan.org/) 版本 5,它是本项目的默认版本。但是,如果你想要使用较旧版本的 Libreswan 版本 4,你可以从源代码构建 Docker 镜像:
```
git clone https://github.com/hwdsl2/docker-ipsec-vpn-server
cd docker-ipsec-vpn-server
# Specify Libreswan version 4
sed -i 's/SWAN_VER=5\..*/SWAN_VER=4.15/' Dockerfile Dockerfile.debian
# To build Alpine-based image
docker build -t hwdsl2/ipsec-vpn-server .
# To build Debian-based image
docker build -f Dockerfile.debian -t hwdsl2/ipsec-vpn-server:debian .
```
</details>
## 如何使用本镜像
### 环境变量
**注:** 所有这些变量对于本镜像都是可选的,也就是说无需定义它们就可以搭建 IPsec VPN 服务器。你可以运行 `touch vpn.env` 创建一个空的 `env` 文件,然后跳到下一节。
这个 Docker 镜像使用以下几个变量,可以在一个 `env` 文件中定义(参见[示例](vpn.env.example)):
```
VPN_IPSEC_PSK=your_ipsec_pre_shared_key
VPN_USER=your_vpn_username
VPN_PASSWORD=your_vpn_password
```
这将创建一个用于 VPN 登录的用户账户,它可以在你的多个设备上使用[\*](#重要提示)。 IPsec PSK (预共享密钥) 由 `VPN_IPSEC_PSK` 环境变量指定。 VPN 用户名和密码分别在 `VPN_USER` 和 `VPN_PASSWORD` 中定义。
支持创建额外的 VPN 用户,如果需要,可以像下面这样在你的 `env` 文件中定义。用户名和密码必须分别使用空格进行分隔,并且用户名不能有重复。所有的 VPN 用户将共享同一个 IPsec PSK。
```
VPN_ADDL_USERS=additional_username_1 additional_username_2
VPN_ADDL_PASSWORDS=additional_password_1 additional_password_2
```
以上变量仅适用于 IPsec/L2TP 和 IPsec/XAuth ("Cisco IPsec") 模式。对于 IKEv2,参见[配置并使用 IKEv2 VPN](#配置并使用-ikev2-vpn)。
**注:** 在你的 `env` 文件中,**不要**为变量值添加 `""` 或者 `''`,或在 `=` 两边添加空格。**不要**在值中使用这些字符: `\ " '`。一个安全的 IPsec PSK 应该至少包含 20 个随机字符。
**注:** 如果在创建 Docker 容器后修改 `env` 文件,则必须删除并重新创建容器才能使更改生效。参见[更新 Docker 镜像](#更新-docker-镜像)。
### 其他环境变量
高级用户可以指定一个域名,客户端名称和/或另外的 DNS 服务器。这是可选的。
<details>
<summary>
了解如何指定一个域名,客户端名称和/或另外的 DNS 服务器。
</summary>
高级用户可以指定一个域名作为 IKEv2 服务器地址。这是可选的。该域名必须是一个全称域名 (FQDN)。示例如下:
```
VPN_DNS_NAME=vpn.example.com
```
你可以指定第一个 IKEv2 客户端的名称。该名称不能包含空格或者除 `-` `_` 之外的任何特殊字符。如果未指定,则使用默认值 `vpnclient`。
```
VPN_CLIENT_NAME=your_client_name
```
在 VPN 已连接时,客户端默认配置为使用 [Google Public DNS](https://developers.google.com/speed/public-dns/)。你可以为所有的 VPN 模式指定另外的 DNS 服务器。示例如下:
```
VPN_DNS_SRV1=1.1.1.1
VPN_DNS_SRV2=1.0.0.1
```
有关详细信息以及一些流行的公共 DNS 提供商的列表,参见[使用其他的 DNS 服务器](docs/advanced-usage-zh.md)。
默认情况下,导入 IKEv2 客户端配置时不需要密码。你可以选择使用随机密码保护客户端配置文件。
```
VPN_PROTECT_CONFIG=yes
```
**注:** 如果在 Docker 容器中已经配置了 IKEv2,则以上变量对 IKEv2 模式无效。在这种情况下,你可以移除 IKEv2 并使用自定义选项重新配置它。参见[配置并使用 IKEv2 VPN](#配置并使用-ikev2-vpn)。
</details>
### 运行 IPsec VPN 服务器
使用本镜像创建一个新的 Docker 容器(将 `./vpn.env` 替换为你自己的 `env` 文件):
```
docker run \
--name ipsec-vpn-server \
--env-file ./vpn.env \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
在该命令中,我们使用 `docker run` 的 `-v` 选项来创建一个名为 `ikev2-vpn-data` 的新 [Docker 卷](https://docs.docker.com/storage/volumes/),并且将它挂载到容器内的 `/etc/ipsec.d` 目录下。IKEv2 的相关数据(比如证书和密钥)在该卷中保存,之后当你需要重新创建 Docker 容器的时候,只需指定同一个卷。
推荐在使用本镜像时启用 IKEv2。如果你不想启用 IKEv2 而仅使用 IPsec/L2TP 和 IPsec/XAuth ("Cisco IPsec") 模式连接到 VPN,可以去掉上面 `docker run` 命令中的第一个 `-v` 选项。
**注:** 高级用户也可以[不启用 privileged 模式运行](docs/advanced-usage-zh.md#不启用-privileged-模式运行)。
### 获取 VPN 登录信息
如果你在上述 `docker run` 命令中没有指定 `env` 文件,`VPN_USER` 会默认为 `vpnuser`,并且 `VPN_IPSEC_PSK` 和 `VPN_PASSWORD` 会被自动随机生成。要获取这些登录信息,可以查看容器的日志:
```
docker logs ipsec-vpn-server
```
在命令输出中查找这些行:
```
Connect to your new VPN with these details:
Server IP: 你的VPN服务器IP
IPsec PSK: 你的IPsec预共享密钥
Username: 你的VPN用户名
Password: 你的VPN密码
```
在命令输出中也会包含 IKEv2 配置信息(如果已启用)。
(可选步骤)备份自动生成的 VPN 登录信息(如果有)到当前目录:
```
docker cp ipsec-vpn-server:/etc/ipsec.d/vpn-gen.env ./
```
## 下一步
*其他语言版本: [English](README.md#next-steps), [简体中文](README-zh.md#下一步), [繁體中文](README-zh-Hant.md#下一步), [Русский](README-ru.md#следующие-шаги)。*
配置你的计算机或其它设备使用 VPN。请参见:
**[配置并使用 IKEv2 VPN(推荐)](#配置并使用-ikev2-vpn)**
**[配置 IPsec/L2TP VPN 客户端](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md)**
**[配置 IPsec/XAuth ("Cisco IPsec") VPN 客户端](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-xauth-zh.md)**
**阅读 [:book: VPN book](docs/vpn-book-zh.md) 以访问[额外内容](https://ko-fi.com/post/Support-this-project-and-get-access-to-supporter-o-X8X5FVFZC)。**
开始使用自己的专属 VPN! :sparkles::tada::rocket::sparkles:
## 重要提示
**Windows 用户** 对于 IPsec/L2TP 模式,在首次连接之前需要[修改注册表](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#windows-错误-809),以解决 VPN 服务器或客户端与 NAT(比如家用路由器)的兼容问题。
同一个 VPN 账户可以在你的多个设备上使用。但是由于 IPsec/L2TP 的局限性,如果需要连接在同一个 NAT(比如家用路由器)后面的多个设备,你必须使用 [IKEv2](#配置并使用-ikev2-vpn) 或者 [IPsec/XAuth](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-xauth-zh.md) 模式。
如需添加,修改或者删除 VPN 用户账户,首先更新你的 `env` 文件,然后你必须按照[下一节](#更新-docker-镜像)的说明来删除并重新创建 Docker 容器。高级用户可以[绑定挂载](docs/advanced-usage-zh.md#绑定挂载-env-文件) `env` 文件。
对于有外部防火墙的服务器(比如 [EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)/[GCE](https://cloud.google.com/vpc/docs/firewalls)),请为 VPN 打开 UDP 端口 500 和 4500。阿里云用户请参见 [#433](https://github.com/hwdsl2/setup-ipsec-vpn/issues/433)。
在 VPN 已连接时,客户端配置为使用 [Google Public DNS](https://developers.google.com/speed/public-dns/)。如果偏好其它的域名解析服务,请看[这里](docs/advanced-usage-zh.md#使用其他的-dns-服务器)。
## 更新 Docker 镜像
要更新 Docker 镜像和容器,首先[下载](#下载)最新版本:
```
docker pull hwdsl2/ipsec-vpn-server
```
如果 Docker 镜像已经是最新的,你会看到提示:
```
Status: Image is up to date for hwdsl2/ipsec-vpn-server:latest
```
否则将会下载最新版本。要更新你的 Docker 容器,首先在纸上记下你所有的 [VPN 登录信息](#获取-vpn-登录信息)。然后删除 Docker 容器: `docker rm -f ipsec-vpn-server`。最后按照[如何使用本镜像](#如何使用本镜像)的说明来重新创建它。
## 配置并使用 IKEv2 VPN
IKEv2 模式是比 IPsec/L2TP 和 IPsec/XAuth ("Cisco IPsec") 更佳的连接模式,该模式无需 IPsec PSK, 用户名或密码。更多信息请看[这里](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto-zh.md)。
首先,查看容器的日志以获取 IKEv2 配置信息:
```bash
docker logs ipsec-vpn-server
```
**注:** 如果你无法找到 IKEv2 配置信息,IKEv2 可能没有在容器中启用。尝试按照[更新 Docker 镜像](#更新-docker-镜像)一节的说明更新 Docker 镜像和容器。
在 IKEv2 安装过程中会创建一个 IKEv2 客户端(默认名称为 `vpnclient`),并且导出它的配置到 **容器内** 的 `/etc/ipsec.d` 目录下。你可以将配置文件复制到 Docker 主机:
```bash
# 查看容器内的 /etc/ipsec.d 目录的文件
docker exec -it ipsec-vpn-server ls -l /etc/ipsec.d
# 示例:将一个客户端配置文件从容器复制到 Docker 主机当前目录
docker cp ipsec-vpn-server:/etc/ipsec.d/vpnclient.p12 ./
```
**下一步:** [配置你的设备](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto-zh.md)以使用 IKEv2 VPN。
<details>
<summary>
了解如何管理 IKEv2 客户端。
</summary>
你可以使用辅助脚本管理 IKEv2 客户端。示例如下。如需自定义客户端选项,可以在不添加参数的情况下运行脚本。
```bash
# 添加一个客户端(使用默认选项)
docker exec -it ipsec-vpn-server ikev2.sh --addclient [client name]
# 导出一个已有的客户端的配置
docker exec -it ipsec-vpn-server ikev2.sh --exportclient [client name]
# 列出已有的客户端
docker exec -it ipsec-vpn-server ikev2.sh --listclients
# 显示使用信息
docker exec -it ipsec-vpn-server ikev2.sh -h
```
**注:** 如果你遇到错误 "executable file not found",将上面的 `ikev2.sh` 换成 `/opt/src/ikev2.sh`。
</details>
<details>
<summary>
了解如何更改 IKEv2 服务器地址。
</summary>
在某些情况下,你可能需要更改 IKEv2 服务器地址。例如切换为使用域名,或者在服务器的 IP 更改之后。要更改 IKEv2 服务器地址,首先[在容器中运行 Bash shell](docs/advanced-usage-zh.md#在容器中运行-bash-shell),然后[按照这里的说明操作](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto-zh.md#更改-ikev2-服务器地址)。请注意,容器的日志在你重启 Docker 容器之前将不显示新的 IKEv2 服务器地址。
</details>
<details>
<summary>
移除 IKEv2 并使用自定义选项重新配置。
</summary>
在某些情况下,你可能需要移除 IKEv2 并使用自定义选项重新配置它。
**警告:** 这将**永久删除**所有的 IKEv2 配置(包括证书和密钥),并且**不可撤销**!
**选项 1:** 使用辅助脚本移除 IKEv2 并重新配置。
请注意,这将覆盖你在 `env` 文件中指定的变量,例如 `VPN_DNS_NAME` 和 `VPN_CLIENT_NAME`,并且容器的日志将不再显示 IKEv2 的最新信息。
```bash
# 移除 IKEv2 并删除所有的 IKEv2 配置
docker exec -it ipsec-vpn-server ikev2.sh --removeikev2
# 使用自定义选项重新配置 IKEv2
docker exec -it ipsec-vpn-server ikev2.sh
```
**选项 2:** 移除 `ikev2-vpn-data` 并重新创建容器。
1. 在纸上记下你所有的 [VPN 登录信息](#获取-vpn-登录信息)。
1. 删除 Docker 容器:`docker rm -f ipsec-vpn-server`。
1. 删除 `ikev2-vpn-data` 卷:`docker volume rm ikev2-vpn-data`。
1. 更新你的 `env` 文件并添加自定义 IKEv2 选项,例如 `VPN_DNS_NAME` 和 `VPN_CLIENT_NAME`,然后重新创建容器。参见[如何使用本镜像](#如何使用本镜像)。
</details>
## 高级用法
请参见[高级用法](docs/advanced-usage-zh.md)。
- [使用其他的 DNS 服务器](docs/advanced-usage-zh.md#使用其他的-dns-服务器)
- [不启用 privileged 模式运行](docs/advanced-usage-zh.md#不启用-privileged-模式运行)
- [选择 VPN 模式](docs/advanced-usage-zh.md#选择-vpn-模式)
- [访问 Docker 主机上的其它容器](docs/advanced-usage-zh.md#访问-docker-主机上的其它容器)
- [指定 VPN 服务器的公有 IP](docs/advanced-usage-zh.md#指定-vpn-服务器的公有-ip)
- [为 VPN 客户端指定静态 IP](docs/advanced-usage-zh.md#为-vpn-客户端指定静态-ip)
- [自定义 VPN 子网](docs/advanced-usage-zh.md#自定义-vpn-子网)
- [IPv6 支持](docs/advanced-usage-zh.md#ipv6-支持)
- [VPN 分流](docs/advanced-usage-zh.md#vpn-分流)
- [关于 host network 模式](docs/advanced-usage-zh.md#关于-host-network-模式)
- [启用 Libreswan 日志](docs/advanced-usage-zh.md#启用-libreswan-日志)
- [查看服务器状态](docs/advanced-usage-zh.md#查看服务器状态)
- [从源代码构建](docs/advanced-usage-zh.md#从源代码构建)
- [在容器中运行 Bash shell](docs/advanced-usage-zh.md#在容器中运行-bash-shell)
- [绑定挂载 env 文件](docs/advanced-usage-zh.md#绑定挂载-env-文件)
- [部署 Google BBR 拥塞控制](docs/advanced-usage-zh.md#部署-google-bbr-拥塞控制)
## 技术细节
需要运行以下两个服务:`Libreswan (pluto)` 提供 IPsec VPN,`xl2tpd` 提供 L2TP 支持。
默认的 IPsec 配置支持以下协议:
* IPsec/L2TP with PSK
* IKEv1 with PSK and XAuth ("Cisco IPsec")
* IKEv2
为使 VPN 服务器正常工作,将会打开以下端口:
* 4500/udp and 500/udp for IPsec
## 授权协议
**注:** 预构建镜像中的软件组件(例如 Libreswan 和 xl2tpd)在其各自版权所有者选择的相应许可下。对于任何预构建的镜像的使用,用户有责任确保对该镜像的任何使用符合其中包含的所有软件的任何相关许可。
版权所有 (C) 2016-2026 [Lin Song](https://github.com/hwdsl2) [](https://www.linkedin.com/in/linsongui)
基于 [Thomas Sarlandie 的工作](https://github.com/sarfata/voodooprivacy) (版权所有 2012)
[](http://creativecommons.org/licenses/by-sa/3.0/)
这个项目是以[知识共享署名-相同方式共享3.0](http://creativecommons.org/licenses/by-sa/3.0/) 许可协议授权。
必须署名: 请包括我的名字在任何衍生产品,并且让我知道你是如何改善它的!
================================================
FILE: README.md
================================================
[English](README.md) | [简体中文](README-zh.md) | [繁體中文](README-zh-Hant.md) | [Русский](README-ru.md)
# IPsec VPN Server on Docker
[](https://github.com/hwdsl2/docker-ipsec-vpn-server/actions/workflows/main-alpine.yml) [](https://github.com/hwdsl2/docker-ipsec-vpn-server/stargazers) [](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/) [](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/)
Docker image to run an IPsec VPN server, with IPsec/L2TP, Cisco IPsec and IKEv2.
Based on Alpine 3.23 or Debian 12 with [Libreswan](https://libreswan.org) (IPsec VPN software) and [xl2tpd](https://github.com/xelerance/xl2tpd) (L2TP daemon).
An IPsec VPN encrypts your network traffic, so that nobody between you and the VPN server can eavesdrop on your data as it travels via the Internet. This is especially useful when using unsecured networks, e.g. at coffee shops, airports or hotel rooms.
**[» :book: Book: Privacy Tools in the Age of AI](docs/vpn-book.md) [Build Your Own VPN Server](docs/vpn-book.md)**
## Quick start
Use this command to set up an IPsec VPN server on Docker:
```
docker run \
--name ipsec-vpn-server \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
Your VPN login details will be randomly generated. See [Retrieve VPN login details](#retrieve-vpn-login-details).
Alternatively, you may [set up IPsec VPN without Docker](https://github.com/hwdsl2/setup-ipsec-vpn). To learn more about how to use this image, read the sections below.
## Features
- Supports IKEv2 with strong and fast ciphers (e.g. AES-GCM)
- Generates VPN profiles to auto-configure iOS, macOS and Android devices
- Supports Windows, macOS, iOS, Android, Chrome OS and Linux as VPN clients
- Includes a helper script to manage IKEv2 users and certificates
## Install Docker
First, [install Docker](https://docs.docker.com/engine/install/) on your Linux server. You may also use [Podman](https://podman.io) to run this image, after [creating an alias](https://podman.io/whatis.html) for `docker`.
Advanced users can use this image on macOS with [Docker for Mac](https://docs.docker.com/docker-for-mac/). Before using IPsec/L2TP mode, you may need to restart the Docker container once with `docker restart ipsec-vpn-server`. This image does not support Docker for Windows.
## Download
Get the trusted build from the [Docker Hub registry](https://hub.docker.com/r/hwdsl2/ipsec-vpn-server/):
```
docker pull hwdsl2/ipsec-vpn-server
```
Alternatively, you may download from [Quay.io](https://quay.io/repository/hwdsl2/ipsec-vpn-server):
```
docker pull quay.io/hwdsl2/ipsec-vpn-server
docker image tag quay.io/hwdsl2/ipsec-vpn-server hwdsl2/ipsec-vpn-server
```
Supported platforms: `linux/amd64`, `linux/arm64` and `linux/arm/v7`.
Advanced users can [build from source code](docs/advanced-usage.md#build-from-source-code) on GitHub.
### Image comparison
Two pre-built images are available. The default Alpine-based image is only ~19 MB.
| | Alpine-based | Debian-based |
| ----------------- | ------------------------ | ------------------------------ |
| Image name | hwdsl2/ipsec-vpn-server | hwdsl2/ipsec-vpn-server:debian |
| Compressed size | ~ 19 MB | ~ 62 MB |
| Base image | Alpine Linux 3.23 | Debian Linux 12 |
| Platforms | amd64, arm64, arm/v7 | amd64, arm64, arm/v7 |
| Libreswan version | 5.3 | 5.3 |
| IPsec/L2TP | ✅ | ✅ |
| Cisco IPsec | ✅ | ✅ |
| IKEv2 | ✅ | ✅ |
**Note:** To use the Debian-based image, replace every `hwdsl2/ipsec-vpn-server` with `hwdsl2/ipsec-vpn-server:debian` in this README. These images are not currently compatible with Synology NAS systems.
<details>
<summary>
I want to use the older Libreswan version 4.
</summary>
It is generally recommended to use the latest [Libreswan](https://libreswan.org/) version 5, which is the default version in this project. However, if you want to use the older Libreswan version 4, you can build the Docker image from source code:
```
git clone https://github.com/hwdsl2/docker-ipsec-vpn-server
cd docker-ipsec-vpn-server
# Specify Libreswan version 4
sed -i 's/SWAN_VER=5\..*/SWAN_VER=4.15/' Dockerfile Dockerfile.debian
# To build Alpine-based image
docker build -t hwdsl2/ipsec-vpn-server .
# To build Debian-based image
docker build -f Dockerfile.debian -t hwdsl2/ipsec-vpn-server:debian .
```
</details>
## How to use this image
### Environment variables
**Note:** All the variables to this image are optional, which means you don't have to type in any variable, and you can have an IPsec VPN server out of the box! To do that, create an empty `env` file using `touch vpn.env`, and skip to the next section.
This Docker image uses the following variables, that can be declared in an `env` file (see [example](vpn.env.example)):
```
VPN_IPSEC_PSK=your_ipsec_pre_shared_key
VPN_USER=your_vpn_username
VPN_PASSWORD=your_vpn_password
```
This will create a user account for VPN login, which can be used by your multiple devices[\*](#important-notes). The IPsec PSK (pre-shared key) is specified by the `VPN_IPSEC_PSK` environment variable. The VPN username is defined in `VPN_USER`, and VPN password is specified by `VPN_PASSWORD`.
Additional VPN users are supported, and can be optionally declared in your `env` file like this. Usernames and passwords must be separated by spaces, and usernames cannot contain duplicates. All VPN users will share the same IPsec PSK.
```
VPN_ADDL_USERS=additional_username_1 additional_username_2
VPN_ADDL_PASSWORDS=additional_password_1 additional_password_2
```
The variables above are only for IPsec/L2TP and IPsec/XAuth ("Cisco IPsec") modes. For IKEv2, see [Configure and use IKEv2 VPN](#configure-and-use-ikev2-vpn).
**Note:** In your `env` file, DO NOT put `""` or `''` around values, or add space around `=`. DO NOT use these special characters within values: `\ " '`. A secure IPsec PSK should consist of at least 20 random characters.
**Note:** If you modify the `env` file after the Docker container is already created, you must remove and re-create the container for the changes to take effect. Refer to [Update Docker image](#update-docker-image).
### Additional environment variables
Advanced users can optionally specify a DNS name, client name and/or custom DNS servers.
<details>
<summary>
Learn how to specify a DNS name, client name and/or custom DNS servers.
</summary>
Advanced users can optionally specify a DNS name for the IKEv2 server address. The DNS name must be a fully qualified domain name (FQDN). Example:
```
VPN_DNS_NAME=vpn.example.com
```
You may specify a name for the first IKEv2 client. Use one word only, no special characters except `-` and `_`. The default is `vpnclient` if not specified.
```
VPN_CLIENT_NAME=your_client_name
```
By default, clients are set to use [Google Public DNS](https://developers.google.com/speed/public-dns/) when the VPN is active. You may specify custom DNS server(s) for all VPN modes. Example:
```
VPN_DNS_SRV1=1.1.1.1
VPN_DNS_SRV2=1.0.0.1
```
For more details and a list of some popular public DNS providers, see [Use alternative DNS servers](docs/advanced-usage.md).
By default, no password is required when importing IKEv2 client configuration. You can choose to protect client config files using a random password.
```
VPN_PROTECT_CONFIG=yes
```
**Note:** The variables above have no effect for IKEv2 mode, if IKEv2 is already set up in the Docker container. In this case, you may remove IKEv2 and set it up again using custom options. Refer to [Configure and use IKEv2 VPN](#configure-and-use-ikev2-vpn).
</details>
### Start the IPsec VPN server
Create a new Docker container from this image (replace `./vpn.env` with your own `env` file):
```
docker run \
--name ipsec-vpn-server \
--env-file ./vpn.env \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
In this command, we use the `-v` option of `docker run` to create a new [Docker volume](https://docs.docker.com/storage/volumes/) named `ikev2-vpn-data`, and mount it into `/etc/ipsec.d` in the container. IKEv2 related data such as certificates and keys will persist in the volume, and later when you need to re-create the Docker container, just specify the same volume again.
It is recommended to enable IKEv2 when using this image. However, if you prefer not to enable IKEv2 and use only the IPsec/L2TP and IPsec/XAuth ("Cisco IPsec") modes to connect to the VPN, remove the first `-v` option from the `docker run` command above.
**Note:** Advanced users can also [run without privileged mode](docs/advanced-usage.md#run-without-privileged-mode).
### Retrieve VPN login details
If you did not specify an `env` file in the `docker run` command above, `VPN_USER` will default to `vpnuser` and both `VPN_IPSEC_PSK` and `VPN_PASSWORD` will be randomly generated. To retrieve them, view the container logs:
```
docker logs ipsec-vpn-server
```
Search for these lines in the output:
```
Connect to your new VPN with these details:
Server IP: your_vpn_server_ip
IPsec PSK: your_ipsec_pre_shared_key
Username: your_vpn_username
Password: your_vpn_password
```
The output will also include details for IKEv2 mode, if enabled.
(Optional) Backup the generated VPN login details (if any) to the current directory:
```
docker cp ipsec-vpn-server:/etc/ipsec.d/vpn-gen.env ./
```
## Next steps
*Read this in other languages: [English](README.md#next-steps), [简体中文](README-zh.md#下一步), [繁體中文](README-zh-Hant.md#下一步), [Русский](README-ru.md#следующие-шаги).*
Get your computer or device to use the VPN. Please refer to:
**[Configure and use IKEv2 VPN (recommended)](#configure-and-use-ikev2-vpn)**
**[Configure IPsec/L2TP VPN Clients](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md)**
**[Configure IPsec/XAuth ("Cisco IPsec") VPN Clients](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-xauth.md)**
**Read [:book: VPN book](docs/vpn-book.md) to access [extra content](https://ko-fi.com/post/Support-this-project-and-get-access-to-supporter-o-O5O7FVF8J).**
Enjoy your very own VPN! :sparkles::tada::rocket::sparkles:
## Important notes
**Windows users**: For IPsec/L2TP mode, a [one-time registry change](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#windows-error-809) is required if the VPN server or client is behind NAT (e.g. home router).
The same VPN account can be used by your multiple devices. However, due to an IPsec/L2TP limitation, if you wish to connect multiple devices from behind the same NAT (e.g. home router), you must use [IKEv2](#configure-and-use-ikev2-vpn) or [IPsec/XAuth](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-xauth.md) mode.
If you wish to add, edit or remove VPN user accounts, first update your `env` file, then you must remove and re-create the Docker container using instructions from the [next section](#update-docker-image). Advanced users can [bind mount](docs/advanced-usage.md#bind-mount-the-env-file) the `env` file.
For servers with an external firewall (e.g. [EC2](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-security-groups.html)/[GCE](https://cloud.google.com/vpc/docs/firewalls)), open UDP ports 500 and 4500 for the VPN. Aliyun users, see [#433](https://github.com/hwdsl2/setup-ipsec-vpn/issues/433).
Clients are set to use [Google Public DNS](https://developers.google.com/speed/public-dns/) when the VPN is active. If another DNS provider is preferred, read [this section](docs/advanced-usage.md#use-alternative-dns-servers).
## Update Docker image
To update the Docker image and container, first [download](#download) the latest version:
```
docker pull hwdsl2/ipsec-vpn-server
```
If the Docker image is already up to date, you should see:
```
Status: Image is up to date for hwdsl2/ipsec-vpn-server:latest
```
Otherwise, it will download the latest version. To update your Docker container, first write down all your [VPN login details](#retrieve-vpn-login-details). Then remove the Docker container with `docker rm -f ipsec-vpn-server`. Finally, re-create it using instructions from [How to use this image](#how-to-use-this-image).
## Configure and use IKEv2 VPN
IKEv2 mode has improvements over IPsec/L2TP and IPsec/XAuth ("Cisco IPsec"), and does not require an IPsec PSK, username or password. Read more [here](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto.md).
First, check container logs to view details for IKEv2:
```bash
docker logs ipsec-vpn-server
```
**Note:** If you cannot find IKEv2 details, IKEv2 may not be enabled in the container. Try updating the Docker image and container using instructions from the [Update Docker image](#update-docker-image) section.
During IKEv2 setup, an IKEv2 client (with default name `vpnclient`) is created, with its configuration exported to `/etc/ipsec.d` **inside the container**. To copy config file(s) to the Docker host:
```bash
# Check contents of /etc/ipsec.d in the container
docker exec -it ipsec-vpn-server ls -l /etc/ipsec.d
# Example: Copy a client config file from the container
# to the current directory on the Docker host
docker cp ipsec-vpn-server:/etc/ipsec.d/vpnclient.p12 ./
```
**Next steps:** [Configure your devices](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto.md) to use the IKEv2 VPN.
<details>
<summary>
Learn how to manage IKEv2 clients.
</summary>
You can manage IKEv2 clients using the helper script. See examples below. To customize client options, run the script without arguments.
```bash
# Add a new client (using default options)
docker exec -it ipsec-vpn-server ikev2.sh --addclient [client name]
# Export configuration for an existing client
docker exec -it ipsec-vpn-server ikev2.sh --exportclient [client name]
# List existing clients
docker exec -it ipsec-vpn-server ikev2.sh --listclients
# Show usage
docker exec -it ipsec-vpn-server ikev2.sh -h
```
**Note:** If you encounter error "executable file not found", replace `ikev2.sh` above with `/opt/src/ikev2.sh`.
</details>
<details>
<summary>
Learn how to change the IKEv2 server address.
</summary>
In certain circumstances, you may need to change the IKEv2 server address. For example, to switch to use a DNS name, or after server IP changes. To change the IKEv2 server address, first [open a bash shell inside the container](docs/advanced-usage.md#bash-shell-inside-container), then [follow these instructions](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/ikev2-howto.md#change-ikev2-server-address). Note that the container logs will not show the new IKEv2 server address until you restart the Docker container.
</details>
<details>
<summary>
Remove IKEv2 and set it up again using custom options.
</summary>
In certain circumstances, you may need to remove IKEv2 and set it up again using custom options.
**Warning:** All IKEv2 configuration including certificates and keys will be **permanently deleted**. This **cannot be undone**!
**Option 1:** Remove IKEv2 and set it up again using the helper script.
Note that this will override variables you specified in the `env` file, such as `VPN_DNS_NAME` and `VPN_CLIENT_NAME`, and the container logs will no longer show up-to-date information for IKEv2.
```bash
# Remove IKEv2 and delete all IKEv2 configuration
docker exec -it ipsec-vpn-server ikev2.sh --removeikev2
# Set up IKEv2 again using custom options
docker exec -it ipsec-vpn-server ikev2.sh
```
**Option 2:** Remove `ikev2-vpn-data` and re-create the container.
1. Write down all your [VPN login details](#retrieve-vpn-login-details).
1. Remove the Docker container: `docker rm -f ipsec-vpn-server`.
1. Remove the `ikev2-vpn-data` volume: `docker volume rm ikev2-vpn-data`.
1. Update your `env` file and add custom IKEv2 options such as `VPN_DNS_NAME` and `VPN_CLIENT_NAME`, then re-create the container. Refer to [How to use this image](#how-to-use-this-image).
</details>
## Advanced usage
See [Advanced usage](docs/advanced-usage.md).
- [Use alternative DNS servers](docs/advanced-usage.md#use-alternative-dns-servers)
- [Run without privileged mode](docs/advanced-usage.md#run-without-privileged-mode)
- [Select VPN modes](docs/advanced-usage.md#select-vpn-modes)
- [Access other containers on the Docker host](docs/advanced-usage.md#access-other-containers-on-the-docker-host)
- [Specify VPN server's public IP](docs/advanced-usage.md#specify-vpn-servers-public-ip)
- [Assign static IPs to VPN clients](docs/advanced-usage.md#assign-static-ips-to-vpn-clients)
- [Customize VPN subnets](docs/advanced-usage.md#customize-vpn-subnets)
- [IPv6 support](docs/advanced-usage.md#ipv6-support)
- [Split tunneling](docs/advanced-usage.md#split-tunneling)
- [About host network mode](docs/advanced-usage.md#about-host-network-mode)
- [Enable Libreswan logs](docs/advanced-usage.md#enable-libreswan-logs)
- [Check server status](docs/advanced-usage.md#check-server-status)
- [Build from source code](docs/advanced-usage.md#build-from-source-code)
- [Bash shell inside container](docs/advanced-usage.md#bash-shell-inside-container)
- [Bind mount the env file](docs/advanced-usage.md#bind-mount-the-env-file)
- [Deploy Google BBR congestion control](docs/advanced-usage.md#deploy-google-bbr-congestion-control)
## Technical details
There are two services running: `Libreswan (pluto)` for the IPsec VPN, and `xl2tpd` for L2TP support.
The default IPsec configuration supports:
* IPsec/L2TP with PSK
* IKEv1 with PSK and XAuth ("Cisco IPsec")
* IKEv2
The ports that are exposed for this container to work are:
* 4500/udp and 500/udp for IPsec
## License
**Note:** The software components inside the pre-built image (such as Libreswan and xl2tpd) are under the respective licenses chosen by their respective copyright holders. As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within.
Copyright (C) 2016-2026 [Lin Song](https://github.com/hwdsl2) [](https://www.linkedin.com/in/linsongui)
Based on [the work of Thomas Sarlandie](https://github.com/sarfata/voodooprivacy) (Copyright 2012)
[](http://creativecommons.org/licenses/by-sa/3.0/)
This work is licensed under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/)
Attribution required: please include my name in any derivative and let me know how you have improved it!
================================================
FILE: docker-compose.yml
================================================
version: '3'
volumes:
ikev2-vpn-data:
services:
vpn:
image: hwdsl2/ipsec-vpn-server
restart: always
env_file:
- ./vpn.env
ports:
- "500:500/udp"
- "4500:4500/udp"
privileged: true
hostname: ipsec-vpn-server
container_name: ipsec-vpn-server
volumes:
- ikev2-vpn-data:/etc/ipsec.d
- /lib/modules:/lib/modules:ro
# To enable IPv6 support for IKEv2 clients, the Docker host must have a
# public IPv6 address. Uncomment the network section below and configure
# IPv6 in your Docker daemon (set "ipv6": true and "fixed-cidr-v6" in
# /etc/docker/daemon.json), then re-create this container.
# networks:
# vpnnet:
# ipv6_address: <assign_a_ula_ipv6_address>
# networks:
# vpnnet:
# enable_ipv6: true
# ipam:
# config:
# - subnet: fddd:1::/64
================================================
FILE: docs/advanced-usage-zh.md
================================================
[English](advanced-usage.md) | [中文](advanced-usage-zh.md)
# 高级用法
- [使用其他的 DNS 服务器](#使用其他的-dns-服务器)
- [不启用 privileged 模式运行](#不启用-privileged-模式运行)
- [选择 VPN 模式](#选择-vpn-模式)
- [访问 Docker 主机上的其它容器](#访问-docker-主机上的其它容器)
- [指定 VPN 服务器的公有 IP](#指定-vpn-服务器的公有-ip)
- [为 VPN 客户端指定静态 IP](#为-vpn-客户端指定静态-ip)
- [自定义 VPN 子网](#自定义-vpn-子网)
- [IPv6 支持](#ipv6-支持)
- [VPN 分流](#vpn-分流)
- [关于 host network 模式](#关于-host-network-模式)
- [启用 Libreswan 日志](#启用-libreswan-日志)
- [查看服务器状态](#查看服务器状态)
- [从源代码构建](#从源代码构建)
- [在容器中运行 Bash shell](#在容器中运行-bash-shell)
- [绑定挂载 env 文件](#绑定挂载-env-文件)
- [部署 Google BBR 拥塞控制](#部署-google-bbr-拥塞控制)
## 使用其他的 DNS 服务器
在 VPN 已连接时,客户端默认配置为使用 [Google Public DNS](https://developers.google.com/speed/public-dns/)。如果偏好其它的域名解析服务,你可以在 `env` 文件中定义 `VPN_DNS_SRV1` 和 `VPN_DNS_SRV2`(可选),然后按照[说明](../README-zh.md#更新-docker-镜像)重新创建 Docker 容器。示例如下:
```
VPN_DNS_SRV1=1.1.1.1
VPN_DNS_SRV2=1.0.0.1
```
使用 `VPN_DNS_SRV1` 指定主 DNS 服务器,使用 `VPN_DNS_SRV2` 指定辅助 DNS 服务器(可选)。
请注意,如果 Docker 容器中已经配置了 IKEv2,你还需要编辑 Docker 容器内的 `/etc/ipsec.d/ikev2.conf` 并将 `8.8.8.8` 和 `8.8.4.4` 替换为你的其他的 DNS 服务器,然后重新启动 Docker 容器。
以下是一些流行的公共 DNS 提供商的列表,供你参考。
| 提供商 | 主 DNS | 辅助 DNS | 注释 |
| ----- | ------ | ------- | ---- |
| [Google Public DNS](https://developers.google.com/speed/public-dns) | 8.8.8.8 | 8.8.4.4 | 本项目默认 |
| [Cloudflare](https://1.1.1.1/dns/) | 1.1.1.1 | 1.0.0.1 | 另见:[Cloudflare for families](https://1.1.1.1/family/) |
| [Quad9](https://www.quad9.net) | 9.9.9.9 | 149.112.112.112 | 阻止恶意域 |
| [OpenDNS](https://www.opendns.com/home-internet-security/) | 208.67.222.222 | 208.67.220.220 | 阻止网络钓鱼域,可配置。 |
| [CleanBrowsing](https://cleanbrowsing.org/filters/) | 185.228.168.9 | 185.228.169.9 | [域过滤器](https://cleanbrowsing.org/filters/)可用 |
| [NextDNS](https://nextdns.io/?from=bg25bwmp) | 按需选择 | 按需选择 | 广告拦截,免费套餐可用。[了解更多](https://nextdns.io/?from=bg25bwmp)。 |
| [Control D](https://controld.com/free-dns) | 按需选择 | 按需选择 | 广告拦截,可配置。[了解更多](https://controld.com/free-dns)。 |
## 不启用 privileged 模式运行
高级用户可以在不启用 [privileged 模式](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities)的情况下使用本镜像创建一个 Docker 容器(将以下命令中的 `./vpn.env` 替换为你自己的 `env` 文件)。
**注:** 如果要启用 [IPv6 支持](#ipv6-支持),你必须在下方的 `docker run` 命令中添加 `--sysctl net.ipv6.conf.all.forwarding=1`。
**注:** 如果你的 Docker 主机运行 CentOS Stream, Oracle Linux 8+, Rocky Linux 或者 AlmaLinux,推荐使用 [privileged 模式](../README-zh.md#运行-ipsec-vpn-服务器)。如果你想要不启用 privileged 模式运行,则 **必须** 在创建 Docker 容器之前以及系统启动时运行 `modprobe ip_tables`。
```
docker run \
--name ipsec-vpn-server \
--env-file ./vpn.env \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-p 500:500/udp \
-p 4500:4500/udp \
-d --cap-add=NET_ADMIN \
--device=/dev/ppp \
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv4.conf.all.accept_redirects=0 \
--sysctl net.ipv4.conf.all.send_redirects=0 \
--sysctl net.ipv4.conf.all.rp_filter=0 \
--sysctl net.ipv4.conf.default.accept_redirects=0 \
--sysctl net.ipv4.conf.default.send_redirects=0 \
--sysctl net.ipv4.conf.default.rp_filter=0 \
hwdsl2/ipsec-vpn-server
```
在不启用 privileged 模式运行时,容器不能更改 `sysctl` 设置。这可能会影响本镜像的某些功能。一个已知问题是 [Android/Linux MTU/MSS fix](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients-zh.md#androidlinux-mtumss-问题) 需要另外在 `docker run` 命令添加 `--sysctl net.ipv4.ip_no_pmtu_disc=1` 才有效。如果你遇到任何问题,可以尝试换用 [privileged 模式](../README-zh.md#运行-ipsec-vpn-服务器)重新创建容器。
在创建 Docker 容器之后,请转到[获取 VPN 登录信息](../README-zh.md#获取-vpn-登录信息)。
类似地,如果你使用 [Docker compose](https://docs.docker.com/compose/),可以将 [docker-compose.yml](../docker-compose.yml) 中的 `privileged: true` 替换为:
```
cap_add:
- NET_ADMIN
devices:
- "/dev/ppp:/dev/ppp"
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.accept_redirects=0
- net.ipv4.conf.all.send_redirects=0
- net.ipv4.conf.all.rp_filter=0
- net.ipv4.conf.default.accept_redirects=0
- net.ipv4.conf.default.send_redirects=0
- net.ipv4.conf.default.rp_filter=0
```
更多信息请参见 [compose file reference](https://docs.docker.com/compose/compose-file/)。
## 选择 VPN 模式
在使用此 Docker 镜像时,默认启用 IPsec/L2TP 和 IPsec/XAuth ("Cisco IPsec") 模式。此外,如果在创建 Docker 容器时在 `docker run` 命令中[指定](../README-zh.md#运行-ipsec-vpn-服务器)了 `-v ikev2-vpn-data:/etc/ipsec.d` 选项,则会启用 IKEv2 模式。
高级用户可以有选择性地禁用 VPN 模式,通过在 `env` 文件中设置以下变量并重新创建 Docker 容器来实现。
禁用 IPsec/L2TP 模式:`VPN_DISABLE_IPSEC_L2TP=yes`
禁用 IPsec/XAuth ("Cisco IPsec") 模式:`VPN_DISABLE_IPSEC_XAUTH=yes`
禁用 IPsec/L2TP 和 IPsec/XAuth 模式:`VPN_IKEV2_ONLY=yes`
## 访问 Docker 主机上的其它容器
连接到 VPN 后,VPN 客户端通常可以访问在同一 Docker 主机上其他容器中运行的服务,而无需进行其他配置。
例如,如果 IPsec VPN 服务器容器的 IP 为 `172.17.0.2`,并且一个 IP 为 `172.17.0.3` 的 Nginx 容器在同一 Docker 主机上运行,则 VPN 客户端可以使用 IP `172.17.0.3` 来访问 Nginx 容器上的服务。要找出分配给容器的 IP ,可以运行 `docker inspect <container name>`。
## 指定 VPN 服务器的公有 IP
在具有多个公有 IP 地址的 Docker 主机上,高级用户可以使用 `env` 文件中的变量 `VPN_PUBLIC_IP` 为 VPN 服务器指定一个公有 IP,然后重新创建 Docker 容器。例如,如果 Docker 主机的 IP 为 `192.0.2.1` 和 `192.0.2.2`,并且你想要 VPN 服务器使用 `192.0.2.2`:
```
VPN_PUBLIC_IP=192.0.2.2
```
请注意,如果在 Docker 容器中已经配置了 IKEv2,则此变量对 IKEv2 模式无效。在这种情况下,你可以移除 IKEv2 并使用自定义选项重新配置它。参见[配置并使用 IKEv2 VPN](../README-zh.md#配置并使用-ikev2-vpn)。
如果你想要 VPN 客户端在 VPN 连接处于活动状态时使用指定的公有 IP 作为其 "出站 IP",并且指定的 IP **不是** Docker 主机上的主 IP(或默认路由),则可能需要额外的配置。在这种情况下,你可以尝试在 Docker 主机上添加一个 IPTables `SNAT` 规则。如果要在重启后继续有效,你可以将命令添加到 `/etc/rc.local`。
继续上面的例子,如果 Docker 容器具有内部 IP `172.17.0.2`(使用 `docker inspect ipsec-vpn-server` 查看),Docker 的网络接口名称为 `docker0`(使用 `iptables -nvL -t nat` 查看),并且你希望 "出站 IP" 为 `192.0.2.2`:
```
iptables -t nat -I POSTROUTING -s 172.17.0.2 ! -o docker0 -j SNAT --to 192.0.2.2
```
要检查一个已连接的 VPN 客户端的 "出站 IP",你可以在该客户端上打开浏览器并到[这里](https://www.ipchicken.com)检测 IP 地址。
## 为 VPN 客户端指定静态 IP
在使用 IPsec/L2TP 模式连接时,VPN 服务器(Docker 容器)在虚拟网络 `192.168.42.0/24` 内具有内网 IP `192.168.42.1`。为客户端分配的内网 IP 在这个范围内:`192.168.42.10` 到 `192.168.42.250`。要找到为特定的客户端分配的 IP,可以查看该 VPN 客户端上的连接状态。
在使用 IPsec/XAuth ("Cisco IPsec") 或 IKEv2 模式连接时,VPN 服务器(Docker 容器)在虚拟网络 `192.168.43.0/24` 内 **没有** 内网 IP。为客户端分配的内网 IP 在这个范围内:`192.168.43.10` 到 `192.168.43.250`。
高级用户可以将静态 IP 分配给 VPN 客户端。这是可选的。对于 **IKEv2 模式**,首先[在容器中运行 Bash shell](#在容器中运行-bash-shell),然后按照[VPN 内网 IP 和流量](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/advanced-usage-zh.md#vpn-内网-ip-和流量)中的 "IKEv2 模式:为 VPN 客户端分配静态 IP" 小节中的步骤操作。请注意,你需要跳过第 2, 3 和 5 步,并且在完成后重启 Docker 容器。这是因为对 `/etc/ipsec.conf` 的修改可能会在重启容器时被覆盖。
以下说明 **仅适用于** IPsec/L2TP 和 IPsec/XAuth ("Cisco IPsec") 模式。
要分配静态 IP,在你的 `env` 文件中定义 `VPN_ADDL_IP_ADDRS` 变量,然后重新创建 Docker 容器。例如:
```
VPN_ADDL_USERS=user1 user2 user3 user4 user5
VPN_ADDL_PASSWORDS=pass1 pass2 pass3 pass4 pass5
VPN_ADDL_IP_ADDRS=* * 192.168.42.2 192.168.43.2
```
在此示例中,我们为 IPsec/L2TP 模式的 `user3` 分配静态 IP `192.168.42.2`,并为 IPsec/XAuth ("Cisco IPsec") 模式的 `user4` 分配静态 IP `192.168.43.2`。`user1`, `user2` 和 `user5` 的内网 IP 将被自动分配。`user3` 在 IPsec/XAuth 模式下的内网 IP 和 `user4` 在 IPsec/L2TP 模式下的内网 IP 也将被自动分配。你可以使用 `*` 来指定自动分配的 IP,或者将这些用户放在列表的末尾。
你为 IPsec/L2TP 模式指定的静态 IP 必须在 `192.168.42.2` 到 `192.168.42.9` 范围内。你为 IPsec/XAuth ("Cisco IPsec") 模式指定的静态 IP 必须在 `192.168.43.2` 到 `192.168.43.9` 范围内。
如果你需要分配更多静态 IP,则必须缩小自动分配的 IP 地址池。示例如下:
```
VPN_L2TP_POOL=192.168.42.100-192.168.42.250
VPN_XAUTH_POOL=192.168.43.100-192.168.43.250
```
这将允许你为 IPsec/L2TP 模式在 `192.168.42.2` 到 `192.168.42.99` 范围内分配静态 IP,并且为 IPsec/XAuth ("Cisco IPsec") 模式在 `192.168.43.2` 到 `192.168.43.99` 范围内分配静态 IP。
请注意,如果你在 `env` 文件中指定了 `VPN_XAUTH_POOL`,并且在 Docker 容器中已经配置了 IKEv2,你 **必须** 在重新创建 Docker 容器之前手动编辑容器内的 `/etc/ipsec.d/ikev2.conf` 并将 `rightaddresspool=192.168.43.10-192.168.43.250` 替换为与 `VPN_XAUTH_POOL` **相同的值**。否则 IKEv2 可能会停止工作。
**注:** 在你的 `env` 文件中,**不要**为变量值添加 `""` 或者 `''`,或在 `=` 两边添加空格。**不要**在值中使用这些字符: `\ " '`。
## 自定义 VPN 子网
默认情况下,IPsec/L2TP VPN 客户端将使用内部 VPN 子网 `192.168.42.0/24`,而 IPsec/XAuth ("Cisco IPsec") 和 IKEv2 VPN 客户端将使用内部 VPN 子网 `192.168.43.0/24`。有关更多详细信息,请阅读上一节。
对于大多数用例,没有必要也 **不建议** 自定义这些子网。但是,如果你的用例需要它,你可以在 `env` 文件中指定自定义子网,然后你必须重新创建 Docker 容器。
```
# 示例:为 IPsec/L2TP 模式指定自定义 VPN 子网
# 注:必须指定所有三个变量。
VPN_L2TP_NET=10.1.0.0/16
VPN_L2TP_LOCAL=10.1.0.1
VPN_L2TP_POOL=10.1.0.10-10.1.254.254
```
```
# 示例:为 IPsec/XAuth 和 IKEv2 模式指定自定义 VPN 子网
# 注:必须指定以下两个变量。
VPN_XAUTH_NET=10.2.0.0/16
VPN_XAUTH_POOL=10.2.0.10-10.2.254.254
```
**注:** 在你的 `env` 文件中,**不要**为变量值添加 `""` 或者 `''`,或在 `=` 两边添加空格。
在上面的例子中,`VPN_L2TP_LOCAL` 是在 IPsec/L2TP 模式下的 VPN 服务器的内网 IP。`VPN_L2TP_POOL` 和 `VPN_XAUTH_POOL` 是为 VPN 客户端自动分配的 IP 地址池。
请注意,如果你在 `env` 文件中指定了 `VPN_XAUTH_POOL`,并且在 Docker 容器中已经配置了 IKEv2,你 **必须** 在重新创建 Docker 容器之前手动编辑容器内的 `/etc/ipsec.d/ikev2.conf` 并将 `rightaddresspool=192.168.43.10-192.168.43.250` 替换为与 `VPN_XAUTH_POOL` **相同的值**。否则 IKEv2 可能会停止工作。
## IPv6 支持
如果 Docker 宿主机拥有公共(全局单播)IPv6 地址并且满足以下要求,IKEv2 客户端的 IPv6 支持将在容器启动时自动启用,无需手动配置。
**注:** IPv6 支持已在 Android 上使用 strongSwan VPN 客户端进行测试。其他平台(例如 Windows、macOS、iOS)可能存在限制,或者需要进行额外配置才能使 IPv6 通过 IKEv2 VPN 正常工作。
**注:** 对于 **Windows** 客户端,你需要在 PowerShell 窗口中运行以下命令一次,以通过 VPN 路由 IPv6 流量。将 `IKEv2 VPN X.X.X.X` 替换为你的 VPN 连接的实际名称。完成后,重新连接到 IKEv2 VPN。
```powershell
Add-VpnConnectionRoute -ConnectionName "IKEv2 VPN X.X.X.X" -DestinationPrefix ::/1
Add-VpnConnectionRoute -ConnectionName "IKEv2 VPN X.X.X.X" -DestinationPrefix 8000::/1
```
启用 IPv6 后,IKEv2 VPN 客户端将同时获得来自 `192.168.43.0/24` 地址池的 IPv4 地址和来自 `fddd:500:500:500::/64` 地址池的 IPv6 地址。容器会将来自 VPN 客户端的 IPv6 流量通过宿主机的 IPv6 地址进行伪装(NAT),使 VPN 客户端通过隧道获得完整的 IPv6 互联网访问。
**要求:**
- Docker 宿主机必须拥有可路由的全局单播 IPv6 地址(以 `2` 或 `3` 开头)。链路本地地址 (`fe80::/10`) 不满足要求。
- 必须为 Docker 容器启用 IPv6。参见[在 Docker 中启用 IPv6 支持](https://docs.docker.com/engine/daemon/ipv6/)。
- Libreswan 5.0 或更新版本(Docker 镜像默认包含 5.x)。
- IPv6 仅支持 **IKEv2 模式**。IPsec/L2TP 和 IPsec/XAuth ("Cisco IPsec") 模式不支持 IPv6。
要为 Docker 容器启用 IPv6,首先在 Docker 宿主机上将以下内容添加到 `/etc/docker/daemon.json`,然后重启 Docker:
```json
{
"ipv6": true,
"fixed-cidr-v6": "fddd:1::/64"
}
```
然后重新创建容器。`run.sh` 脚本将检测容器的公共 IPv6 地址并自动配置 IPv6 支持。
要验证 IPv6 是否正常工作,请使用 IKEv2 连接到 VPN,然后检查你的 IPv6 地址,例如使用 [test-ipv6.com](https://test-ipv6.com)。
**注:** 如果[不启用 privileged 模式运行](#不启用-privileged-模式运行) Docker 容器,你必须在 `docker run` 命令中添加 `--sysctl net.ipv6.conf.all.forwarding=1`。
**现有容器注意事项:** 如果你的容器中已经配置了 IKEv2(即 `ikev2-vpn-data` 卷中已存在 `ikev2.conf`),在容器重启或重新创建时 IPv6 **不会**自动添加到现有的 IKEv2 配置中。要为 IKEv2 启用完整的 IPv6 支持,必须移除 IKEv2 并重新配置。参见[配置并使用 IKEv2 VPN](../README-zh.md#配置并使用-ikev2-vpn) 中的"移除 IKEv2"部分。
你也可以在 `env` 文件中设置 `VPN_IP6_NET`,在重新创建容器之前自定义 IPv6 地址池子网:
```
# 示例:为 IKEv2 模式指定自定义 IPv6 地址池子网
# 必须是 ULA 范围内的 /64 子网
VPN_IP6_NET=fddd:1234:5678:9012::/64
```
## VPN 分流
在启用 VPN 分流 (split tunneling) 时,VPN 客户端将仅通过 VPN 隧道发送特定目标子网的流量。其他流量 **不会** 通过 VPN 隧道。这允许你通过 VPN 安全访问指定的网络,而无需通过 VPN 发送所有客户端的流量。VPN 分流有一些局限性,而且并非所有的 VPN 客户端都支持。
高级用户可以为 IKEv2 模式启用 VPN 分流。这是可选的。将变量 `VPN_SPLIT_IKEV2` 添加到你的 `env` 文件,然后重新创建 Docker 容器。例如,如果目标子网是 `10.123.123.0/24`:
```
VPN_SPLIT_IKEV2=10.123.123.0/24
```
请注意,如果在 Docker 容器中已经配置了 IKEv2,则此变量无效。在这种情况下,有两个选项:
**选项 1:** 首先[在容器中运行 Bash shell](#在容器中运行-bash-shell),然后编辑 `/etc/ipsec.d/ikev2.conf` 并将 `leftsubnet=0.0.0.0/0` 替换为你想要的子网。在完成后,退出容器并运行 `docker restart ipsec-vpn-server`。
**选项 2:** 删除 Docker 容器以及 `ikev2-vpn-data` 卷,然后重新创建容器。这将**永久删除**所有的 VPN 配置。参见[配置并使用 IKEv2 VPN](../README-zh.md#配置并使用-ikev2-vpn) 中的"移除 IKEv2"部分。
另外,Windows 用户也可以通过手动添加路由的方式启用 VPN 分流。有关详细信息,请参阅 [VPN 分流](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/advanced-usage-zh.md#vpn-分流)。
## 关于 host network 模式
高级用户可以使用 [host network 模式](https://docs.docker.com/network/host/)运行本镜像,通过为 `docker run` 命令添加 `--network=host` 参数来实现。
在非必要的情况下,**不推荐**使用 host network 模式运行本镜像。在该模式下,容器的网络栈未与 Docker 主机隔离,从而在使用 IPsec/L2TP 模式连接之后,VPN 客户端可以使用 Docker 主机的 VPN 内网 IP `192.168.42.1` 访问主机上的端口或服务。请注意,当你不再使用本镜像时,你需要手动清理 [run.sh](../run.sh) 所更改的 IPTables 规则和 sysctl 设置,或者重启服务器。
某些 Docker 主机操作系统,比如 Debian 10,不能使用 host network 模式运行本镜像,因为它们使用 nftables。
## 启用 Libreswan 日志
为了保持较小的 Docker 镜像,Libreswan (IPsec) 日志默认未开启。如果你需要启用它以进行故障排除,首先在正在运行的 Docker 容器中开始一个 Bash 会话:
```
docker exec -it ipsec-vpn-server env TERM=xterm bash -l
```
然后运行以下命令:
```
# For Alpine-based image
apk add --no-cache rsyslog
rsyslogd
rc-service ipsec stop; rc-service -D ipsec start >/dev/null 2>&1
sed -i '\|pluto\.pid|a rm -f /var/run/rsyslogd.pid; rsyslogd' /opt/src/run.sh
exit
# For Debian-based image
apt-get update && apt-get -y install rsyslog
rsyslogd
service ipsec restart
sed -i '\|pluto\.pid|a rm -f /var/run/rsyslogd.pid; rsyslogd' /opt/src/run.sh
exit
```
**注:** 如果你在不启用 privileged 模式的情况下使用本镜像,则错误 `rsyslogd: imklog: cannot open kernel log` 是正常的。
完成后你可以这样查看 Libreswan 日志:
```
docker exec -it ipsec-vpn-server grep pluto /var/log/auth.log
```
如需查看 xl2tpd 日志,请运行 `docker logs ipsec-vpn-server`。
## 查看服务器状态
检查 IPsec VPN 服务器状态:
```
docker exec -it ipsec-vpn-server ipsec status
```
查看当前已建立的 VPN 连接:
```
docker exec -it ipsec-vpn-server ipsec trafficstatus
```
## 从源代码构建
高级用户可以从 GitHub 下载并自行编译源代码:
```
git clone https://github.com/hwdsl2/docker-ipsec-vpn-server
cd docker-ipsec-vpn-server
# To build Alpine-based image
docker build -t hwdsl2/ipsec-vpn-server .
# To build Debian-based image
docker build -f Dockerfile.debian -t hwdsl2/ipsec-vpn-server:debian .
```
若不需要改动源码,也可以这样:
```
# To build Alpine-based image
docker build -t hwdsl2/ipsec-vpn-server github.com/hwdsl2/docker-ipsec-vpn-server
# To build Debian-based image
docker build -f Dockerfile.debian -t hwdsl2/ipsec-vpn-server:debian \
github.com/hwdsl2/docker-ipsec-vpn-server
```
## 在容器中运行 Bash shell
在正在运行的 Docker 容器中开始一个 Bash 会话:
```
docker exec -it ipsec-vpn-server env TERM=xterm bash -l
```
(可选步骤)安装 `nano` 编辑器:
```
# For Alpine-based image
apk add --no-cache nano
# For Debian-based image
apt-get update && apt-get -y install nano
```
然后在容器中运行你的命令。完成后退出并重启 Docker 容器(如果需要):
```
exit
docker restart ipsec-vpn-server
```
## 绑定挂载 env 文件
作为 `--env-file` 选项的替代方案,高级用户可以绑定挂载 `env` 文件。该方法的好处是你在更新 `env` 文件之后可以重启 Docker 容器以生效,而不需要重新创建它。要使用这个方法,你必须首先编辑你的 `env` 文件并将所有的变量值用单引号 `''` 括起来。然后(重新)创建 Docker 容器(将第一个 `vpn.env` 替换为你自己的 `env` 文件):
```
docker run \
--name ipsec-vpn-server \
--restart=always \
-v "$(pwd)/vpn.env:/opt/src/env/vpn.env:ro" \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
## 部署 Google BBR 拥塞控制
VPN 服务器搭建完成后,可以通过在 Docker 主机上部署 Google BBR 拥塞控制算法提升性能。
这通常只需要在配置文件 `/etc/sysctl.conf` 中插入设定即可完成。但是部分 Linux 发行版可能需要额外更新 Linux 内核。
详细的部署方法,可以参考[这篇文档](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/bbr-zh.md)。在完成后重启 Docker 容器:
```
docker restart ipsec-vpn-server
```
## 授权协议
**注:** 预构建镜像中的软件组件(例如 Libreswan 和 xl2tpd)在其各自版权所有者选择的相应许可下。对于任何预构建的镜像的使用,用户有责任确保对该镜像的任何使用符合其中包含的所有软件的任何相关许可。
版权所有 (C) 2016-2026 [Lin Song](https://github.com/hwdsl2) [](https://www.linkedin.com/in/linsongui)
[](http://creativecommons.org/licenses/by-sa/3.0/)
这个项目是以[知识共享署名-相同方式共享3.0](http://creativecommons.org/licenses/by-sa/3.0/) 许可协议授权。
必须署名: 请包括我的名字在任何衍生产品,并且让我知道你是如何改善它的!
================================================
FILE: docs/advanced-usage.md
================================================
[English](advanced-usage.md) | [中文](advanced-usage-zh.md)
# Advanced Usage
- [Use alternative DNS servers](#use-alternative-dns-servers)
- [Run without privileged mode](#run-without-privileged-mode)
- [Select VPN modes](#select-vpn-modes)
- [Access other containers on the Docker host](#access-other-containers-on-the-docker-host)
- [Specify VPN server's public IP](#specify-vpn-servers-public-ip)
- [Assign static IPs to VPN clients](#assign-static-ips-to-vpn-clients)
- [Customize VPN subnets](#customize-vpn-subnets)
- [IPv6 support](#ipv6-support)
- [Split tunneling](#split-tunneling)
- [About host network mode](#about-host-network-mode)
- [Enable Libreswan logs](#enable-libreswan-logs)
- [Check server status](#check-server-status)
- [Build from source code](#build-from-source-code)
- [Bash shell inside container](#bash-shell-inside-container)
- [Bind mount the env file](#bind-mount-the-env-file)
- [Deploy Google BBR congestion control](#deploy-google-bbr-congestion-control)
## Use alternative DNS servers
By default, clients are set to use [Google Public DNS](https://developers.google.com/speed/public-dns/) when the VPN is active. If another DNS provider is preferred, define `VPN_DNS_SRV1` and optionally `VPN_DNS_SRV2` in your `env` file, then follow [instructions](../README.md#update-docker-image) to re-create the Docker container. Example:
```
VPN_DNS_SRV1=1.1.1.1
VPN_DNS_SRV2=1.0.0.1
```
Use `VPN_DNS_SRV1` to specify the primary DNS server, and `VPN_DNS_SRV2` to specify the secondary DNS server (optional).
Note that if IKEv2 is already set up in the Docker container, you will also need to edit `/etc/ipsec.d/ikev2.conf` inside the Docker container and replace `8.8.8.8` and `8.8.4.4` with your alternative DNS server(s), then restart the Docker container.
Below is a list of some popular public DNS providers for your reference.
| Provider | Primary DNS | Secondary DNS | Notes |
| -------- | ----------- | ------------- | ----- |
| [Google Public DNS](https://developers.google.com/speed/public-dns) | 8.8.8.8 | 8.8.4.4 | Default in this project |
| [Cloudflare](https://1.1.1.1/dns/) | 1.1.1.1 | 1.0.0.1 | See also: [Cloudflare for families](https://1.1.1.1/family/) |
| [Quad9](https://www.quad9.net) | 9.9.9.9 | 149.112.112.112 | Blocks malicious domains |
| [OpenDNS](https://www.opendns.com/home-internet-security/) | 208.67.222.222 | 208.67.220.220 | Blocks phishing domains, configurable. |
| [CleanBrowsing](https://cleanbrowsing.org/filters/) | 185.228.168.9 | 185.228.169.9 | [Domain filters](https://cleanbrowsing.org/filters/) available |
| [NextDNS](https://nextdns.io/?from=bg25bwmp) | Varies | Varies | Ad blocking, free tier available. [Learn more](https://nextdns.io/?from=bg25bwmp). |
| [Control D](https://controld.com/free-dns) | Varies | Varies | Ad blocking, configurable. [Learn more](https://controld.com/free-dns). |
## Run without privileged mode
Advanced users can create a Docker container from this image without using [privileged mode](https://docs.docker.com/engine/reference/run/#runtime-privilege-and-linux-capabilities) (replace `./vpn.env` in the command below with your own `env` file).
**Note:** For [IPv6 support](#ipv6-support) to work, you must add `--sysctl net.ipv6.conf.all.forwarding=1` to the `docker run` command below.
**Note:** If your Docker host runs CentOS Stream, Oracle Linux 8+, Rocky Linux or AlmaLinux, it is recommended to use [privileged mode](../README.md#start-the-ipsec-vpn-server). If you want to run without privileged mode, you **must** run `modprobe ip_tables` before creating the Docker container and also on boot.
```
docker run \
--name ipsec-vpn-server \
--env-file ./vpn.env \
--restart=always \
-v ikev2-vpn-data:/etc/ipsec.d \
-p 500:500/udp \
-p 4500:4500/udp \
-d --cap-add=NET_ADMIN \
--device=/dev/ppp \
--sysctl net.ipv4.ip_forward=1 \
--sysctl net.ipv4.conf.all.accept_redirects=0 \
--sysctl net.ipv4.conf.all.send_redirects=0 \
--sysctl net.ipv4.conf.all.rp_filter=0 \
--sysctl net.ipv4.conf.default.accept_redirects=0 \
--sysctl net.ipv4.conf.default.send_redirects=0 \
--sysctl net.ipv4.conf.default.rp_filter=0 \
hwdsl2/ipsec-vpn-server
```
When running without privileged mode, the container is unable to change `sysctl` settings. This could affect certain features of this image. A known issue is that the [Android/Linux MTU/MSS fix](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/clients.md#androidlinux-mtumss-issues) also requires adding `--sysctl net.ipv4.ip_no_pmtu_disc=1` to the `docker run` command. If you encounter any issues, try re-creating the container using [privileged mode](../README.md#start-the-ipsec-vpn-server).
After creating the Docker container, see [Retrieve VPN login details](../README.md#retrieve-vpn-login-details).
Similarly, if using [Docker compose](https://docs.docker.com/compose/), you may replace `privileged: true` in [docker-compose.yml](../docker-compose.yml) with:
```
cap_add:
- NET_ADMIN
devices:
- "/dev/ppp:/dev/ppp"
sysctls:
- net.ipv4.ip_forward=1
- net.ipv4.conf.all.accept_redirects=0
- net.ipv4.conf.all.send_redirects=0
- net.ipv4.conf.all.rp_filter=0
- net.ipv4.conf.default.accept_redirects=0
- net.ipv4.conf.default.send_redirects=0
- net.ipv4.conf.default.rp_filter=0
```
For more information, see [compose file reference](https://docs.docker.com/compose/compose-file/).
## Select VPN modes
Using this Docker image, the IPsec/L2TP and IPsec/XAuth ("Cisco IPsec") modes are enabled by default. In addition, IKEv2 mode will be enabled if the `-v ikev2-vpn-data:/etc/ipsec.d` option [is specified](../README.md#start-the-ipsec-vpn-server) in the `docker run` command when creating the Docker container.
Advanced users can selectively disable VPN modes by setting the following variable(s) in the `env` file, then re-create the Docker container.
Disable IPsec/L2TP mode: `VPN_DISABLE_IPSEC_L2TP=yes`
Disable IPsec/XAuth ("Cisco IPsec") mode: `VPN_DISABLE_IPSEC_XAUTH=yes`
Disable both IPsec/L2TP and IPsec/XAuth modes: `VPN_IKEV2_ONLY=yes`
## Access other containers on the Docker host
After connecting to the VPN, VPN clients can generally access services running in other containers on the same Docker host, without additional configuration.
For example, if the IPsec VPN server container has IP `172.17.0.2`, and an Nginx container with IP `172.17.0.3` is running on the same Docker host, VPN clients can use IP `172.17.0.3` to access services on the Nginx container. To find out which IP is assigned to a container, run `docker inspect <container name>`.
## Specify VPN server's public IP
On Docker hosts with multiple public IP addresses, advanced users can specify a public IP for the VPN server using variable `VPN_PUBLIC_IP` in the `env` file, then re-create the Docker container. For example, if the Docker host has IPs `192.0.2.1` and `192.0.2.2`, and you want the VPN server to use `192.0.2.2`:
```
VPN_PUBLIC_IP=192.0.2.2
```
Note that this variable has no effect for IKEv2 mode, if IKEv2 is already set up in the Docker container. In this case, you may remove IKEv2 and set it up again using custom options. Refer to [Configure and use IKEv2 VPN](../README.md#configure-and-use-ikev2-vpn).
Additional configuration may be required if you want VPN clients to use the specified public IP as their "outgoing IP" when the VPN connection is active, and the specified IP is NOT the main IP (or default route) on the Docker host. In this case, you can try adding an IPTables `SNAT` rule on the Docker host. To persist after reboot, you may add the command to `/etc/rc.local`.
Continuing with the example above, if the Docker container has internal IP `172.17.0.2` (check using `docker inspect ipsec-vpn-server`), Docker's network interface name is `docker0` (check using `iptables -nvL -t nat`), and you want the "outgoing IP" to be `192.0.2.2`:
```
iptables -t nat -I POSTROUTING -s 172.17.0.2 ! -o docker0 -j SNAT --to 192.0.2.2
```
To check the "outgoing IP" for a connected VPN client, you may open a browser on the client and [look up the IP address on Google](https://www.google.com/search?q=my+ip).
## Assign static IPs to VPN clients
When connecting using IPsec/L2TP mode, the VPN server (Docker container) has internal IP `192.168.42.1` within the VPN subnet `192.168.42.0/24`. Clients are assigned internal IPs from `192.168.42.10` to `192.168.42.250`. To check which IP is assigned to a client, view the connection status on the VPN client.
When connecting using IPsec/XAuth ("Cisco IPsec") or IKEv2 mode, the VPN server (Docker container) does NOT have an internal IP within the VPN subnet `192.168.43.0/24`. Clients are assigned internal IPs from `192.168.43.10` to `192.168.43.250`.
Advanced users may optionally assign static IPs to VPN clients. For **IKEv2 mode**, first open a [Bash shell inside the container](#bash-shell-inside-container), then follow the steps in section "IKEv2 mode: Assign static IPs to VPN clients" of [Internal VPN IPs and traffic](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/advanced-usage.md#internal-vpn-ips-and-traffic). Note that you should skip steps 2, 3 and 5, and restart the Docker container when finished. This is because changes to `/etc/ipsec.conf` may be overwritten on container restart.
Instructions below **ONLY** apply to IPsec/L2TP and IPsec/XAuth ("Cisco IPsec") modes.
To assign static IPs, declare the `VPN_ADDL_IP_ADDRS` variable in your `env` file, then re-create the Docker container. Example:
```
VPN_ADDL_USERS=user1 user2 user3 user4 user5
VPN_ADDL_PASSWORDS=pass1 pass2 pass3 pass4 pass5
VPN_ADDL_IP_ADDRS=* * 192.168.42.2 192.168.43.2
```
In this example, we assign static IP `192.168.42.2` for `user3` for IPsec/L2TP mode, and assign static IP `192.168.43.2` for `user4` for IPsec/XAuth ("Cisco IPsec") mode. Internal IPs for `user1`, `user2` and `user5` will be auto-assigned. The internal IP for `user3` for IPsec/XAuth mode and the internal IP for `user4` for IPsec/L2TP mode will also be auto-assigned. You may use `*` to specify auto-assigned IPs, or put those user(s) at the end of the list.
Static IPs that you specify for IPsec/L2TP mode must be within the range from `192.168.42.2` to `192.168.42.9`. Static IPs that you specify for IPsec/XAuth ("Cisco IPsec") mode must be within the range from `192.168.43.2` to `192.168.43.9`.
If you need to assign more static IPs, you must shrink the pool of auto-assigned IP addresses. Example:
```
VPN_L2TP_POOL=192.168.42.100-192.168.42.250
VPN_XAUTH_POOL=192.168.43.100-192.168.43.250
```
This will allow you to assign static IPs within the range from `192.168.42.2` to `192.168.42.99` for IPsec/L2TP mode, and within the range from `192.168.43.2` to `192.168.43.99` for IPsec/XAuth ("Cisco IPsec") mode.
Note that if you specify `VPN_XAUTH_POOL` in the `env` file, and IKEv2 is already set up in the Docker container, you **must** manually edit `/etc/ipsec.d/ikev2.conf` inside the container and replace `rightaddresspool=192.168.43.10-192.168.43.250` with the **same value** as `VPN_XAUTH_POOL`, before re-creating the Docker container. Otherwise, IKEv2 may stop working.
**Note:** In your `env` file, DO NOT put `""` or `''` around values, or add space around `=`. DO NOT use these special characters within values: `\ " '`.
## Customize VPN subnets
By default, IPsec/L2TP VPN clients will use internal VPN subnet `192.168.42.0/24`, while IPsec/XAuth ("Cisco IPsec") and IKEv2 VPN clients will use internal VPN subnet `192.168.43.0/24`. For more details, read the previous section.
For most use cases, it is NOT necessary and NOT recommended to customize these subnets. If your use case requires it, however, you may specify custom subnet(s) in your `env` file, then you must re-create the Docker container.
```
# Example: Specify custom VPN subnet for IPsec/L2TP mode
# Note: All three variables must be specified.
VPN_L2TP_NET=10.1.0.0/16
VPN_L2TP_LOCAL=10.1.0.1
VPN_L2TP_POOL=10.1.0.10-10.1.254.254
```
```
# Example: Specify custom VPN subnet for IPsec/XAuth and IKEv2 modes
# Note: Both variables must be specified.
VPN_XAUTH_NET=10.2.0.0/16
VPN_XAUTH_POOL=10.2.0.10-10.2.254.254
```
**Note:** In your `env` file, DO NOT put `""` or `''` around values, or add space around `=`.
In the examples above, `VPN_L2TP_LOCAL` is the VPN server's internal IP for IPsec/L2TP mode. `VPN_L2TP_POOL` and `VPN_XAUTH_POOL` are the pools of auto-assigned IP addresses for VPN clients.
Note that if you specify `VPN_XAUTH_POOL` in the `env` file, and IKEv2 is already set up in the Docker container, you **must** manually edit `/etc/ipsec.d/ikev2.conf` inside the container and replace `rightaddresspool=192.168.43.10-192.168.43.250` with the **same value** as `VPN_XAUTH_POOL`, before re-creating the Docker container. Otherwise, IKEv2 may stop working.
## IPv6 support
If the Docker host has a public (global unicast) IPv6 address and the requirements below are met, IPv6 support for IKEv2 clients is automatically enabled when the container starts. No additional configuration is needed.
**Note:** IPv6 support has been tested on Android using the strongSwan VPN client. Other platforms (e.g. Windows, macOS, iOS) may have limitations or require additional configuration for IPv6 to work over the IKEv2 VPN.
**Note:** For **Windows** clients, you need to run the following commands once in a PowerShell window to route IPv6 traffic through the VPN. Replace `IKEv2 VPN X.X.X.X` with the actual name of your VPN connection. When finished, reconnect to the IKEv2 VPN.
```powershell
Add-VpnConnectionRoute -ConnectionName "IKEv2 VPN X.X.X.X" -DestinationPrefix ::/1
Add-VpnConnectionRoute -ConnectionName "IKEv2 VPN X.X.X.X" -DestinationPrefix 8000::/1
```
When IPv6 is enabled, IKEv2 VPN clients receive both an IPv4 address from the `192.168.43.0/24` pool and an IPv6 address from the `fddd:500:500:500::/64` pool. The container masquerades IPv6 traffic from VPN clients through the host's IPv6 address, giving clients full IPv6 internet access through the tunnel.
**Requirements:**
- The Docker host must have a routable global unicast IPv6 address (starting with `2` or `3`). Link-local (`fe80::/10`) addresses are not sufficient.
- IPv6 must be enabled for the Docker container. See [Enable IPv6 support in Docker](https://docs.docker.com/engine/daemon/ipv6/).
- Libreswan 5.0 or newer (the Docker image includes 5.x by default).
- IPv6 is only supported for **IKEv2 mode**. IPsec/L2TP and IPsec/XAuth ("Cisco IPsec") modes do not support IPv6.
To enable IPv6 for the Docker container, first enable IPv6 in the Docker daemon by adding the following to `/etc/docker/daemon.json` on the Docker host, then restart Docker:
```json
{
"ipv6": true,
"fixed-cidr-v6": "fddd:1::/64"
}
```
After that, re-create the Docker container. The `run.sh` script will detect the container's public IPv6 address and automatically configure IPv6 support.
To verify that IPv6 is working, connect to the VPN using IKEv2 and check your IPv6 address, e.g. using [test-ipv6.com](https://test-ipv6.com).
**Note:** If you run the Docker container [without privileged mode](#run-without-privileged-mode), you must add `--sysctl net.ipv6.conf.all.forwarding=1` to the `docker run` command.
**Note for existing containers:** If IKEv2 is already set up in your container (i.e. `ikev2.conf` already exists in the `ikev2-vpn-data` volume), IPv6 will **not** be automatically added to the existing IKEv2 configuration when the container restarts or is recreated. To enable full IPv6 support for IKEv2, you must remove IKEv2 and set it up again. Refer to "remove IKEv2" in [Configure and use IKEv2 VPN](../README.md#configure-and-use-ikev2-vpn).
You may optionally customize the IPv6 pool subnet by setting `VPN_IP6_NET` in your `env` file before re-creating the container:
```
# Example: Specify custom IPv6 pool subnet for IKEv2 mode
# Must be a /64 subnet in the ULA range
VPN_IP6_NET=fddd:1234:5678:9012::/64
```
## Split tunneling
With split tunneling, VPN clients will only send traffic for a specific destination subnet through the VPN tunnel. Other traffic will NOT go through the VPN tunnel. This allows you to gain secure access to a network through your VPN, without routing all your client's traffic through the VPN. Split tunneling has some limitations, and is not supported by all VPN clients.
Advanced users can optionally enable split tunneling for IKEv2 mode. Add the variable `VPN_SPLIT_IKEV2` to your `env` file, then re-create the Docker container. For example, if the destination subnet is `10.123.123.0/24`:
```
VPN_SPLIT_IKEV2=10.123.123.0/24
```
Note that this variable has no effect if IKEv2 is already set up in the Docker container. In this case, you have two options:
**Option 1:** First start a [Bash shell inside the container](#bash-shell-inside-container), then edit `/etc/ipsec.d/ikev2.conf` and replace `leftsubnet=0.0.0.0/0` with your desired subnet. When finished, `exit` the container and run `docker restart ipsec-vpn-server`.
**Option 2:** Remove both the Docker container and the `ikev2-vpn-data` volume, then re-create the Docker container. All VPN configuration will be **permanently deleted**. Refer to "remove IKEv2" in [Configure and use IKEv2 VPN](../README.md#configure-and-use-ikev2-vpn).
Alternatively, Windows users can enable split tunneling by manually adding routes. For more details, see [Split tunneling](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/advanced-usage.md#split-tunneling).
## About host network mode
Advanced users can run this image in [host network mode](https://docs.docker.com/network/host/), by adding `--network=host` to the `docker run` command.
Host network mode is NOT recommended for this image, unless your use case requires it. In this mode, the container's network stack is not isolated from the Docker host, and VPN clients may be able to access ports or services on the Docker host using its internal VPN IP `192.168.42.1` after connecting using IPsec/L2TP mode. Note that you will need to manually clean up the changes to IPTables rules and sysctl settings by [run.sh](../run.sh) or reboot the server when you no longer use this image.
Some Docker host OS, such as Debian 10, cannot run this image in host network mode due to the use of nftables.
## Enable Libreswan logs
To keep the Docker image small, Libreswan (IPsec) logs are not enabled by default. If you need to enable it for troubleshooting purposes, first start a Bash session in the running container:
```
docker exec -it ipsec-vpn-server env TERM=xterm bash -l
```
Then run the following commands:
```
# For Alpine-based image
apk add --no-cache rsyslog
rsyslogd
rc-service ipsec stop; rc-service -D ipsec start >/dev/null 2>&1
sed -i '\|pluto\.pid|a rm -f /var/run/rsyslogd.pid; rsyslogd' /opt/src/run.sh
exit
# For Debian-based image
apt-get update && apt-get -y install rsyslog
rsyslogd
service ipsec restart
sed -i '\|pluto\.pid|a rm -f /var/run/rsyslogd.pid; rsyslogd' /opt/src/run.sh
exit
```
**Note:** The error `rsyslogd: imklog: cannot open kernel log` is normal if you use this Docker image without privileged mode.
When finished, you may check Libreswan logs with:
```
docker exec -it ipsec-vpn-server grep pluto /var/log/auth.log
```
To check xl2tpd logs, run `docker logs ipsec-vpn-server`.
## Check server status
Check the status of the IPsec VPN server:
```
docker exec -it ipsec-vpn-server ipsec status
```
Show currently established VPN connections:
```
docker exec -it ipsec-vpn-server ipsec trafficstatus
```
## Build from source code
Advanced users can download and compile the source code from GitHub:
```
git clone https://github.com/hwdsl2/docker-ipsec-vpn-server
cd docker-ipsec-vpn-server
# To build Alpine-based image
docker build -t hwdsl2/ipsec-vpn-server .
# To build Debian-based image
docker build -f Dockerfile.debian -t hwdsl2/ipsec-vpn-server:debian .
```
Or use this if not modifying the source code:
```
# To build Alpine-based image
docker build -t hwdsl2/ipsec-vpn-server github.com/hwdsl2/docker-ipsec-vpn-server
# To build Debian-based image
docker build -f Dockerfile.debian -t hwdsl2/ipsec-vpn-server:debian \
github.com/hwdsl2/docker-ipsec-vpn-server
```
## Bash shell inside container
To start a Bash session in the running container:
```
docker exec -it ipsec-vpn-server env TERM=xterm bash -l
```
(Optional) Install the `nano` editor:
```
# For Alpine-based image
apk add --no-cache nano
# For Debian-based image
apt-get update && apt-get -y install nano
```
Then run your commands inside the container. When finished, exit the container and restart if needed:
```
exit
docker restart ipsec-vpn-server
```
## Bind mount the env file
As an alternative to the `--env-file` option, advanced users can bind mount the `env` file. The advantage of this method is that after updating the `env` file, you can restart the Docker container to take effect instead of re-creating it. To use this method, you must first edit your `env` file and use single quotes `''` to enclose the values of all variables. Then (re-)create the Docker container (replace the first `vpn.env` with your own `env` file):
```
docker run \
--name ipsec-vpn-server \
--restart=always \
-v "$(pwd)/vpn.env:/opt/src/env/vpn.env:ro" \
-v ikev2-vpn-data:/etc/ipsec.d \
-v /lib/modules:/lib/modules:ro \
-p 500:500/udp \
-p 4500:4500/udp \
-d --privileged \
hwdsl2/ipsec-vpn-server
```
## Deploy Google BBR congestion control
After the VPN server is set up, the performance can be improved by deploying the Google BBR congestion control algorithm on your Docker host.
This is usually done by modifying the configuration file `/etc/sysctl.conf`. However, some Linux distributions may additionally require updates to the Linux kernel.
For detailed deployment methods, please refer to [this document](https://github.com/hwdsl2/setup-ipsec-vpn/blob/master/docs/bbr.md). When finished, restart the Docker container:
```
docker restart ipsec-vpn-server
```
## License
**Note:** The software components inside the pre-built image (such as Libreswan and xl2tpd) are under the respective licenses chosen by their respective copyright holders. As for any pre-built image usage, it is the image user's responsibility to ensure that any use of this image complies with any relevant licenses for all software contained within.
Copyright (C) 2016-2026 [Lin Song](https://github.com/hwdsl2) [](https://www.linkedin.com/in/linsongui)
[](http://creativecommons.org/licenses/by-sa/3.0/)
This work is licensed under the [Creative Commons Attribution-ShareAlike 3.0 Unported License](http://creativecommons.org/licenses/by-sa/3.0/)
Attribution required: please include my name in any derivative and let me know how you have improved it!
================================================
FILE: docs/vpn-book-zh-Hant.md
================================================
[« 返回首頁](../README-zh-Hant.md) | [English](vpn-book.md) | [简体中文](vpn-book-zh.md) | [繁體中文](vpn-book-zh-Hant.md)
## 新:Privacy Tools in the Age of AI
了解如何在人工智慧時代建立更高等級的隱私保護。本書提供電子書、精裝本、平裝本與有聲書格式。
» [Amazon](https://books2read.com/privacy?store=amazon)
» [Google Play](https://books2read.com/privacy?store=google)
» [Apple Books](https://books2read.com/privacy?store=apple)
» [Hoopla](https://books2read.com/privacy?store=hoopla)
» [其他平台](https://books2read.com/privacy)
其他語言版本:[Español](https://books2read.com/privacyes)、[Deutsch](https://books2read.com/privacyde)、[Français](https://books2read.com/privacyfr)、[Italiano](https://books2read.com/privacyit)。
## 架設自己的 VPN 伺服器:IPsec VPN、OpenVPN 與 WireGuard 實作指南
本書是架設你自己的 IPsec VPN、OpenVPN 與 WireGuard 伺服器的**實作指南**。提供電子書與平裝本格式。
» [Google Play](https://books2read.com/vpnguidezht?store=google)
» [Apple Books](https://books2read.com/vpnguidezht?store=apple)
» [Amazon](https://books2read.com/vpnguidezht?store=amazon)
» [Amazon 平裝本](https://books2read.com/vpnguidezht?store=amazon-paperback&format=PAPERBACK)
» [其他平台](https://books2read.com/vpnguidezht)
其他語言版本:[简体中文](https://books2read.com/vpnguidezh)、[English](https://books2read.com/vpnguide)、[Español](https://books2read.com/vpnguidees)、[Deutsch](https://books2read.com/vpnguidede)、[Français](https://books2read.com/vpnguidefr)、[Italiano](https://books2read.com/vpnguideit)、[Nederlands](https://books2read.com/vpnguidenl)、[Português](https://books2read.com/vpnguidept)、[日本語](https://books2read.com/vpnguideja)。
## 架設自己的 VPN 伺服器:IPsec VPN、OpenVPN 與 WireGuard 完整指南
本書是架設你自己的 IPsec VPN、OpenVPN 與 WireGuard 伺服器的**完整指南**。提供電子書與平裝本格式。
» [Google Play](https://books2read.com/vpnzht?store=google)
» [Apple Books](https://books2read.com/vpnzht?store=apple)
» [Amazon](https://books2read.com/vpnzht?store=amazon)
» [Amazon 平裝本](https://books2read.com/vpnzht?store=amazon-paperback&format=PAPERBACK)
» [其他平台](https://books2read.com/vpnzht)
其他語言版本:[简体中文](https://books2read.com/vpnzh)、[English](https://books2read.com/vpn)、[Español](https://books2read.com/vpnes)、[Deutsch](https://books2read.com/vpnde)、[Français](https://books2read.com/vpnfr)、[Italiano](https://books2read.com/vpnit)、[日本語](https://books2read.com/vpnja)。
作者頁面:[amazon.com/author/linsong](https://amazon.com/author/linsong)
================================================
FILE: docs/vpn-book-zh.md
================================================
[« 返回主页](../README-zh.md) | [English](vpn-book.md) | [简体中文](vpn-book-zh.md) | [繁體中文](vpn-book-zh-Hant.md)
## 新:Privacy Tools in the Age of AI
了解如何在人工智能时代构建更高级别的隐私保护。本书提供电子书、精装本、平装本和有声读物格式。
» [Amazon](https://books2read.com/privacy?store=amazon)
» [Google Play](https://books2read.com/privacy?store=google)
» [Apple Books](https://books2read.com/privacy?store=apple)
» [Hoopla](https://books2read.com/privacy?store=hoopla)
» [其他平台](https://books2read.com/privacy)
其他语言版本:[Español](https://books2read.com/privacyes)、[Deutsch](https://books2read.com/privacyde)、[Français](https://books2read.com/privacyfr)、[Italiano](https://books2read.com/privacyit)。
## 搭建自己的 VPN 服务器:IPsec VPN、OpenVPN 与 WireGuard 实战指南
本书是搭建你自己的 IPsec VPN、OpenVPN 和 WireGuard 服务器的**实战指南**。提供电子书和平装本格式。
» [Google Play](https://books2read.com/vpnguidezh?store=google)
» [Apple Books](https://books2read.com/vpnguidezh?store=apple)
» [Amazon](https://books2read.com/vpnguidezh?store=amazon)
» [Amazon 平装本](https://books2read.com/vpnguidezh?store=amazon-paperback&format=PAPERBACK)
» [其他平台](https://books2read.com/vpnguidezh)
其他语言版本:[繁體中文](https://books2read.com/vpnguidezht)、[English](https://books2read.com/vpnguide)、[Español](https://books2read.com/vpnguidees)、[Deutsch](https://books2read.com/vpnguidede)、[Français](https://books2read.com/vpnguidefr)、[Italiano](https://books2read.com/vpnguideit)、[Nederlands](https://books2read.com/vpnguidenl)、[Português](https://books2read.com/vpnguidept)、[日本語](https://books2read.com/vpnguideja)。
## 搭建自己的 VPN 服务器:IPsec VPN、OpenVPN 与 WireGuard 完整指南
本书是搭建你自己的 IPsec VPN、OpenVPN 和 WireGuard 服务器的**完整指南**。提供电子书和平装本格式。
» [Google Play](https://books2read.com/vpnzh?store=google)
» [Apple Books](https://books2read.com/vpnzh?store=apple)
» [Amazon](https://books2read.com/vpnzh?store=amazon)
» [Amazon 平装本](https://books2read.com/vpnzh?store=amazon-paperback&format=PAPERBACK)
» [其他平台](https://books2read.com/vpnzh)
其他语言版本:[繁體中文](https://books2read.com/vpnzht)、[English](https://books2read.com/vpn)、[Español](https://books2read.com/vpnes)、[Deutsch](https://books2read.com/vpnde)、[Français](https://books2read.com/vpnfr)、[Italiano](https://books2read.com/vpnit)、[日本語](https://books2read.com/vpnja)。
作者页面:[amazon.com/author/linsong](https://amazon.com/author/linsong)
================================================
FILE: docs/vpn-book.md
================================================
[« Back to home page](../README.md) | [English](vpn-book.md) | [简体中文](vpn-book-zh.md) | [繁體中文](vpn-book-zh-Hant.md)
## New: Privacy Tools in the Age of AI
Learn how to build next-level privacy protection in the age of AI. This book is available in eBook, print and audiobook formats on:
» [Amazon](https://books2read.com/privacy?store=amazon)
» [Google Play](https://books2read.com/privacy?store=google)
» [Apple Books](https://books2read.com/privacy?store=apple)
» [Hoopla](https://books2read.com/privacy?store=hoopla)
» [Other Platforms](https://books2read.com/privacy)
Other language versions: [Español](https://books2read.com/privacyes), [Deutsch](https://books2read.com/privacyde), [Français](https://books2read.com/privacyfr), [Italiano](https://books2read.com/privacyit).
## Build Your Own VPN Server: A Step by Step Guide
This book is a **step-by-step guide** to building your own IPsec VPN, OpenVPN and WireGuard server. Available in eBook, print and audiobook formats on:
» [Amazon](https://books2read.com/vpnguide?store=amazon)
» [Google Play](https://books2read.com/vpnguide?store=google)
» [Apple Books](https://books2read.com/vpnguide?store=apple)
» [Hoopla](https://books2read.com/vpnguide?store=hoopla)
» [Other Platforms](https://books2read.com/vpnguide)
Other language versions: [简体中文](https://books2read.com/vpnguidezh), [繁體中文](https://books2read.com/vpnguidezht), [Español](https://books2read.com/vpnguidees), [Deutsch](https://books2read.com/vpnguidede), [Français](https://books2read.com/vpnguidefr), [Italiano](https://books2read.com/vpnguideit), [Nederlands](https://books2read.com/vpnguidenl), [Português](https://books2read.com/vpnguidept), [日本語](https://books2read.com/vpnguideja).
## Set Up Your Own IPsec VPN, OpenVPN and WireGuard Server
This book is a **comprehensive guide** to building your own IPsec VPN, OpenVPN and WireGuard server. Available in eBook, print and audiobook formats on:
» [Amazon](https://books2read.com/vpn?store=amazon)
» [Google Play](https://books2read.com/vpn?store=google)
» [Apple Books](https://books2read.com/vpn?store=apple)
» [Hoopla](https://books2read.com/vpn?store=hoopla)
» [Other Platforms](https://books2read.com/vpn)
Other language versions: [简体中文](https://books2read.com/vpnzh), [繁體中文](https://books2read.com/vpnzht), [Español](https://books2read.com/vpnes), [Deutsch](https://books2read.com/vpnde), [Français](https://books2read.com/vpnfr), [Italiano](https://books2read.com/vpnit), [日本語](https://books2read.com/vpnja).
Author page: [amazon.com/author/linsong](https://amazon.com/author/linsong)
================================================
FILE: run.sh
================================================
#!/bin/bash
#
# Docker script to configure and start an IPsec VPN server
#
# DO NOT RUN THIS SCRIPT ON YOUR PC OR MAC! THIS IS ONLY MEANT TO BE RUN
# IN A CONTAINER!
#
# This file is part of IPsec VPN Docker image, available at:
# https://github.com/hwdsl2/docker-ipsec-vpn-server
#
# Copyright (C) 2016-2026 Lin Song <linsongui@gmail.com>
# Based on the work of Thomas Sarlandie (Copyright 2012)
#
# This work is licensed under the Creative Commons Attribution-ShareAlike 3.0
# Unported License: http://creativecommons.org/licenses/by-sa/3.0/
#
# Attribution required: please include my name in any derivative and let me
# know how you have improved it!
export PATH="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin"
exiterr() { echo "Error: $1" >&2; exit 1; }
nospaces() { printf '%s' "$1" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//'; }
onespace() { printf '%s' "$1" | tr -s ' '; }
noquotes() { printf '%s' "$1" | sed -e 's/^"\(.*\)"$/\1/' -e "s/^'\(.*\)'$/\1/"; }
noquotes2() { printf '%s' "$1" | sed -e 's/" "/ /g' -e "s/' '/ /g"; }
check_ip() {
IP_REGEX='^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])$'
printf '%s' "$1" | tr -d '\n' | grep -Eq "$IP_REGEX"
}
check_ip6() {
IP6_REGEX='^[0-9a-fA-F]{0,4}(:[0-9a-fA-F]{0,4}){1,7}$'
printf '%s' "$1" | tr -d '\n' | grep -Eq "$IP6_REGEX"
}
check_cidr() {
CIDR_REGEX='^(([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\.){3}([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])(/(3[0-2]|[1-2][0-9]|[0-9]))$'
printf '%s' "$1" | tr -d '\n' | grep -Eq "$CIDR_REGEX"
}
check_dns_name() {
FQDN_REGEX='^([a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$'
printf '%s' "$1" | tr -d '\n' | grep -Eq "$FQDN_REGEX"
}
check_client_name() {
! { [ "${#1}" -gt "64" ] || printf '%s' "$1" | LC_ALL=C grep -q '[^A-Za-z0-9_-]\+' \
|| case $1 in -*) true ;; *) false ;; esac; }
}
if [ ! -f "/.dockerenv" ] && [ ! -f "/run/.containerenv" ] \
&& [ -z "$KUBERNETES_SERVICE_HOST" ] \
&& ! head -n 1 /proc/1/sched 2>/dev/null | grep -q '^run\.sh '; then
exiterr "This script ONLY runs in a container (e.g. Docker, Podman)."
fi
if ip link add dummy0 type dummy 2>&1 | grep -q "not permitted"; then
cat 1>&2 <<'EOF'
Error: This Docker image should be run in privileged mode.
See: https://github.com/hwdsl2/docker-ipsec-vpn-server
EOF
exit 1
fi
ip link delete dummy0 >/dev/null 2>&1
os_type=debian
os_arch=$(uname -m | tr -dc 'A-Za-z0-9_-')
[ -f /etc/os-release ] && os_type=$(. /etc/os-release && printf '%s' "$ID")
if [ ! -e /dev/ppp ]; then
cat <<'EOF'
Warning: /dev/ppp is missing, and IPsec/L2TP mode may not work.
Please use IKEv2 or IPsec/XAuth mode to connect.
Debian 11/10 users, see https://vpnsetup.net/debian10
EOF
fi
NET_IFACE=$(route 2>/dev/null | grep -m 1 '^default' | grep -o '[^ ]*$')
[ -z "$NET_IFACE" ] && NET_IFACE=$(ip -4 route list 0/0 2>/dev/null | grep -m 1 -Po '(?<=dev )(\S+)')
[ -z "$NET_IFACE" ] && NET_IFACE=eth0
mkdir -p /opt/src
vpn_env="/opt/src/vpn.env"
vpn_env_dir="/opt/src/env/vpn.env"
if [ -f "$vpn_env_dir" ]; then
vpn_env="$vpn_env_dir"
fi
vpn_gen_env="/etc/ipsec.d/vpn-gen.env"
if [ -z "$VPN_IPSEC_PSK" ] && [ -z "$VPN_USER" ] && [ -z "$VPN_PASSWORD" ]; then
if [ -f "$vpn_env" ]; then
echo
echo 'Retrieving VPN credentials...'
. "$vpn_env"
elif [ -f "$vpn_gen_env" ]; then
echo
echo 'Retrieving previously generated VPN credentials...'
. "$vpn_gen_env"
else
echo
echo 'VPN credentials not set by user. Generating random PSK and password...'
VPN_IPSEC_PSK=$(LC_CTYPE=C tr -dc 'A-HJ-NPR-Za-km-z2-9' </dev/urandom 2>/dev/null | head -c 20)
VPN_USER=vpnuser
VPN_PASSWORD=$(LC_CTYPE=C tr -dc 'A-HJ-NPR-Za-km-z2-9' </dev/urandom 2>/dev/null | head -c 16)
printf '%s\n' "VPN_IPSEC_PSK='$VPN_IPSEC_PSK'" > "$vpn_gen_env"
printf '%s\n' "VPN_USER='$VPN_USER'" >> "$vpn_gen_env"
printf '%s\n' "VPN_PASSWORD='$VPN_PASSWORD'" >> "$vpn_gen_env"
chmod 600 "$vpn_gen_env"
fi
fi
# Remove whitespace and quotes around VPN variables, if any
VPN_IPSEC_PSK=$(nospaces "$VPN_IPSEC_PSK")
VPN_IPSEC_PSK=$(noquotes "$VPN_IPSEC_PSK")
VPN_USER=$(nospaces "$VPN_USER")
VPN_USER=$(noquotes "$VPN_USER")
VPN_PASSWORD=$(nospaces "$VPN_PASSWORD")
VPN_PASSWORD=$(noquotes "$VPN_PASSWORD")
if [ -n "$VPN_ADDL_USERS" ] && [ -n "$VPN_ADDL_PASSWORDS" ]; then
VPN_ADDL_USERS=$(nospaces "$VPN_ADDL_USERS")
VPN_ADDL_USERS=$(noquotes "$VPN_ADDL_USERS")
VPN_ADDL_USERS=$(onespace "$VPN_ADDL_USERS")
VPN_ADDL_USERS=$(noquotes2 "$VPN_ADDL_USERS")
VPN_ADDL_PASSWORDS=$(nospaces "$VPN_ADDL_PASSWORDS")
VPN_ADDL_PASSWORDS=$(noquotes "$VPN_ADDL_PASSWORDS")
VPN_ADDL_PASSWORDS=$(onespace "$VPN_ADDL_PASSWORDS")
VPN_ADDL_PASSWORDS=$(noquotes2 "$VPN_ADDL_PASSWORDS")
if [ -n "$VPN_ADDL_IP_ADDRS" ]; then
VPN_ADDL_IP_ADDRS=$(nospaces "$VPN_ADDL_IP_ADDRS")
VPN_ADDL_IP_ADDRS=$(noquotes "$VPN_ADDL_IP_ADDRS")
VPN_ADDL_IP_ADDRS=$(onespace "$VPN_ADDL_IP_ADDRS")
VPN_ADDL_IP_ADDRS=$(noquotes2 "$VPN_ADDL_IP_ADDRS")
fi
else
VPN_ADDL_USERS=""
VPN_ADDL_PASSWORDS=""
VPN_ADDL_IP_ADDRS=""
fi
if [ -n "$VPN_DNS_SRV1" ]; then
VPN_DNS_SRV1=$(nospaces "$VPN_DNS_SRV1")
VPN_DNS_SRV1=$(noquotes "$VPN_DNS_SRV1")
fi
if [ -n "$VPN_DNS_SRV2" ]; then
VPN_DNS_SRV2=$(nospaces "$VPN_DNS_SRV2")
VPN_DNS_SRV2=$(noquotes "$VPN_DNS_SRV2")
fi
if [ -n "$VPN_CLIENT_NAME" ]; then
VPN_CLIENT_NAME=$(nospaces "$VPN_CLIENT_NAME")
VPN_CLIENT_NAME=$(noquotes "$VPN_CLIENT_NAME")
fi
if [ -n "$VPN_DNS_NAME" ]; then
VPN_DNS_NAME=$(nospaces "$VPN_DNS_NAME")
VPN_DNS_NAME=$(noquotes "$VPN_DNS_NAME")
fi
if [ -n "$VPN_PUBLIC_IP" ]; then
VPN_PUBLIC_IP=$(nospaces "$VPN_PUBLIC_IP")
VPN_PUBLIC_IP=$(noquotes "$VPN_PUBLIC_IP")
fi
if [ -n "$VPN_ANDROID_MTU_FIX" ]; then
VPN_ANDROID_MTU_FIX=$(nospaces "$VPN_ANDROID_MTU_FIX")
VPN_ANDROID_MTU_FIX=$(noquotes "$VPN_ANDROID_MTU_FIX")
fi
if [ -n "$VPN_SHA2_TRUNCBUG" ]; then
VPN_SHA2_TRUNCBUG=$(nospaces "$VPN_SHA2_TRUNCBUG")
VPN_SHA2_TRUNCBUG=$(noquotes "$VPN_SHA2_TRUNCBUG")
fi
if [ -n "$VPN_PROTECT_CONFIG" ]; then
VPN_PROTECT_CONFIG=$(nospaces "$VPN_PROTECT_CONFIG")
VPN_PROTECT_CONFIG=$(noquotes "$VPN_PROTECT_CONFIG")
fi
if [ -n "$VPN_SPLIT_IKEV2" ]; then
VPN_SPLIT_IKEV2=$(nospaces "$VPN_SPLIT_IKEV2")
VPN_SPLIT_IKEV2=$(noquotes "$VPN_SPLIT_IKEV2")
fi
if [ -n "$VPN_DISABLE_IPSEC_L2TP" ]; then
VPN_DISABLE_IPSEC_L2TP=$(nospaces "$VPN_DISABLE_IPSEC_L2TP")
VPN_DISABLE_IPSEC_L2TP=$(noquotes "$VPN_DISABLE_IPSEC_L2TP")
fi
if [ -n "$VPN_DISABLE_IPSEC_XAUTH" ]; then
VPN_DISABLE_IPSEC_XAUTH=$(nospaces "$VPN_DISABLE_IPSEC_XAUTH")
VPN_DISABLE_IPSEC_XAUTH=$(noquotes "$VPN_DISABLE_IPSEC_XAUTH")
fi
if [ -n "$VPN_IKEV2_ONLY" ]; then
VPN_IKEV2_ONLY=$(nospaces "$VPN_IKEV2_ONLY")
VPN_IKEV2_ONLY=$(noquotes "$VPN_IKEV2_ONLY")
fi
if [ -n "$VPN_ENABLE_MODP1024" ]; then
VPN_ENABLE_MODP1024=$(nospaces "$VPN_ENABLE_MODP1024")
VPN_ENABLE_MODP1024=$(noquotes "$VPN_ENABLE_MODP1024")
fi
if [ -n "$VPN_ENABLE_MODP1536" ]; then
VPN_ENABLE_MODP1536=$(nospaces "$VPN_ENABLE_MODP1536")
VPN_ENABLE_MODP1536=$(noquotes "$VPN_ENABLE_MODP1536")
fi
if [ -n "$VPN_L2TP_NET" ]; then
VPN_L2TP_NET=$(nospaces "$VPN_L2TP_NET")
VPN_L2TP_NET=$(noquotes "$VPN_L2TP_NET")
fi
if [ -n "$VPN_L2TP_LOCAL" ]; then
VPN_L2TP_LOCAL=$(nospaces "$VPN_L2TP_LOCAL")
VPN_L2TP_LOCAL=$(noquotes "$VPN_L2TP_LOCAL")
fi
if [ -n "$VPN_L2TP_POOL" ]; then
VPN_L2TP_POOL=$(nospaces "$VPN_L2TP_POOL")
VPN_L2TP_POOL=$(noquotes "$VPN_L2TP_POOL")
fi
if [ -n "$VPN_XAUTH_NET" ]; then
VPN_XAUTH_NET=$(nospaces "$VPN_XAUTH_NET")
VPN_XAUTH_NET=$(noquotes "$VPN_XAUTH_NET")
fi
if [ -n "$VPN_XAUTH_POOL" ]; then
VPN_XAUTH_POOL=$(nospaces "$VPN_XAUTH_POOL")
VPN_XAUTH_POOL=$(noquotes "$VPN_XAUTH_POOL")
fi
if [ -n "$VPN_PUBLIC_IP6" ]; then
VPN_PUBLIC_IP6=$(nospaces "$VPN_PUBLIC_IP6")
VPN_PUBLIC_IP6=$(noquotes "$VPN_PUBLIC_IP6")
fi
if [ -n "$VPN_IP6_NET" ]; then
VPN_IP6_NET=$(nospaces "$VPN_IP6_NET")
VPN_IP6_NET=$(noquotes "$VPN_IP6_NET")
fi
ip6=""
if [ -n "$VPN_PUBLIC_IP6" ]; then
ip6="$VPN_PUBLIC_IP6"
check_ip6 "$ip6" || { echo "Warning: Invalid IPv6 address in 'VPN_PUBLIC_IP6'. Detecting IPv6..." >&2; ip6=""; }
fi
if [ -z "$ip6" ]; then
ip6=$(ip -6 addr 2>/dev/null | awk '/inet6 [23]/ {print $2}' | cut -d'/' -f1 | head -n1)
check_ip6 "$ip6" || ip6=""
if [ -z "$ip6" ] && ip -6 addr 2>/dev/null | grep 'inet6' | grep -qv 'inet6 \(::1\|fe80\)'; then
ip6=$(wget -t 2 -T 10 -qO- https://ipv6.icanhazip.com 2>/dev/null)
check_ip6 "$ip6" || ip6=""
fi
fi
if [ -z "$VPN_IPSEC_PSK" ] || [ -z "$VPN_USER" ] || [ -z "$VPN_PASSWORD" ]; then
exiterr "All VPN credentials must be specified. Edit your 'env' file and re-enter them."
fi
if printf '%s' "$VPN_IPSEC_PSK $VPN_USER $VPN_PASSWORD $VPN_ADDL_USERS $VPN_ADDL_PASSWORDS" | LC_ALL=C grep -q '[^ -~]\+'; then
exiterr "VPN credentials must not contain non-ASCII characters."
fi
case "$VPN_IPSEC_PSK $VPN_USER $VPN_PASSWORD $VPN_ADDL_USERS $VPN_ADDL_PASSWORDS" in
*[\\\"\']*)
exiterr "VPN credentials must not contain these special characters: \\ \" '"
;;
esac
if printf '%s' "$VPN_USER $VPN_ADDL_USERS" | tr ' ' '\n' | sort | uniq -c | grep -qv '^ *1 '; then
exiterr "VPN usernames must not contain duplicates."
fi
# Check DNS servers and try to resolve hostnames to IPs
if [ -n "$VPN_DNS_SRV1" ]; then
check_ip "$VPN_DNS_SRV1" || VPN_DNS_SRV1=$(dig -t A -4 +short "$VPN_DNS_SRV1")
if ! check_ip "$VPN_DNS_SRV1"; then
cat <<'EOF'
Warning: Invalid DNS server. Check VPN_DNS_SRV1 in your 'env' file.
EOF
VPN_DNS_SRV1=""
fi
fi
if [ -n "$VPN_DNS_SRV2" ]; then
check_ip "$VPN_DNS_SRV2" || VPN_DNS_SRV2=$(dig -t A -4 +short "$VPN_DNS_SRV2")
if ! check_ip "$VPN_DNS_SRV2"; then
cat <<'EOF'
Warning: Invalid DNS server. Check VPN_DNS_SRV2 in your 'env' file.
EOF
VPN_DNS_SRV2=""
fi
fi
if [ -n "$VPN_CLIENT_NAME" ]; then
if ! check_client_name "$VPN_CLIENT_NAME"; then
cat <<'EOF'
Warning: Invalid client name. Use one word only, no special characters except '-' and '_'.
Falling back to default client name 'vpnclient'.
EOF
VPN_CLIENT_NAME=""
fi
fi
if [ -n "$VPN_DNS_NAME" ]; then
if ! check_dns_name "$VPN_DNS_NAME"; then
cat <<'EOF'
Warning: Invalid DNS name. 'VPN_DNS_NAME' must be a fully qualified domain name (FQDN).
Falling back to using this server's IP address.
EOF
VPN_DNS_NAME=""
fi
fi
if [ -n "$VPN_SPLIT_IKEV2" ]; then
if ! check_cidr "$VPN_SPLIT_IKEV2"; then
cat <<'EOF'
Warning: Invalid split VPN subnet. Check VPN_SPLIT_IKEV2 in your 'env' file.
EOF
VPN_SPLIT_IKEV2=""
fi
fi
echo
echo 'Trying to auto discover IP of this server...'
# In case auto IP discovery fails, manually define the public IP
# of this server in your 'env' file, as variable 'VPN_PUBLIC_IP'.
public_ip=${VPN_PUBLIC_IP:-''}
check_ip "$public_ip" || public_ip=$(dig @resolver1.opendns.com -t A -4 myip.opendns.com +short)
check_ip "$public_ip" || public_ip=$(wget -t 2 -T 10 -qO- http://ipv4.icanhazip.com)
check_ip "$public_ip" || public_ip=$(wget -t 2 -T 10 -qO- http://ip1.dynupdate.no-ip.com)
check_ip "$public_ip" || exiterr "Cannot detect this server's public IP. Define it in your 'env' file as 'VPN_PUBLIC_IP'."
if [ -n "$VPN_DNS_NAME" ]; then
server_addr="$VPN_DNS_NAME"
else
server_addr="$public_ip"
fi
L2TP_NET=${VPN_L2TP_NET:-'192.168.42.0/24'}
L2TP_LOCAL=${VPN_L2TP_LOCAL:-'192.168.42.1'}
L2TP_POOL=${VPN_L2TP_POOL:-'192.168.42.10-192.168.42.250'}
XAUTH_NET=${VPN_XAUTH_NET:-'192.168.43.0/24'}
XAUTH_POOL=${VPN_XAUTH_POOL:-'192.168.43.10-192.168.43.250'}
IP6_NET=${VPN_IP6_NET:-'fddd:500:500:500::/64'}
DNS_SRV1=${VPN_DNS_SRV1:-'8.8.8.8'}
DNS_SRV2=${VPN_DNS_SRV2:-'8.8.4.4'}
DNS_SRVS="\"$DNS_SRV1 $DNS_SRV2\""
[ -n "$VPN_DNS_SRV1" ] && [ -z "$VPN_DNS_SRV2" ] && DNS_SRVS="$DNS_SRV1"
vp_ip6=""
[ -n "$ip6" ] && vp_ip6=",%v6:fc00::/7,%v6:!$IP6_NET"
if [ -n "$VPN_DNS_SRV1" ] && [ -n "$VPN_DNS_SRV2" ]; then
echo
echo "Setting DNS servers to $VPN_DNS_SRV1 and $VPN_DNS_SRV2..."
elif [ -n "$VPN_DNS_SRV1" ]; then
echo
echo "Setting DNS server to $VPN_DNS_SRV1..."
fi
sha2_truncbug=no
case $VPN_SHA2_TRUNCBUG in
[yY][eE][sS])
echo
echo "Setting sha2-truncbug to yes in ipsec.conf..."
sha2_truncbug=yes
;;
esac
disable_ipsec_l2tp=no
case $VPN_DISABLE_IPSEC_L2TP in
[yY][eE][sS])
disable_ipsec_l2tp=yes
;;
esac
disable_ipsec_xauth=no
case $VPN_DISABLE_IPSEC_XAUTH in
[yY][eE][sS])
disable_ipsec_xauth=yes
;;
esac
case $VPN_IKEV2_ONLY in
[yY][eE][sS])
disable_ipsec_l2tp=yes
disable_ipsec_xauth=yes
;;
esac
ike_algs="aes256-sha2;modp2048,aes128-sha2;modp2048,aes256-sha1;modp2048,aes128-sha1;modp2048"
ike_algs_addl_1=",aes256-sha2;modp1024,aes128-sha1;modp1024"
ike_algs_addl_2=",aes256-sha2;modp1536,aes128-sha1;modp1536"
case $VPN_ENABLE_MODP1024 in
[yY][eE][sS])
echo
echo "Enabling modp1024 in ipsec.conf..."
ike_algs="$ike_algs$ike_algs_addl_1"
;;
esac
case $VPN_ENABLE_MODP1536 in
[yY][eE][sS])
echo
echo "Enabling modp1536 in ipsec.conf..."
ike_algs="$ike_algs$ike_algs_addl_2"
;;
esac
if [ "$disable_ipsec_l2tp" = yes ] && [ "$disable_ipsec_xauth" = yes ]; then
cat <<'EOF'
Note: Running in IKEv2-only mode via env file option.
IPsec/L2TP and IPsec/XAuth ("Cisco IPsec") modes are disabled.
EOF
if ! grep -q " /etc/ipsec.d " /proc/mounts; then
cat <<'EOF'
Warning: /etc/ipsec.d not mounted. IKEv2 setup requires a Docker volume
mounted at /etc/ipsec.d.
EOF
fi
elif [ "$disable_ipsec_l2tp" = yes ]; then
cat <<'EOF'
Note: IPsec/L2TP mode is disabled via env file option.
EOF
elif [ "$disable_ipsec_xauth" = yes ]; then
cat <<'EOF'
Note: IPsec/XAuth ("Cisco IPsec") mode is disabled via env file option.
EOF
fi
# Create IPsec config
cat > /etc/ipsec.conf <<EOF
version 2.0
config setup
ikev1-policy=accept
virtual-private=%v4:10.0.0.0/8,%v4:192.168.0.0/16,%v4:172.16.0.0/12,%v4:!$L2TP_NET,%v4:!$XAUTH_NET$vp_ip6
uniqueids=no
conn shared
left=%defaultroute
leftid=$public_ip
right=%any
encapsulation=yes
authby=secret
pfs=no
rekey=no
dpddelay=30
dpdtimeout=300
ikev2=never
ike=$ike_algs
phase2alg=aes_gcm-null,aes128-sha1,aes256-sha1,aes256-sha2_512,aes128-sha2,aes256-sha2
ikelifetime=24h
salifetime=24h
sha2-truncbug=$sha2_truncbug
EOF
if [ "$disable_ipsec_l2tp" != yes ]; then
cat >> /etc/ipsec.conf <<'EOF'
conn l2tp-psk
auto=add
leftprotoport=17/1701
rightprotoport=17/%any
type=transport
also=shared
EOF
fi
if [ "$disable_ipsec_xauth" != yes ]; then
cat >> /etc/ipsec.conf <<EOF
conn xauth-psk
auto=add
leftsubnet=0.0.0.0/0
rightaddresspool=$XAUTH_POOL
modecfgdns=$DNS_SRVS
leftxauthserver=yes
rightxauthclient=yes
leftmodecfgserver=yes
rightmodecfgclient=yes
modecfgpull=yes
cisco-unity=yes
also=shared
EOF
fi
cat >> /etc/ipsec.conf <<'EOF'
include /etc/ipsec.d/*.conf
EOF
if uname -r | grep -qi 'coreos'; then
sed -i '/phase2alg/s/,aes256-sha2_512//' /etc/ipsec.conf
fi
if grep -qs ike-frag /etc/ipsec.d/ikev2.conf; then
sed -i 's/^[[:space:]]\+ike-frag=/ fragmentation=/' /etc/ipsec.d/ikev2.conf
fi
# Specify IPsec PSK
cat > /etc/ipsec.secrets <<EOF
%any %any : PSK "$VPN_IPSEC_PSK"
EOF
# Create xl2tpd config
cat > /etc/xl2tpd/xl2tpd.conf <<EOF
[global]
port = 1701
[lns default]
ip range = $L2TP_POOL
local ip = $L2TP_LOCAL
require chap = yes
refuse pap = yes
require authentication = yes
name = l2tpd
pppoptfile = /etc/ppp/options.xl2tpd
length bit = yes
EOF
# Set xl2tpd options
cat > /etc/ppp/options.xl2tpd <<EOF
+mschap-v2
ipcp-accept-local
ipcp-accept-remote
noccp
auth
mtu 1280
mru 1280
proxyarp
lcp-echo-failure 4
lcp-echo-interval 30
connect-delay 5000
ms-dns $DNS_SRV1
EOF
if [ -z "$VPN_DNS_SRV1" ] || [ -n "$VPN_DNS_SRV2" ]; then
cat >> /etc/ppp/options.xl2tpd <<EOF
ms-dns $DNS_SRV2
EOF
fi
# Create VPN credentials
cat > /etc/ppp/chap-secrets <<EOF
"$VPN_USER" l2tpd "$VPN_PASSWORD" *
EOF
VPN_PASSWORD_ENC=$(openssl passwd -1 "$VPN_PASSWORD")
cat > /etc/ipsec.d/passwd <<EOF
$VPN_USER:$VPN_PASSWORD_ENC:xauth-psk
EOF
if [ -n "$VPN_ADDL_USERS" ] && [ -n "$VPN_ADDL_PASSWORDS" ]; then
count=1
addl_user=$(printf '%s' "$VPN_ADDL_USERS" | cut -d ' ' -f 1)
addl_password=$(printf '%s' "$VPN_ADDL_PASSWORDS" | cut -d ' ' -f 1)
addl_ip=$(printf '%s' "$VPN_ADDL_IP_ADDRS" | cut -d ' ' -f 1)
while [ -n "$addl_user" ] && [ -n "$addl_password" ]; do
addl_ip_l2tp="$addl_ip"
addl_ip_xauth="$addl_ip"
if [ "$addl_ip" = "*" ] || ! check_ip "$addl_ip"; then
addl_ip_l2tp=""
addl_ip_xauth=""
elif [ "$L2TP_NET" = "192.168.42.0/24" ] && [ "$XAUTH_NET" = "192.168.43.0/24" ]; then
addl_ip_part=$(printf '%s' "$addl_ip" | cut -f 1-3 -d '.')
if [ "$addl_ip_part" = "192.168.42" ]; then
addl_ip_xauth=""
elif [ "$addl_ip_part" = "192.168.43" ]; then
addl_ip_l2tp=""
else
addl_ip_l2tp=""
addl_ip_xauth=""
fi
fi
cat >> /etc/ppp/chap-secrets <<EOF
"$addl_user" l2tpd "$addl_password" ${addl_ip_l2tp:-*}
EOF
[ -n "$addl_ip_xauth" ] && addl_ip_xauth=$(printf '%s' ":$addl_ip_xauth")
addl_password_enc=$(openssl passwd -1 "$addl_password")
cat >> /etc/ipsec.d/passwd <<EOF
$addl_user:$addl_password_enc:xauth-psk${addl_ip_xauth}
EOF
count=$((count+1))
addl_user=$(printf '%s' "$VPN_ADDL_USERS" | cut -s -d ' ' -f "$count")
addl_password=$(printf '%s' "$VPN_ADDL_PASSWORDS" | cut -s -d ' ' -f "$count")
addl_ip=$(printf '%s' "$VPN_ADDL_IP_ADDRS" | cut -s -d ' ' -f "$count")
done
fi
# Update sysctl settings
syt='/sbin/sysctl -e -q -w'
$syt kernel.msgmnb=65536 2>/dev/null
$syt kernel.msgmax=65536 2>/dev/null
$syt net.ipv4.ip_forward=1 2>/dev/null
$syt net.ipv4.conf.all.accept_redirects=0 2>/dev/null
$syt net.ipv4.conf.all.send_redirects=0 2>/dev/null
$syt net.ipv4.conf.all.rp_filter=0 2>/dev/null
$syt net.ipv4.conf.default.accept_redirects=0 2>/dev/null
$syt net.ipv4.conf.default.send_redirects=0 2>/dev/null
$syt net.ipv4.conf.default.rp_filter=0 2>/dev/null
$syt "net.ipv4.conf.$NET_IFACE.send_redirects=0" 2>/dev/null
$syt "net.ipv4.conf.$NET_IFACE.rp_filter=0" 2>/dev/null
$syt net.ipv4.tcp_rmem="4096 87380 16777216" 2>/dev/null
$syt net.ipv4.tcp_wmem="4096 87380 16777216" 2>/dev/null
if [ -n "$ip6" ]; then
$syt net.ipv6.conf.all.forwarding=1 2>/dev/null
fi
if modprobe -q tcp_bbr 2>/dev/null \
&& printf '%s\n%s' "4.20" "$(uname -r)" | sort -C -V; then
$syt net.ipv4.tcp_congestion_control=bbr 2>/dev/null
fi
# Create IPTables rules
ipi='iptables -I INPUT'
ipf='iptables -I FORWARD'
ipp='iptables -t nat -I POSTROUTING'
res='RELATED,ESTABLISHED'
modprobe -q ip_tables 2>/dev/null
if ! iptables -t nat -C POSTROUTING -s "$L2TP_NET" -o "$NET_IFACE" -j MASQUERADE 2>/dev/null; then
$ipi 1 -p udp --dport 1701 -m policy --dir in --pol none -j DROP
$ipi 2 -m conntrack --ctstate INVALID -j DROP
$ipi 3 -m conntrack --ctstate "$res" -j ACCEPT
$ipi 4 -p udp -m multiport --dports 500,4500 -j ACCEPT
$ipi 5 -p udp --dport 1701 -m policy --dir in --pol ipsec -j ACCEPT
$ipi 6 -p udp --dport 1701 -j DROP
$ipf 1 -m conntrack --ctstate INVALID -j DROP
$ipf 2 -i "$NET_IFACE" -o ppp+ -m conntrack --ctstate "$res" -j ACCEPT
$ipf 3 -i ppp+ -o "$NET_IFACE" -j ACCEPT
$ipf 4 -i ppp+ -o ppp+ -j ACCEPT
$ipf 5 -i "$NET_IFACE" -d "$XAUTH_NET" -m conntrack --ctstate "$res" -j ACCEPT
$ipf 6 -s "$XAUTH_NET" -o "$NET_IFACE" -j ACCEPT
$ipf 7 -s "$XAUTH_NET" -o ppp+ -j ACCEPT
# Client-to-client traffic is allowed by default. To *disallow* such traffic,
# uncomment below and restart the Docker container.
# $ipf 2 -i ppp+ -o ppp+ -s "$L2TP_NET" -d "$L2TP_NET" -j DROP
# $ipf 3 -s "$XAUTH_NET" -d "$XAUTH_NET" -j DROP
# $ipf 4 -i ppp+ -d "$XAUTH_NET" -j DROP
# $ipf 5 -s "$XAUTH_NET" -o ppp+ -j DROP
iptables -A FORWARD -j DROP
if ! $ipp -s "$XAUTH_NET" -o "$NET_IFACE" -m policy --dir out --pol none -j MASQUERADE; then
$ipp -s "$XAUTH_NET" -o "$NET_IFACE" ! -d "$XAUTH_NET" -j MASQUERADE
fi
$ipp -s "$L2TP_NET" -o "$NET_IFACE" -j MASQUERADE
fi
if [ -n "$ip6" ]; then
modprobe -q ip6_tables 2>/dev/null
if ip6tables -t nat -L >/dev/null 2>&1; then
ipi6='ip6tables -I INPUT'
ipf6='ip6tables -I FORWARD'
ipp6='ip6tables -t nat -I POSTROUTING'
if ! ip6tables -t nat -C POSTROUTING -s "$IP6_NET" -o "$NET_IFACE" \
-j MASQUERADE 2>/dev/null; then
$ipi6 1 -m conntrack --ctstate INVALID -j DROP
$ipi6 2 -m conntrack --ctstate "$res" -j ACCEPT
$ipi6 3 -p udp -m multiport --dports 500,4500 -j ACCEPT
$ipf6 1 -m conntrack --ctstate INVALID -j DROP
$ipf6 2 -i "$NET_IFACE" -d "$IP6_NET" -m conntrack --ctstate "$res" -j ACCEPT
$ipf6 3 -s "$IP6_NET" -o "$NET_IFACE" -j ACCEPT
if ! $ipp6 -s "$IP6_NET" -o "$NET_IFACE" \
-m policy --dir out --pol none -j MASQUERADE; then
$ipp6 -s "$IP6_NET" -o "$NET_IFACE" ! -d "$IP6_NET" -j MASQUERADE
fi
fi
fi
fi
case $VPN_ANDROID_MTU_FIX in
[yY][eE][sS])
echo
echo "Applying fix for Android MTU/MSS issues..."
iptables -t mangle -A FORWARD -m policy --pol ipsec --dir in \
-p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 \
-j TCPMSS --set-mss 1360
iptables -t mangle -A FORWARD -m policy --pol ipsec --dir out \
-p tcp -m tcp --tcp-flags SYN,RST SYN -m tcpmss --mss 1361:1536 \
-j TCPMSS --set-mss 1360
echo 1 > /proc/sys/net/ipv4/ip_no_pmtu_disc
;;
esac
# Update file attributes
chmod 600 /etc/ipsec.secrets /etc/ppp/chap-secrets /etc/ipsec.d/passwd
echo
echo "Starting IPsec service..."
mkdir -p /run/pluto /var/run/pluto
rm -f /run/pluto/pluto.pid /var/run/pluto/pluto.pid
if [ "$os_type" = "alpine" ]; then
sed -i '1c\#!/sbin/openrc-run' /etc/init.d/ipsec
rc-status >/dev/null 2>&1
rc-service ipsec zap >/dev/null
rc-service -D ipsec start >/dev/null 2>&1
mkdir -p /etc/crontabs
cron_cmd="rc-service -c -D ipsec zap start"
if ! grep -qs "$cron_cmd" /etc/crontabs/root; then
cat >> /etc/crontabs/root <<EOF
* * * * * $cron_cmd
* * * * * sleep 15; $cron_cmd
* * * * * sleep 30; $cron_cmd
* * * * * sleep 45; $cron_cmd
EOF
fi
/usr/sbin/crond -L /dev/null
else
service ipsec start >/dev/null 2>&1
fi
if [ -n "$VPN_DNS_NAME" ]; then
server_text="Server"
else
server_text="Server IP"
fi
if [ "$disable_ipsec_l2tp" != yes ] || [ "$disable_ipsec_xauth" != yes ]; then
cat <<EOF
================================================
IPsec VPN server is now ready for use!
Connect to your new VPN with these details:
$server_text: $server_addr
IPsec PSK: $VPN_IPSEC_PSK
Username: $VPN_USER
Password: $VPN_PASSWORD
EOF
if [ -n "$VPN_ADDL_USERS" ] && [ -n "$VPN_ADDL_PASSWORDS" ]; then
count=1
addl_user=$(printf '%s' "$VPN_ADDL_USERS" | cut -d ' ' -f 1)
addl_password=$(printf '%s' "$VPN_ADDL_PASSWORDS" | cut -d ' ' -f 1)
cat <<'EOF'
Additional VPN users (username | password):
EOF
while [ -n "$addl_user" ] && [ -n "$addl_password" ]; do
cat <<EOF
$addl_user | $addl_password
EOF
count=$((count+1))
addl_user=$(printf '%s' "$VPN_ADDL_USERS" | cut -s -d ' ' -f "$count")
addl_password=$(printf '%s' "$VPN_ADDL_PASSWORDS" | cut -s -d ' ' -f "$count")
done
fi
cat <<'EOF'
Write these down. You'll need them to connect!
VPN client setup: https://vpnsetup.net/clients2
================================================
EOF
fi
# Set up IKEv2
status=0
ikev2_sh="/opt/src/ikev2.sh"
ikev2_conf="/etc/ipsec.d/ikev2.conf"
ikev2_log="/etc/ipsec.d/ikev2setup.log"
if grep -q " /etc/ipsec.d " /proc/mounts && [ -s "$ikev2_sh" ] && [ ! -f "$ikev2_conf" ]; then
echo
echo "Setting up IKEv2. This may take a few moments..."
if [ -n "$VPN_SPLIT_IKEV2" ]; then
sed -i "s|^ leftsubnet=0\.0\.0\.0/0$| leftsubnet=$VPN_SPLIT_IKEV2|g" "$ikev2_sh"
fi
if VPN_DNS_NAME="$VPN_DNS_NAME" VPN_PUBLIC_IP="$public_ip" \
VPN_CLIENT_NAME="$VPN_CLIENT_NAME" VPN_XAUTH_POOL="$VPN_XAUTH_POOL" \
VPN_DNS_SRV1="$VPN_DNS_SRV1" VPN_DNS_SRV2="$VPN_DNS_SRV2" \
VPN_PROTECT_CONFIG="$VPN_PROTECT_CONFIG" \
VPN_PUBLIC_IP6="$ip6" \
/bin/bash "$ikev2_sh" --auto >"$ikev2_log" 2>&1; then
status=1
status_text="IKEv2 setup successful."
else
status=4
rm -f "$ikev2_conf"
echo "IKEv2 setup failed."
fi
chmod 600 "$ikev2_log"
fi
if [ "$status" = 0 ] && [ -f "$ikev2_conf" ] && [ -s "$ikev2_log" ]; then
status=2
status_text="IKEv2 is already set up."
fi
if [ "$status" = 1 ] || [ "$status" = 2 ]; then
cat <<EOF
================================================
$status_text Details for IKEv2 mode:
EOF
sed -n '/VPN server address:/,/Next steps:/p' "$ikev2_log"
cat <<'EOF'
https://vpnsetup.net/clients2
================================================
EOF
else
echo
fi
if [ "$status" = 2 ] && [ -n "$VPN_DNS_NAME" ]; then
server_addr_cur=$(grep -s "leftcert=" /etc/ipsec.d/ikev2.conf | cut -f2 -d= | head -n 1)
if [ "$VPN_DNS_NAME" != "$server_addr_cur" ]; then
cat <<'EOF'
Warning: The VPN_DNS_NAME variable you specified has no effect
for IKEv2 mode, because IKEv2 is already set up in this
container. To change the IKEv2 server address, see:
https://vpnsetup.net/ikev2docker
EOF
fi
fi
# Check for new Libreswan version
ts_file="/opt/src/swanver"
if [ ! -f "$ts_file" ] || [ "$(find "$ts_file" -mmin +10080)" ]; then
touch "$ts_file"
ipsec_ver=$(ips
gitextract_e54mtk_x/ ├── .dockerignore ├── .github/ │ ├── ISSUE_TEMPLATE/ │ │ ├── 00-bug-report.md │ │ ├── 10-bug-report-zh.md │ │ ├── 20-enhancement-request.md │ │ └── 30-enhancement-request-zh.md │ └── workflows/ │ ├── buildx.yml │ ├── check_update.yml │ ├── cron-alpine.yml │ ├── cron-debian.yml │ ├── main-alpine.yml │ ├── main-debian.yml │ ├── shellcheck.yml │ └── vpn_test.yml ├── .gitignore ├── Dockerfile ├── Dockerfile.debian ├── LICENSE.md ├── README-ru.md ├── README-zh-Hant.md ├── README-zh.md ├── README.md ├── docker-compose.yml ├── docs/ │ ├── advanced-usage-zh.md │ ├── advanced-usage.md │ ├── vpn-book-zh-Hant.md │ ├── vpn-book-zh.md │ └── vpn-book.md ├── run.sh └── vpn.env.example
Condensed preview — 29 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (207K chars).
[
{
"path": ".dockerignore",
"chars": 101,
"preview": ".git\n.github\n.gitignore\ndocker-compose.yml\nLICENSE.md\nREADME.md\nREADME-zh.md\nvpn.env\nvpn.env.example\n"
},
{
"path": ".github/ISSUE_TEMPLATE/00-bug-report.md",
"chars": 2453,
"preview": "---\nname: Bug report\nabout: Tell us about a problem you are experiencing\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**Chec"
},
{
"path": ".github/ISSUE_TEMPLATE/10-bug-report-zh.md",
"chars": 1805,
"preview": "---\nname: 错误报告\nabout: 请使用这个模板来提交 bug\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**任务列表**\n\n- [ ] 我已阅读[自述文件](https://github."
},
{
"path": ".github/ISSUE_TEMPLATE/20-enhancement-request.md",
"chars": 1577,
"preview": "---\nname: Enhancement request\nabout: Suggest an improvement for this project\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**"
},
{
"path": ".github/ISSUE_TEMPLATE/30-enhancement-request-zh.md",
"chars": 1073,
"preview": "---\nname: 改进建议\nabout: 请使用这个模板来提交改进建议\ntitle: ''\nlabels: ''\nassignees: ''\n\n---\n\n**任务列表**\n\n- [ ] 我搜索了已有的 [Issues](https://g"
},
{
"path": ".github/workflows/buildx.yml",
"chars": 7267,
"preview": "#\n# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": ".github/workflows/check_update.yml",
"chars": 3394,
"preview": "#\n# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": ".github/workflows/cron-alpine.yml",
"chars": 1168,
"preview": "#\n# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": ".github/workflows/cron-debian.yml",
"chars": 1173,
"preview": "#\n# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": ".github/workflows/main-alpine.yml",
"chars": 1202,
"preview": "#\n# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": ".github/workflows/main-debian.yml",
"chars": 1215,
"preview": "#\n# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": ".github/workflows/shellcheck.yml",
"chars": 1023,
"preview": "#\n# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": ".github/workflows/vpn_test.yml",
"chars": 6909,
"preview": "#\n# Copyright (C) 2020-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": ".gitignore",
"chars": 8,
"preview": "vpn.env\n"
},
{
"path": "Dockerfile",
"chars": 2966,
"preview": "#\n# Copyright (C) 2021-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": "Dockerfile.debian",
"chars": 3135,
"preview": "#\n# Copyright (C) 2016-2026 Lin Song <linsongui@gmail.com>\n#\n# This work is licensed under the Creative Commons Attribut"
},
{
"path": "LICENSE.md",
"chars": 20533,
"preview": "### Creative Commons Attribution-ShareAlike 3.0 Unported License\nLink to license summary: https://creativecommons.org/li"
},
{
"path": "README-ru.md",
"chars": 21169,
"preview": "[English](README.md) | [简体中文](README-zh.md) | [繁體中文](README-zh-Hant.md) | [Русский](README-ru.md)\n\n# IPsec VPN сервер на"
},
{
"path": "README-zh-Hant.md",
"chars": 13324,
"preview": "[English](README.md) | [简体中文](README-zh.md) | [繁體中文](README-zh-Hant.md) | [Русский](README-ru.md)\n\n# Docker 上的 IPsec VPN"
},
{
"path": "README-zh.md",
"chars": 13296,
"preview": "[English](README.md) | [简体中文](README-zh.md) | [繁體中文](README-zh-Hant.md) | [Русский](README-ru.md)\n\n# Docker 上的 IPsec VPN"
},
{
"path": "README.md",
"chars": 19546,
"preview": "[English](README.md) | [简体中文](README-zh.md) | [繁體中文](README-zh-Hant.md) | [Русский](README-ru.md)\n\n# IPsec VPN Server on"
},
{
"path": "docker-compose.yml",
"chars": 861,
"preview": "version: '3'\n\nvolumes:\n ikev2-vpn-data:\n\nservices:\n vpn:\n image: hwdsl2/ipsec-vpn-server\n restart: always\n en"
},
{
"path": "docs/advanced-usage-zh.md",
"chars": 15050,
"preview": "[English](advanced-usage.md) | [中文](advanced-usage-zh.md)\n\n# 高级用法\n\n- [使用其他的 DNS 服务器](#使用其他的-dns-服务器)\n- [不启用 privileged 模"
},
{
"path": "docs/advanced-usage.md",
"chars": 23127,
"preview": "[English](advanced-usage.md) | [中文](advanced-usage-zh.md)\n\n# Advanced Usage\n\n- [Use alternative DNS servers](#use-altern"
},
{
"path": "docs/vpn-book-zh-Hant.md",
"chars": 2419,
"preview": "[« 返回首頁](../README-zh-Hant.md) | [English](vpn-book.md) | [简体中文](vpn-book-zh.md) | [繁體中文](vpn-book-zh-Hant.md)\n\n##"
},
{
"path": "docs/vpn-book-zh.md",
"chars": 2407,
"preview": "[« 返回主页](../README-zh.md) | [English](vpn-book.md) | [简体中文](vpn-book-zh.md) | [繁體中文](vpn-book-zh-Hant.md)\n\n## 新:Pr"
},
{
"path": "docs/vpn-book.md",
"chars": 2712,
"preview": "[« Back to home page](../README.md) | [English](vpn-book.md) | [简体中文](vpn-book-zh.md) | [繁體中文](vpn-book-zh-Hant.md"
},
{
"path": "run.sh",
"chars": 26274,
"preview": "#!/bin/bash\n#\n# Docker script to configure and start an IPsec VPN server\n#\n# DO NOT RUN THIS SCRIPT ON YOUR PC OR MAC! T"
},
{
"path": "vpn.env.example",
"chars": 1983,
"preview": "# Note: All the variables to this image are optional.\n# See README for more information.\n# To use, uncomment and replace"
}
]
About this extraction
This page contains the full source code of the hwdsl2/docker-ipsec-vpn-server GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 29 files (194.5 KB), approximately 65.5k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.