diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index af24b421..00f6bc97 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -27,7 +27,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Initialize CodeQL uses: github/codeql-action/init@v4 diff --git a/.github/workflows/deploy-development.yml b/.github/workflows/deploy-development.yml index 3fcdde6e..9d549148 100644 --- a/.github/workflows/deploy-development.yml +++ b/.github/workflows/deploy-development.yml @@ -18,10 +18,10 @@ jobs: os: ubuntu-latest - platform: linux/arm64 os: ubuntu-24.04-arm - - platform: linux/arm/v7 - os: ubuntu-24.04-arm + # - platform: linux/arm/v7 + # os: ubuntu-24.04-arm steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Prepare run: | @@ -30,7 +30,7 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: | ghcr.io/wg-easy/wg-easy @@ -38,21 +38,21 @@ jobs: latest=false - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Build and push by digest id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . platforms: ${{ matrix.arch.platform }} @@ -69,7 +69,7 @@ jobs: touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: digests-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/* @@ -85,32 +85,32 @@ jobs: needs: docker-build steps: - name: Download digests - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: path: ${{ runner.temp }}/digests pattern: digests-* merge-multiple: true - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Codeberg - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: codeberg.org username: ${{ secrets.CODEBERG_USER }} password: ${{ secrets.CODEBERG_PASS }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: | ghcr.io/wg-easy/wg-easy @@ -138,7 +138,7 @@ jobs: contents: write needs: docker-merge steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v6 diff --git a/.github/workflows/deploy-edge.yml b/.github/workflows/deploy-edge.yml index 6999b23d..4e46b92a 100644 --- a/.github/workflows/deploy-edge.yml +++ b/.github/workflows/deploy-edge.yml @@ -25,10 +25,10 @@ jobs: os: ubuntu-latest - platform: linux/arm64 os: ubuntu-24.04-arm - - platform: linux/arm/v7 - os: ubuntu-24.04-arm + # - platform: linux/arm/v7 + # os: ubuntu-24.04-arm steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: ref: master @@ -39,7 +39,7 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: | ghcr.io/wg-easy/wg-easy @@ -47,21 +47,21 @@ jobs: latest=false - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Build and push by digest id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . platforms: ${{ matrix.arch.platform }} @@ -78,7 +78,7 @@ jobs: touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: digests-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/* @@ -94,32 +94,32 @@ jobs: needs: docker-build steps: - name: Download digests - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: path: ${{ runner.temp }}/digests pattern: digests-* merge-multiple: true - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Codeberg - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: codeberg.org username: ${{ secrets.CODEBERG_USER }} password: ${{ secrets.CODEBERG_PASS }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: | ghcr.io/wg-easy/wg-easy @@ -147,7 +147,7 @@ jobs: contents: write needs: docker-merge steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: ref: master diff --git a/.github/workflows/deploy-pr.yml b/.github/workflows/deploy-pr.yml index da215625..ca8710fc 100644 --- a/.github/workflows/deploy-pr.yml +++ b/.github/workflows/deploy-pr.yml @@ -21,10 +21,10 @@ jobs: os: ubuntu-latest - platform: linux/arm64 os: ubuntu-24.04-arm - - platform: linux/arm/v7 - os: ubuntu-24.04-arm + # - platform: linux/arm/v7 + # os: ubuntu-24.04-arm steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Prepare run: | @@ -32,20 +32,20 @@ jobs: echo "PLATFORM_PAIR=${platform//\//-}" >> $GITHUB_ENV - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Build Docker Image - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . push: false diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 1d0f8715..f45b5589 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -26,10 +26,10 @@ jobs: os: ubuntu-latest - platform: linux/arm64 os: ubuntu-24.04-arm - - platform: linux/arm/v7 - os: ubuntu-24.04-arm + # - platform: linux/arm/v7 + # os: ubuntu-24.04-arm steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Prepare run: | @@ -38,7 +38,7 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: | ghcr.io/wg-easy/wg-easy @@ -46,21 +46,21 @@ jobs: latest=false - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@v4 - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Build and push by digest id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@v7 with: context: . platforms: ${{ matrix.arch.platform }} @@ -77,7 +77,7 @@ jobs: touch "${{ runner.temp }}/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v5 + uses: actions/upload-artifact@v7 with: name: digests-${{ env.PLATFORM_PAIR }} path: ${{ runner.temp }}/digests/* @@ -95,32 +95,32 @@ jobs: needs: docker-build steps: - name: Download digests - uses: actions/download-artifact@v6 + uses: actions/download-artifact@v8 with: path: ${{ runner.temp }}/digests pattern: digests-* merge-multiple: true - name: Login to GitHub Container Registry - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Login to Codeberg - uses: docker/login-action@v3 + uses: docker/login-action@v4 with: registry: codeberg.org username: ${{ secrets.CODEBERG_USER }} password: ${{ secrets.CODEBERG_PASS }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@v4 - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@v6 with: images: | ghcr.io/wg-easy/wg-easy @@ -152,7 +152,7 @@ jobs: contents: write needs: docker-merge steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup Python uses: actions/setup-python@v6 diff --git a/.github/workflows/lint.yml b/.github/workflows/lint.yml index 0ace07ab..366e5440 100644 --- a/.github/workflows/lint.yml +++ b/.github/workflows/lint.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: pnpm/action-setup@v4 name: Install pnpm @@ -24,7 +24,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v6 with: - node-version: "lts/jod" + node-version: "lts/krypton" check-latest: true cache: "pnpm" @@ -47,7 +47,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - uses: pnpm/action-setup@v4 name: Install pnpm @@ -57,7 +57,7 @@ jobs: - name: Setup Node uses: actions/setup-node@v6 with: - node-version: "lts/jod" + node-version: "lts/krypton" check-latest: true cache: "pnpm" diff --git a/CHANGELOG.md b/CHANGELOG.md index 0a3350f3..1ed45d74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,7 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -## Added +### Added + +- AWG: support for H1-H4 ranges (https://github.com/wg-easy/wg-easy/pull/2480) +- Client Firewall (https://github.com/wg-easy/wg-easy/pull/2418) +- CLI: Show QR code (https://github.com/wg-easy/wg-easy/pull/2518) +- Copy QR code to clipboard / save as png (https://github.com/wg-easy/wg-easy/pull/2521) + +### Changed + +- Hooks are now Textareas (https://github.com/wg-easy/wg-easy/pull/2522) +- Update to Node Krypton (24) (https://github.com/wg-easy/wg-easy/pull/2536) + +## [15.2.2] - 2026-02-06 + +### Added + +- Added Userspace WireGuard support (https://github.com/wg-easy/wg-easy/pull/2419) + +### Fixed + +- LangSelector overlapping with Buttons (https://github.com/wg-easy/wg-easy/pull/2434) +- AmnzeziaWG config parameters (https://github.com/wg-easy/wg-easy/pull/2440) +- OpenMetrics help string format (https://github.com/wg-easy/wg-easy/pull/2453) +- Reset 2fa when resetting admin password (https://github.com/wg-easy/wg-easy/pull/2461) + +### Docs + +- Replace Watchtower with maintained fork (https://github.com/wg-easy/wg-easy/pull/2456) + +## [15.2.1] - 2026-01-14 + +### Fixed + +- Icon in Searchbar (https://github.com/wg-easy/wg-easy/commit/458f66818a400f181e2c6326ede077c8793d71f2) +- Interface save not working (https://github.com/wg-easy/wg-easy/commit/48f3fbd715a889e2425702a8a46332f2752aef91) +- Error Messages in Setup (https://github.com/wg-easy/wg-easy/commit/32a055093a76342c40858d8dcf563b0700a8bd48) + +## [15.2.0] - 2026-01-12 + +### Added - AmneziaWG integration (https://github.com/wg-easy/wg-easy/pull/2102, https://github.com/wg-easy/wg-easy/pull/2226) - Search / filter box (https://github.com/wg-easy/wg-easy/pull/2170) @@ -15,12 +54,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Show client endpoint (https://github.com/wg-easy/wg-easy/pull/2058) - Add option to view and copy config (https://github.com/wg-easy/wg-easy/pull/2289) -## Fixed +### Fixed - Fix download as conf.txt (https://github.com/wg-easy/wg-easy/pull/2269) - Clean filename for OTL download (https://github.com/wg-easy/wg-easy/pull/2253) +- Text color in admin menu in light mode (https://github.com/wg-easy/wg-easy/pull/2307) -## Changed +### Changed - Allow lower MTU (https://github.com/wg-easy/wg-easy/pull/2228) - Use /32 and /128 for client Cidr (https://github.com/wg-easy/wg-easy/pull/2217) @@ -29,11 +69,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - Allow empty DNS (https://github.com/wg-easy/wg-easy/pull/2052, https://github.com/wg-easy/wg-easy/pull/2057) - Don't include keys in API responses (https://github.com/wg-easy/wg-easy/pull/2015) - Try all QR ecc levels (https://github.com/wg-easy/wg-easy/pull/2288) +- Update OneTimeLink expiry on reuse (https://github.com/wg-easy/wg-easy/pull/2370) +- Removed ARMv7 support (https://github.com/wg-easy/wg-easy/pull/2369) -## Docs +### Docs - Add AdGuard Home (https://github.com/wg-easy/wg-easy/pull/2175) -- Add Routed (No NAT) docs (https://github.com/wg-easy/wg-easy/pull/2181) +- Add Routed (No NAT) docs (https://github.com/wg-easy/wg-easy/pull/2181, https://github.com/wg-easy/wg-easy/pull/2380) +- Add AmneziaWG docs (https://github.com/wg-easy/wg-easy/pull/2108, https://github.com/wg-easy/wg-easy/pull/2292) ## [15.1.0] - 2025-07-01 diff --git a/Dockerfile b/Dockerfile index 45b46d50..61676675 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM docker.io/library/node:jod-alpine AS build +FROM docker.io/library/node:krypton-alpine AS build WORKDIR /app # update corepack @@ -15,14 +15,17 @@ COPY src ./ RUN pnpm build # Build amneziawg-tools -RUN apk add linux-headers build-base git && \ +RUN apk add linux-headers build-base go git && \ git clone https://github.com/amnezia-vpn/amneziawg-tools.git && \ - cd amneziawg-tools/src && \ + git clone https://github.com/amnezia-vpn/amneziawg-go && \ + cd amneziawg-go && \ + make && \ + cd ../amneziawg-tools/src && \ make # Copy build result to a new image. # This saves a lot of disk space. -FROM docker.io/library/node:jod-alpine +FROM docker.io/library/node:krypton-alpine WORKDIR /app HEALTHCHECK --interval=1m --timeout=5s --retries=3 CMD /usr/bin/timeout 5s /bin/sh -c "/usr/bin/wg show | /bin/grep -q interface || exit 1" @@ -33,11 +36,14 @@ COPY --from=build /app/.output /app COPY --from=build /app/server/database/migrations /app/server/database/migrations # libsql (https://github.com/nitrojs/nitro/issues/3328) RUN cd /app/server && \ - npm install --no-save libsql && \ + npm install --no-save --omit=dev libsql && \ npm cache clean --force # cli COPY --from=build /app/cli/cli.sh /usr/local/bin/cli RUN chmod +x /usr/local/bin/cli +# Copy amneziawg-go +COPY --from=build /app/amneziawg-go/amneziawg-go /usr/bin/amneziawg-go +RUN chmod +x /usr/bin/amneziawg-go # Copy amneziawg-tools COPY --from=build /app/amneziawg-tools/src/wg /usr/bin/awg COPY --from=build /app/amneziawg-tools/src/wg-quick/linux.bash /usr/bin/awg-quick @@ -52,6 +58,7 @@ RUN apk add --no-cache \ nftables \ kmod \ iptables-legacy \ + wireguard-go \ wireguard-tools RUN mkdir -p /etc/amnezia diff --git a/Dockerfile.dev b/Dockerfile.dev index e9dcec16..5d3fab12 100644 --- a/Dockerfile.dev +++ b/Dockerfile.dev @@ -1,4 +1,4 @@ -FROM docker.io/library/node:jod-alpine +FROM docker.io/library/node:krypton-alpine WORKDIR /app # update corepack @@ -16,6 +16,7 @@ RUN apk add --no-cache \ ip6tables \ kmod \ iptables-legacy \ + wireguard-go \ wireguard-tools # Use iptables-legacy diff --git a/README.md b/README.md index 741cc12e..7a1db607 100644 --- a/README.md +++ b/README.md @@ -33,6 +33,7 @@ You have found the easiest way to install & manage WireGuard on any Linux host! - IPv6 support - CIDR support - 2FA support +- Per-client firewall filtering (requires iptables) > [!NOTE] > To better manage documentation for this project, it has its own site here: [https://wg-easy.github.io/wg-easy/latest](https://wg-easy.github.io/wg-easy/latest) diff --git a/assets/screenshot.png b/assets/screenshot.png index dd39f3ff..b55deb79 100644 Binary files a/assets/screenshot.png and b/assets/screenshot.png differ diff --git a/docs/content/advanced/config/amnezia.md b/docs/content/advanced/config/amnezia.md index bdead28b..1d4f2b5b 100644 --- a/docs/content/advanced/config/amnezia.md +++ b/docs/content/advanced/config/amnezia.md @@ -2,7 +2,9 @@ title: AmneziaWG --- -Experimental support for AmneziaWG can be enabled by setting the `EXPERIMENTAL_AWG` environment variable to `true`. This feature is still under development and may change in future releases. +## Introduction + +**AmneziaWG** is a modified version of the WireGuard protocol with enhanced traffic obfuscation capabilities. AmneziaWG's primary goal is to counter deep packet inspection (DPI) systems and bypass VPN blocking. AmneziaWG adds multi-level transport-layer obfuscation by: @@ -12,6 +14,12 @@ AmneziaWG adds multi-level transport-layer obfuscation by: These measures make it harder for third parties to analyze or identify your traffic, enhancing both privacy and security. +## Activating AmneziaWG + +You must install the [AmneziaWG kernel module](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module) on the host system. + +Experimental support for AmneziaWG can be enabled by setting the `EXPERIMENTAL_AWG` environment variable to `true`. Starting from wg-easy version 16, this setting will be enabled by default. This feature is still under development and may change in future releases. + When enabled, wg-easy will automatically detect whether the AmneziaWG kernel module is available. If it is not, the system will fall back to the standard WireGuard module. To override this automatic detection, set the `OVERRIDE_AUTO_AWG` environment variable. By default, this variable is unset. @@ -21,17 +29,51 @@ Possible values: - `awg` — Force use of AmneziaWG - `wg` — Force use of standard WireGuard -To be able to connect to wg-easy if AmneziaWG is enabled, you must have a AmneziaWG-compatible client. +## AmneziaWG Parameters + +Parameter descriptions can be found in the [AmneziaWG documentation](https://docs.amnezia.org/documentation/amnezia-wg) and on the [kernel module page](https://github.com/amnezia-vpn/amneziawg-linux-kernel-module). + +All parameters except I1-I5 will be set at first startup. For information on how to set I1-I5 parameters, refer to the [AmneziaWG documentation](https://docs.amnezia.org/documentation/instructions/new-amneziawg-selfhosted/#how-to-extract-a-protocol-signature-for-amneziawg-15-manually). + +If a parameter is not set, it will not be added to the configuration. If all AmneziaWG-specific parameters are absent, AmneziaWG will be fully compatible with standard WireGuard. + +### Parameter Compatibility Table + +| Parameter | Can differ between server and client | Configurable on server | Configurable on client | +| --------- | ------------------------------------ | ---------------------- | ----------------------- | +| Jc | ✅ Yes | ✅ | ✅ | +| Jmin | ✅ Yes | ✅ | ✅ | +| Jmax | ✅ Yes | ✅ | ✅ | +| S1-S4 | ❌ No, must match | ✅ | ❌ (copied from server) | +| H1-H4 | ❌ No, must match | ✅ | ❌ (copied from server) | +| I1-I5 | ✅ Yes | ✅ | ✅ | + +## Client Applications + +To be able to connect to wg-easy if AmneziaWG is enabled, you must have an AmneziaWG-compatible client. Where an AmneziaWG app is available for your platform, it is recommended to use it rather than Amnezia VPN. Android: -- [AmneziaWG](https://play.google.com/store/apps/details?id=org.amnezia.awg) - Official Client +- [AmneziaWG](https://play.google.com/store/apps/details?id=org.amnezia.awg) - AmneziaWG Official Client - [WG Tunnel](https://play.google.com/store/apps/details?id=com.zaneschepke.wireguardautotunnel) - Third Party Client +- [Amnezia VPN](https://play.google.com/store/apps/details?id=org.amnezia.vpn) - Amnezia VPN Official Client iOS and macOS: -- [AmneziaWG](https://apps.apple.com/us/app/amneziawg/id6478942365) - Official Client +- [AmneziaWG](https://apps.apple.com/us/app/amneziawg/id6478942365) - AmneziaWG Official Client +- [Amnezia VPN](https://apps.apple.com/us/app/amneziavpn/id1600529900) - Amnezia VPN Official Client Windows: -- [AmneziaWG](https://github.com/amnezia-vpn/amneziawg-windows-client/releases) - Official Client +- [AmneziaWG](https://github.com/amnezia-vpn/amneziawg-windows-client/releases) - AmneziaWG Official Client (Requires building from source code) +- [Amnezia VPN](https://amnezia.org/downloads) - Amnezia VPN Official Client + +Linux: + +- [Amnezia VPN](https://amnezia.org/downloads) - Amnezia VPN Official Client +- [amneziawg-tools](https://github.com/amnezia-vpn/amneziawg-tools) - AmneziaWG Tools + +OpenWRT: + +- [AmneziaWG OpenWRT](https://github.com/Slava-Shchipunov/awg-openwrt) - AmneziaWG OpenWRT Packages +- [AmneziaWG OpenWRT](https://github.com/lolo6oT/awg-openwrt) - AmneziaWG OpenWRT Packages diff --git a/docs/content/advanced/migrate/from-14-to-15.md b/docs/content/advanced/migrate/from-14-to-15.md index c0e2bd16..3ec3276e 100644 --- a/docs/content/advanced/migrate/from-14-to-15.md +++ b/docs/content/advanced/migrate/from-14-to-15.md @@ -7,7 +7,7 @@ This guide will help you migrate from `v14` to version `v15` of `wg-easy`. ## Changes - This is a complete rewrite of the `wg-easy` project, therefore the configuration files and the way you interact with the project have changed. -- If you use armv6, you unfortunately won't be able to migrate to `v15`. +- If you use armv6 or armv7, you unfortunately won't be able to migrate to `v15`. - If you are connecting to the Web UI via HTTP, you need to set the `INSECURE` environment variable to `true` in the new container. ## Migration diff --git a/docs/content/examples/tutorials/auto-updates.md b/docs/content/examples/tutorials/auto-updates.md index 2386e2b6..c5883f02 100644 --- a/docs/content/examples/tutorials/auto-updates.md +++ b/docs/content/examples/tutorials/auto-updates.md @@ -20,7 +20,7 @@ File: `/etc/docker/containers/watchtower/docker-compose.yml` ```yaml services: watchtower: - image: containrrr/watchtower:latest + image: nickfedor/watchtower:latest volumes: - /var/run/docker.sock:/var/run/docker.sock env_file: diff --git a/docs/content/examples/tutorials/basic-installation.md b/docs/content/examples/tutorials/basic-installation.md index 8fc2ea17..0ef8cddb 100644 --- a/docs/content/examples/tutorials/basic-installation.md +++ b/docs/content/examples/tutorials/basic-installation.md @@ -8,7 +8,7 @@ title: Basic Installation 1. You need to have a host that you can manage 2. You need to have a domain name or a public IP address -3. You need a supported architecture (x86_64, arm64, armv7) +3. You need a supported architecture (x86_64, arm64) 4. You need curl installed on your host ## Install Docker diff --git a/docs/content/examples/tutorials/routed.md b/docs/content/examples/tutorials/routed.md index b72eb647..3ed4070d 100644 --- a/docs/content/examples/tutorials/routed.md +++ b/docs/content/examples/tutorials/routed.md @@ -93,3 +93,19 @@ PostDown ```shell iptables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; iptables -D FORWARD -i wg0 -j ACCEPT; iptables -D FORWARD -o wg0 -j ACCEPT; ip6tables -D INPUT -p udp -m udp --dport {{port}} -j ACCEPT; ip6tables -D FORWARD -i wg0 -j ACCEPT; ip6tables -D FORWARD -o wg0 -j ACCEPT ``` + +/// warning | Important: When using nftables use the following hooks instead. + +PostUp + +```shell +nft add chain ip filter WG_EASY; nft add rule ip filter DOCKER-USER jump WG_EASY; nft add rule ip filter WG_EASY iifname {{device}} accept; nft add rule ip filter WG_EASY oifname {{device}} accept; nft add chain ip6 filter WG_EASY; nft add rule ip6 filter DOCKER-USER jump WG_EASY; nft add rule ip6 filter WG_EASY iifname {{device}} accept; nft add rule ip6 filter WG_EASY oifname {{device}} accept; +``` + +PostDown + +```shell +nft delete rule ip filter DOCKER-USER handle $(nft -a list chain ip filter DOCKER-USER | awk '/jump WG_EASY/ {print $NF}'); nft flush chain ip filter WG_EASY; nft delete chain ip filter WG_EASY; nft delete rule ip6 filter DOCKER-USER handle $(nft -a list chain ip6 filter DOCKER-USER | awk '/jump WG_EASY/ {print $NF}'); nft flush chain ip6 filter WG_EASY; nft delete chain ip6 filter WG_EASY +``` + +/// diff --git a/docs/content/faq.md b/docs/content/faq.md index b2430530..8e2138cc 100644 --- a/docs/content/faq.md +++ b/docs/content/faq.md @@ -6,6 +6,20 @@ hide: Here are some frequently asked questions or errors about `wg-easy`. If you have a question that is not answered here, please feel free to open a discussion on GitHub. +## How do I restrict client access to specific networks or servers? + +Use the **Per-Client Firewall** feature to enforce server-side restrictions on what each client can access. + +**Requirements:** This feature requires `iptables` (and `ip6tables` for IPv6) to be installed on the host system. + +1. Enable "Per-Client Firewall" in **Admin Panel → Interface** +2. Edit a client and configure "Firewall Allowed IPs" +3. Specify which destinations the client should be allowed to access + +Unlike "Allowed IPs" which only controls client-side routing, firewall rules are enforced by the server and cannot be bypassed. + +See the [Admin Panel Guide](./guides/admin.md#per-client-firewall) and [Client Guide](./guides/clients.md#firewall-allowed-ips) for detailed configuration. + ## Error: WireGuard exited with the error: Cannot find device "wg0" This error indicates that the WireGuard interface `wg0` does not exist. This can happen if the WireGuard kernel module is not loaded or if the interface was not created properly. diff --git a/docs/content/getting-started.md b/docs/content/getting-started.md index c1d2a451..406fbf90 100644 --- a/docs/content/getting-started.md +++ b/docs/content/getting-started.md @@ -12,7 +12,7 @@ Before you can get started with deploying your own VPN, there are some requireme 1. You need to have a host that you can manage 2. You need to have a domain name or a public IP address -3. You need a supported architecture (x86_64, arm64, armv7) +3. You need a supported architecture (x86_64, arm64) ### Host Setup @@ -45,15 +45,15 @@ All workflows are using the tagging convention listed below. It is subsequently | tag | Type | Example | Description | | ------------- | ------------------------------- | ------------------------------------------------------------- | ----------------------------------------------------------------------------- | | `15` | latest minor for that major tag | `ghcr.io/wg-easy/wg-easy:15` | latest features for specific major versions, no breaking changes, recommended | -| `latest` | latest tag | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | points to latest release, can include breaking changes | | `15.0` | latest patch for that minor tag | `ghcr.io/wg-easy/wg-easy:15.0` | latest patches for specific minor version | | `15.0.0` | specific tag | `ghcr.io/wg-easy/wg-easy:15.0.0` | specific release, no updates | | `edge` | push to `master` | `ghcr.io/wg-easy/wg-easy:edge` | mostly unstable, gets frequent package and code updates | | `development` | pull requests | `ghcr.io/wg-easy/wg-easy:development` | used for development, testing code from PRs | +| `latest` | latest tag | `ghcr.io/wg-easy/wg-easy:latest` or `ghcr.io/wg-easy/wg-easy` | points to the v14 release, should be avoided | -When publishing a tag we follow the [Semantic Versioning][semver] specification. The `latest` tag is always pointing to the latest stable release. If you want to avoid breaking changes, use the major version tag (e.g. `15`). +When publishing a tag we follow the [Semantic Versioning][semver] specification. Pin to the latest major version to avoid breaking changes (e.g. `15`), avoid using the `latest` tag. [github-ci]: https://github.com/wg-easy/wg-easy/actions [ghcr-image]: https://github.com/wg-easy/wg-easy/pkgs/container/wg-easy diff --git a/docs/content/guides/admin.md b/docs/content/guides/admin.md index 7899d670..66c765b3 100644 --- a/docs/content/guides/admin.md +++ b/docs/content/guides/admin.md @@ -2,4 +2,42 @@ title: Admin Panel --- -TODO +## Interface Settings + +### Per-Client Firewall + +Enable server-side firewall filtering to enforce network access restrictions per client. + +When enabled, each client can have custom "Firewall Allowed IPs" configured that restrict which destinations they can access through the VPN. These restrictions are enforced by the server using iptables/ip6tables and cannot be bypassed by the client. + +/// warning | Experimental Feature + +This feature is currently experimental. While functional, it should be thoroughly tested in your environment before relying on it for production security requirements. Always verify that firewall rules are working as expected using test traffic or by manually inspecting the rules. + +/// + +**Requirements:** + +- `iptables` must be installed on the host system +- `ip6tables` must be installed if IPv6 is enabled (default) +- The feature cannot be enabled if these tools are not available + +/// note +Most Linux distributions include iptables by default. If you're running in a minimal container environment, you may need to install the `iptables` package on the host system. +/// + +**Enable this feature if you want to:** + +- Restrict certain clients to only access specific servers or networks +- Prevent clients from accessing the internet while allowing LAN access +- Enforce port-based restrictions (e.g., only allow HTTP/HTTPS) +- Separate routing configuration from security enforcement + +**How it works:** + +1. Enable "Per-Client Firewall" in Admin Panel → Interface +2. Edit any client to see the new "Firewall Allowed IPs" field +3. Specify allowed destinations (IPs, subnets, ports) for that client +4. Server enforces these rules automatically + +See [Edit Client → Firewall Allowed IPs](./clients.md#firewall-allowed-ips) for detailed configuration syntax and examples. diff --git a/docs/content/guides/cli.md b/docs/content/guides/cli.md index 5a4b5a07..f563e427 100644 --- a/docs/content/guides/cli.md +++ b/docs/content/guides/cli.md @@ -41,3 +41,31 @@ docker compose exec -it wg-easy cli db:admin:reset --password ``` This will reset the password for the admin user to the new password you provided. If you include special characters in the password, make sure to escape them properly. + +### Show Clients + +List all clients that are currently configured with details such as client ID, Name, Public Key, and enabled status. + +```shell +cli clients:list +``` + +### Show Client QR Code + +Display the QR code for a specific client, which can be scanned by a compatible app to import the client's configuration. + +```shell +cli clients:qr +``` + +Replace `` with the actual client ID you want to show the QR code for. + +/// warning | IPv6 Support + +IPv6 support is enabled by default, even if you disabled it using environment variables. To disable it pass the `--no-ipv6` flag when running the CLI. + +```shell +cli clients:qr --no-ipv6 +``` + +/// diff --git a/docs/content/guides/clients.md b/docs/content/guides/clients.md index 973be689..6314adac 100644 --- a/docs/content/guides/clients.md +++ b/docs/content/guides/clients.md @@ -19,7 +19,58 @@ Which IPs will be routed through the VPN. This will not prevent the user from modifying it locally and accessing IP ranges that they should not be able to access. -Use firewall rules to prevent access to IP ranges that the user should not be able to access. +Use the Firewall Allowed IPs feature to prevent access to IP ranges that the user should not be able to access. + +## Firewall Allowed IPs + +/// note | Attention + +This field only appears when **Per-Client Firewall** is enabled in the Admin Panel → Interface settings. + +/// + +Server-side firewall rules that restrict which destinations the client can access, regardless of their local configuration. + +Unlike "Allowed IPs" which only controls routing on the client side, these rules are enforced by the server using iptables/ip6tables and cannot be bypassed by the client. + +**Supported Formats:** + +- `10.10.0.3`, `2001:db8::1` - Allow access to a single IP address +- `10.10.0.0/24`, `2001:db8::/32` - Allow access to an entire subnet +- `192.168.1.5:443` - Allow access to specific port (TCP+UDP) +- `192.168.1.5:443/tcp` - Allow access to specific port (TCP only) +- `192.168.1.5:443/udp` - Allow access to specific port (UDP only) +- `10.10.0.0/24:443` - Allow access to an entire subnet on a specific port (TCP+UDP) +- `10.10.0.0/24:443/tcp` - Allow access to an entire subnet on a specific port (TCP only) +- `10.10.0.0/24:443/udp` - Allow access to an entire subnet on a specific port (UDP only) +- `[2001:db8::1]:443` - IPv6 address with port (brackets required) +- `[2001:db8::/32]:443/tcp` - IPv6 CIDR with port and protocol + +/// warning | Invalid Formats + +Protocol specifiers (`/tcp` or `/udp`) require a port number. The following formats are **not supported** and will result in an error: + +- `10.10.0.3/tcp` (use `10.10.0.3:443/tcp` instead) +- `10.10.0.0/24/udp` (use `10.10.0.0/24:53/udp` instead) + +/// + +**Behavior:** + +- **Empty**: Falls back to the client's "Allowed IPs" setting +- **Specified**: Only listed destinations are accessible (allow-only, everything else is blocked) +- **Disable for specific client**: To disable firewall filtering for a single client while keeping it enabled for others, add `0.0.0.0/0, ::/0` to allow all traffic + +/// note +To allow clients to reach the VPN server itself (e.g. for DNS), include the server's VPN address in the firewall allowed IPs. +/// + +**Use Case Examples**: + +- Allow only specific servers: `10.10.0.5` +- Allow only internal network: `10.10.0.0/24, 192.168.1.0/24` +- Allow only web browsing: `0.0.0.0/0:80, 0.0.0.0/0:443, [::/0]:80, [::/0]:443` +- Block internet, allow LAN: Leave "Allowed IPs" as `0.0.0.0/0, ::/0` but set Firewall IPs to `10.0.0.0/8, 192.168.0.0/16` ## Server Allowed IPs diff --git a/docs/content/index.md b/docs/content/index.md index 2e2c3235..e15fd9c0 100644 --- a/docs/content/index.md +++ b/docs/content/index.md @@ -8,7 +8,7 @@ hide: /// info | This Documentation is Versioned -**Make sure** to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to the `:latest` image tag - [the most recent stable release][docs-tagging]. +**Make sure** to select the correct version of this documentation! It should match the version of the image you are using. The default version corresponds to [the most recent stable release][docs-tagging]. /// This documentation provides you not only with the basic setup and configuration of `wg-easy` but also with advanced configuration, elaborate usage scenarios, detailed examples, hints and more. diff --git a/package.json b/package.json index 27831696..9ee964ae 100644 --- a/package.json +++ b/package.json @@ -7,10 +7,11 @@ "build": "docker build -t wg-easy .", "docs:preview": "docker run --rm -it -p 8080:8080 -v ./docs:/docs squidfunk/mkdocs-material serve -a 0.0.0.0:8080", "scripts:version": "bash scripts/version.sh", + "scripts:i18n": "bash scripts/i18n.sh", "format:check:docs": "prettier --check docs" }, "devDependencies": { - "prettier": "^3.6.2" + "prettier": "^3.8.1" }, - "packageManager": "pnpm@10.21.0" + "packageManager": "pnpm@10.31.0" } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index af0feb48..300c186a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -9,16 +9,16 @@ importers: .: devDependencies: prettier: - specifier: ^3.6.2 - version: 3.6.2 + specifier: ^3.8.1 + version: 3.8.1 packages: - prettier@3.6.2: - resolution: {integrity: sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==} + prettier@3.8.1: + resolution: {integrity: sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==} engines: {node: '>=14'} hasBin: true snapshots: - prettier@3.6.2: {} + prettier@3.8.1: {} diff --git a/scripts/version.sh b/scripts/version.sh index ba6ee106..6d38b2e1 100644 --- a/scripts/version.sh +++ b/scripts/version.sh @@ -30,6 +30,7 @@ echo "Updated package.json to version $new_version" echo "----" echo "If you changed the major version, remember to update the docker-compose.yml file and docs (search for: ref: major version)" +echo "Make sure to stage any changes before proceeding (e.g. Changelog updates)." echo "----" echo "If you did everything press 'y' to commit the changes and create a new tag" diff --git a/src/.gitignore b/src/.gitignore index 2c23449e..c1b25baa 100644 --- a/src/.gitignore +++ b/src/.gitignore @@ -23,4 +23,6 @@ logs .env.* !.env.example +coverage/ + wg-easy.db diff --git a/src/.nuxtrc b/src/.nuxtrc new file mode 100644 index 00000000..1e1fe833 --- /dev/null +++ b/src/.nuxtrc @@ -0,0 +1 @@ +setups.@nuxt/test-utils="4.0.0" \ No newline at end of file diff --git a/src/app/app.css b/src/app/app.css new file mode 100644 index 00000000..afa0ee69 --- /dev/null +++ b/src/app/app.css @@ -0,0 +1,7 @@ +:root { + color-scheme: light; +} + +.dark { + color-scheme: dark; +} diff --git a/src/app/components/Base/TextArea.vue b/src/app/components/Base/TextArea.vue new file mode 100644 index 00000000..8a44ac84 --- /dev/null +++ b/src/app/components/Base/TextArea.vue @@ -0,0 +1,10 @@ +