├── .mdlrc ├── .gitignore ├── .github ├── autolabeler.yml ├── FUNDING.yml ├── CODEOWNERS ├── PULL_REQUEST_TEMPLATE.md ├── invite-contributors.yml ├── ISSUE_TEMPLATE.md ├── potential-duplicates.yml ├── move.yml ├── support.yml ├── lock.yml ├── workflows │ ├── test_publish.yml │ └── publish.yml ├── no-response.yml ├── stale.yml ├── config.yml └── settings.yml ├── wireguard_client ├── icon.png ├── logo.png ├── build.yaml ├── rootfs │ └── etc │ │ ├── services.d │ │ ├── status │ │ │ └── run │ │ ├── wireguard_client │ │ │ └── run │ │ └── api │ │ │ └── run │ │ ├── cont-finish.d │ │ └── 00-stop │ │ └── cont-init.d │ │ └── config.sh ├── config.yaml ├── Dockerfile ├── .README.j2 ├── DOCS.md ├── CHANGELOG.md └── API.md ├── .editorconfig ├── .devcontainer └── devcontainer.json ├── LICENSE.md ├── CONTRIBUTING.md ├── .yamllint ├── devcontainer_bootstrap ├── .vscode └── tasks.json ├── CODE_OF_CONDUCT.md └── README.md /.mdlrc: -------------------------------------------------------------------------------- 1 | rules "~MD024" -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | wireguard-server/** 3 | -------------------------------------------------------------------------------- /.github/autolabeler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | "Type: Documentation": ["*.md", "*.j2"] 3 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | --- 2 | github: bigmoby 3 | custom: buymeacoff.ee/bigmoby 4 | -------------------------------------------------------------------------------- /wireguard_client/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigmoby/addon-wireguard-client/HEAD/wireguard_client/icon.png -------------------------------------------------------------------------------- /wireguard_client/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bigmoby/addon-wireguard-client/HEAD/wireguard_client/logo.png -------------------------------------------------------------------------------- /wireguard_client/build.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | build_from: 3 | aarch64: ghcr.io/hassio-addons/base:18.1.4 4 | amd64: ghcr.io/hassio-addons/base:18.1.4 5 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # Require maintainer's :+1: for changes to the .github/ repo-config files 2 | # mainly due to https://github.com/probot/settings privilege escalation 3 | .github/* @frenck 4 | .gitlab-ci.yml @frenck 5 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Proposed Changes 2 | 3 | > (Describe the changes and rationale behind them) 4 | 5 | ## Related Issues 6 | 7 | > ([Github link][autolink-references] to related issues or pull requests) 8 | 9 | [autolink-references]: https://help.github.com/articles/autolinked-references-and-urls/ -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | end_of_line = lf 6 | indent_style = space 7 | insert_final_newline = true 8 | trim_trailing_whitespace = true 9 | ident_size = 4 10 | 11 | [*.md] 12 | ident_size = 2 13 | trim_trailing_whitespace = false 14 | 15 | [*.json] 16 | ident_size = 2 17 | 18 | [{.gitignore,.gitkeep,.editorconfig}] 19 | ident_size = 2 20 | -------------------------------------------------------------------------------- /.github/invite-contributors.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # If true, this will add new contributors as outside collaborators 3 | # to the repo their PR was merged in. Team name is ignored if this 4 | # flag is set to true. 5 | isOutside: false 6 | 7 | # Specify team name to add new contributors to a specific team 8 | # within your organization. 9 | # Use team name or team-name-slug 10 | team: Contributors 11 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | # Problem/Motivation 2 | 3 | > (Why the issue was filed) 4 | 5 | ## Expected behavior 6 | 7 | > (What you expected to happen) 8 | 9 | ## Actual behavior 10 | 11 | > (What actually happened) 12 | 13 | ## Steps to reproduce 14 | 15 | > (How can someone else make/see it happen) 16 | 17 | ## Proposed changes 18 | 19 | > (If you have a proposed change, workaround or fix, 20 | > describe the rationale behind it) 21 | -------------------------------------------------------------------------------- /.github/potential-duplicates.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Label name and color to set, when potential duplicates are detected 3 | issueLabel: "Potential duplicate" 4 | labelColor: e6e6e6 5 | 6 | # If similarity is higher than this threshold, issue will be marked as duplicate 7 | threshold: 0.70 8 | 9 | # Comment to post when potential duplicates are detected 10 | referenceComment: > 11 | Potential duplicates found: 12 | {{#issues}} 13 | - [#{{ number }}] {{ title }} ({{ accuracy }}%) 14 | {{/issues}} 15 | -------------------------------------------------------------------------------- /.github/move.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Delete the command comment when it contains no other content 3 | deleteCommand: true 4 | 5 | # Close the source issue after moving 6 | closeSourceIssue: true 7 | 8 | # Lock the source issue after moving 9 | lockSourceIssue: true 10 | 11 | # Mention issue and comment authors 12 | mentionAuthors: true 13 | 14 | # Preserve mentions in the issue content 15 | keepContentMentions: false 16 | 17 | # Set custom aliases for targets 18 | # aliases: 19 | # r: repo 20 | # or: owner/repo 21 | -------------------------------------------------------------------------------- /wireguard_client/rootfs/etc/services.d/status/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bashio 2 | # ============================================================================== 3 | # Home Assistant Community Add-on: WireGuard Client 4 | # Shows current WireGuard status periodically 5 | # ============================================================================== 6 | sleep 30 7 | bashio::log.info "Requesting current status from WireGuard Client..." 8 | if [[ "${__BASHIO_LOG_LEVEL}" -ge "${__BASHIO_LOG_LEVEL_INFO}" ]]; then 9 | exec wg show 10 | fi 11 | -------------------------------------------------------------------------------- /.github/support.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Configuration for support-requests - https://github.com/dessant/support-requests 3 | 4 | # Label used to mark issues as support requests 5 | supportLabel: "Type: Support" 6 | 7 | # Comment to post on issues marked as support requests. Add a link 8 | # to a support page, or set to `false` to disable 9 | supportComment: > 10 | :wave: We use the issue tracker exclusively for bug reports and feature requests. 11 | However, this issue appears to be a support request. Please use our 12 | support channels to get help with the project. 13 | 14 | # Close issues marked as support requests 15 | close: true 16 | 17 | # Lock issues marked as support requests 18 | lock: false 19 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Configuration for lock-threads - https://github.com/dessant/lock-threads 3 | # Number of days of inactivity before a closed issue or pull request is locked 4 | daysUntilLock: 30 5 | 6 | # Comment to post before locking. Set to `false` to disable 7 | lockComment: > 8 | This thread has been automatically locked because it has not had recent 9 | activity. Please open a new issue for related bugs and link to relevant 10 | comments in this thread. 11 | 12 | # Issues or pull requests with these labels will not be locked 13 | # exemptLabels: 14 | # - no-locking 15 | 16 | # Limit to only `issues` or `pulls` 17 | # only: issues 18 | 19 | # Add a label when locking. Set to `false` to disable 20 | lockLabel: false 21 | -------------------------------------------------------------------------------- /.github/workflows/test_publish.yml: -------------------------------------------------------------------------------- 1 | name: "Test" 2 | 3 | on: [workflow_dispatch] 4 | 5 | jobs: 6 | build: 7 | name: Test build 8 | runs-on: ubuntu-latest 9 | steps: 10 | - name: Checkout the repository 11 | uses: actions/checkout@v4 12 | - name: Test build 13 | uses: home-assistant/builder@2024.08.2 14 | with: 15 | args: | 16 | --test \ 17 | --repository https://github.com/bigmoby/addon-wireguard-client \ 18 | --target wireguard_client \ 19 | --branch main \ 20 | --all \ 21 | --docker-hub bigmoby \ 22 | --docker-user ${{ secrets.DOCKERHUB_USERNAME }} \ 23 | --docker-password ${{ secrets.DOCKERHUB_TOKEN }} 24 | -------------------------------------------------------------------------------- /.github/no-response.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Configuration for probot-no-response - https://github.com/probot/no-response 3 | # Number of days of inactivity before an Issue is closed for lack of response 4 | daysUntilClose: 14 5 | # Label requiring a response 6 | responseRequiredLabel: "Status: Awaiting response" 7 | # Comment to post when closing an Issue for lack of response. Set to `false` to disable 8 | closeComment: > 9 | This issue has been automatically closed because there has been no response 10 | to our request for more information from the original author. With only the 11 | information that is currently in the issue, we don't have enough information 12 | to take action. Please reach out if you have or find the answers we need so 13 | that we can investigate further. 14 | -------------------------------------------------------------------------------- /.github/workflows/publish.yml: -------------------------------------------------------------------------------- 1 | name: "Publish" 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | publish: 8 | name: Publish 9 | runs-on: ubuntu-latest 10 | steps: 11 | - name: Checkout the repository 12 | uses: actions/checkout@v4 13 | - name: Login to DockerHub 14 | uses: docker/login-action@v3.2.0 15 | with: 16 | username: ${{ secrets.DOCKERHUB_USERNAME }} 17 | password: ${{ secrets.DOCKERHUB_TOKEN }} 18 | - name: Home Assistant builder 19 | uses: home-assistant/builder@2024.08.2 20 | with: 21 | args: | 22 | --repository https://github.com/bigmoby/addon-wireguard-client \ 23 | --target wireguard_client \ 24 | --branch main \ 25 | --all \ 26 | --docker-hub bigmoby \ 27 | --docker-user ${{ secrets.DOCKERHUB_USERNAME }} \ 28 | --docker-password ${{ secrets.DOCKERHUB_TOKEN }} 29 | -------------------------------------------------------------------------------- /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name":"Example devcontainer for add-on repositories", 3 | "image":"ghcr.io/home-assistant/devcontainer:addons", 4 | "appPort":[ 5 | "8123:8123", 6 | "4357:4357" 7 | ], 8 | "postStartCommand":"bash devcontainer_bootstrap", 9 | "runArgs":[ 10 | "-e", 11 | "GIT_EDITOR=code --wait", 12 | "--privileged", 13 | "--mount", 14 | "type=tmpfs,destination=/mnt/supervisor/share" 15 | ], 16 | "containerEnv":{ 17 | "WORKSPACE_DIRECTORY":"${containerWorkspaceFolder}" 18 | }, 19 | "customizations":{ 20 | "vscode":{ 21 | "extensions":[ 22 | "timonwong.shellcheck", 23 | "esbenp.prettier-vscode" 24 | ], 25 | "settings":{ 26 | "terminal.integrated.profiles.linux":{ 27 | "zsh":{ 28 | "path":"/usr/bin/zsh" 29 | } 30 | }, 31 | "terminal.integrated.defaultProfile.linux":"zsh", 32 | "editor.formatOnPaste":false, 33 | "editor.formatOnSave":true, 34 | "editor.formatOnType":true, 35 | "files.trimTrailingWhitespace":true 36 | } 37 | } 38 | }, 39 | "mounts":[ 40 | "type=volume,target=/var/lib/docker" 41 | ] 42 | } -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # MIT License 2 | 3 | Copyright (c) 2020-2025 Fabio Mauro 4 | 5 | Copyright (c) 2019-2020 Franck Nijhof 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | When contributing to this repository, please first discuss the change you wish 4 | to make via issue, email, or any other method with the owners of this repository 5 | before making a change. 6 | 7 | Please note we have a code of conduct, please follow it in all your interactions 8 | with the project. 9 | 10 | ## Issues and feature requests 11 | 12 | You've found a bug in the source code, a mistake in the documentation or maybe 13 | you'd like a new feature? You can help us by submitting an issue to our 14 | [GitHub Repository][github]. Before you create an issue, make sure you search 15 | the archive, maybe your question was already answered. 16 | 17 | Even better: You could submit a pull request with a fix / new feature! 18 | 19 | ## Pull request process 20 | 21 | 1. Search our repository for open or closed [pull requests][prs] that relates 22 | to your submission. You don't want to duplicate effort. 23 | 24 | 1. You may merge the pull request in once you have the sign-off of two other 25 | developers, or if you do not have permission to do that, you may request 26 | the second reviewer to merge it for you. 27 | 28 | [github]: https://github.com/bigmoby/addon-wireguard/issues 29 | [prs]: https://github.com/bigmoby/addon-wireguard/pulls 30 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | rules: 3 | braces: 4 | level: error 5 | min-spaces-inside: 0 6 | max-spaces-inside: 1 7 | min-spaces-inside-empty: -1 8 | max-spaces-inside-empty: -1 9 | brackets: 10 | level: error 11 | min-spaces-inside: 0 12 | max-spaces-inside: 0 13 | min-spaces-inside-empty: -1 14 | max-spaces-inside-empty: -1 15 | colons: 16 | level: error 17 | max-spaces-before: 0 18 | max-spaces-after: 1 19 | commas: 20 | level: error 21 | max-spaces-before: 0 22 | min-spaces-after: 1 23 | max-spaces-after: 1 24 | comments: 25 | level: error 26 | require-starting-space: true 27 | min-spaces-from-content: 2 28 | comments-indentation: 29 | level: error 30 | document-end: 31 | level: error 32 | present: false 33 | document-start: 34 | level: error 35 | present: true 36 | empty-lines: 37 | level: error 38 | max: 1 39 | max-start: 0 40 | max-end: 1 41 | hyphens: 42 | level: error 43 | max-spaces-after: 1 44 | indentation: 45 | level: error 46 | spaces: 2 47 | indent-sequences: true 48 | check-multi-line-strings: false 49 | key-duplicates: 50 | level: error 51 | line-length: 52 | ignore: | 53 | .github/support.yml 54 | level: warning 55 | max: 120 56 | allow-non-breakable-words: true 57 | allow-non-breakable-inline-mappings: true 58 | new-line-at-end-of-file: 59 | level: error 60 | new-lines: 61 | level: error 62 | type: unix 63 | trailing-spaces: 64 | level: error 65 | truthy: 66 | level: error 67 | -------------------------------------------------------------------------------- /wireguard_client/config.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | name: WireGuard Client 3 | version: 0.2.8 4 | slug: wireguard_client 5 | description: Fast, modern, secure Wireguard tunnel (client) 6 | url: https://github.com/bigmoby/addon-wireguard-client 7 | arch: 8 | - aarch64 9 | - amd64 10 | apparmor: true 11 | host_network: true 12 | init: false 13 | ports: 14 | 51821/tcp: 51821 15 | ports_description: 16 | 51821/tcp: WireGuard client unified API (sensors and services) 17 | hassio_api: true 18 | privileged: 19 | - NET_ADMIN 20 | devices: 21 | - /dev/net/tun 22 | map: 23 | - ssl:rw 24 | options: 25 | interface: 26 | private_key: "" 27 | address: 10.6.0.2 28 | dns: [8.8.8.8, 8.8.4.4] 29 | post_up: "iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE; iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu" 30 | post_down: "iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE; iptables -D FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu" 31 | mtu: 1420 32 | peers: 33 | - public_key: "" 34 | pre_shared_key: "" 35 | endpoint: "xxxxxx.duckdns.com:51820" 36 | allowed_ips: ["10.6.0.0/24"] 37 | persistent_keep_alive: "25" 38 | schema: 39 | log_level: list(trace|debug|info|notice|warning|error|fatal)? 40 | interface: 41 | private_key: password? 42 | address: str 43 | dns: 44 | - str? 45 | post_up: str? 46 | post_down: str? 47 | mtu: int 48 | peers: 49 | - public_key: str? 50 | pre_shared_key: str? 51 | endpoint: str 52 | allowed_ips: 53 | - str 54 | persistent_keep_alive: int 55 | image: "bigmoby/{arch}-addon-wireguard-client" 56 | -------------------------------------------------------------------------------- /devcontainer_bootstrap: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Bootstrap script for Home Assistant devcontainer 3 | # Fixes Docker mount propagation issues 4 | 5 | set -e 6 | 7 | echo "🔧 Setting up Docker mount propagation..." 8 | 9 | # Create supervisor directories 10 | mkdir -p /mnt/supervisor/{share,addon_configs} 11 | echo "✓ Supervisor directories created" 12 | 13 | # Check if /mnt/supervisor/share is already a mount point 14 | if mountpoint -q /mnt/supervisor/share 2>/dev/null; then 15 | echo "✓ /mnt/supervisor/share is already mounted" 16 | # Try to make it shared if it's not already 17 | mount --make-shared /mnt/supervisor/share 2>/dev/null || \ 18 | mount --make-rshared /mnt/supervisor/share 2>/dev/null || \ 19 | echo "⚠ Could not make /mnt/supervisor/share shared" 20 | else 21 | # Try to mount tmpfs if not already mounted 22 | if mount -t tmpfs -o size=100M tmpfs /mnt/supervisor/share 2>/dev/null; then 23 | echo "✓ tmpfs montato su /mnt/supervisor/share" 24 | else 25 | echo "ℹ /mnt/supervisor/share non montato (verrà creato automaticamente)" 26 | fi 27 | fi 28 | 29 | # Try to make root mount shared (required for Docker-in-Docker) 30 | # This might fail if we don't have sufficient privileges 31 | if mount --make-shared / 2>/dev/null || mount --make-rshared / 2>/dev/null; then 32 | echo "✓ Root mount configured as shared" 33 | else 34 | # Check if it's already shared 35 | if grep -q "shared:" /proc/self/mountinfo | grep " / "; then 36 | echo "✓ Root mount is already shared" 37 | else 38 | echo "⚠ Could not configure root mount as shared" 39 | echo " This may cause issues with Docker-in-Docker" 40 | fi 41 | fi 42 | 43 | echo "" 44 | echo "✅ Bootstrap complete" 45 | 46 | -------------------------------------------------------------------------------- /wireguard_client/Dockerfile: -------------------------------------------------------------------------------- 1 | ARG BUILD_FROM=ghcr.io/hassio-addons/base:18.1.4 2 | # hadolint ignore=DL3006 3 | FROM ${BUILD_FROM} 4 | 5 | # Set shell 6 | SHELL ["/bin/bash", "-o", "pipefail", "-c"] 7 | 8 | # Setup base 9 | # hadolint ignore=DL3003 10 | RUN \ 11 | apk add --no-cache \ 12 | openresolv=3.14.1-r0 \ 13 | iptables=1.8.11-r1 \ 14 | wireguard-tools=1.0.20250521-r0 \ 15 | socat=1.8.0.3-r1 \ 16 | \ 17 | && ln -sf /sbin/xtables-nft-multi /sbin/ip6tables \ 18 | && ln -sf /sbin/xtables-nft-multi /sbin/iptables 19 | 20 | # Copy root filesystem 21 | COPY rootfs / 22 | 23 | # Build arguments 24 | ARG BUILD_ARCH 25 | ARG BUILD_DATE 26 | ARG BUILD_REF 27 | ARG BUILD_VERSION 28 | 29 | # Labels 30 | LABEL \ 31 | io.hass.name="WireGuard Client" \ 32 | io.hass.description="Fast, modern, secure VPN tunnel (Client)" \ 33 | io.hass.arch="${BUILD_ARCH}" \ 34 | io.hass.type="addon" \ 35 | io.hass.version=${BUILD_VERSION} \ 36 | maintainer="Fabio Bigmoby Mauro " \ 37 | org.opencontainers.image.title="WireGuard Client" \ 38 | org.opencontainers.image.description="Fast, modern, secure VPN tunnel" \ 39 | org.opencontainers.image.vendor="Home Assistant Community Add-ons" \ 40 | org.opencontainers.image.authors="Fabio Bigmoby Mauro " \ 41 | org.opencontainers.image.licenses="MIT" \ 42 | org.opencontainers.image.url="https://addons.community" \ 43 | org.opencontainers.image.source="https://github.com/bigmoby/addon-wireguard-client" \ 44 | org.opencontainers.image.documentation="https://github.com/bigmoby/addon-wireguard-client/blob/master/README.md" \ 45 | org.opencontainers.image.created=${BUILD_DATE} \ 46 | org.opencontainers.image.revision=${BUILD_REF} \ 47 | org.opencontainers.image.version=${BUILD_VERSION} 48 | -------------------------------------------------------------------------------- /wireguard_client/rootfs/etc/cont-finish.d/00-stop: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bashio 2 | # ============================================================================== 3 | # Home Assistant Community Add-on: WireGuard Client 4 | # Stops WireGuard Client and cleans up interface 5 | # ============================================================================== 6 | declare interface 7 | 8 | bashio::log.info "Stopping WireGuard Client container..." 9 | 10 | # Get the interface 11 | interface="wg0" 12 | 13 | # Check if interface exists before trying to stop it 14 | if ip link show "${interface}" >/dev/null 2>&1; then 15 | bashio::log.info "Interface ${interface} found, stopping WireGuard..." 16 | 17 | # Try to stop with wg-quick first 18 | if wg-quick down "${interface}" 2>/dev/null; then 19 | bashio::log.info "WireGuard stopped successfully with wg-quick" 20 | else 21 | bashio::log.warning "wg-quick down failed, performing manual cleanup..." 22 | 23 | # Manual cleanup if wg-quick fails 24 | ip route del dev "${interface}" 2>/dev/null || true 25 | ip addr flush dev "${interface}" 2>/dev/null || true 26 | ip link set "${interface}" down 2>/dev/null || true 27 | ip link delete "${interface}" 2>/dev/null || true 28 | 29 | bashio::log.info "Manual cleanup completed" 30 | fi 31 | 32 | # Verify interface is removed 33 | if ip link show "${interface}" >/dev/null 2>&1; then 34 | bashio::log.warning "Interface ${interface} still exists, forcing removal..." 35 | ip link delete "${interface}" 2>/dev/null || true 36 | else 37 | bashio::log.info "Interface ${interface} successfully removed" 38 | fi 39 | else 40 | bashio::log.info "Interface ${interface} not found, nothing to stop" 41 | fi 42 | 43 | # Clean up any remaining WireGuard processes 44 | pkill -f "wg-quick" 2>/dev/null || true 45 | 46 | bashio::log.info "WireGuard Client stop completed" -------------------------------------------------------------------------------- /wireguard_client/rootfs/etc/services.d/wireguard_client/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bashio 2 | # ============================================================================== 3 | # Home Assistant Community Add-on: WireGuard Client 4 | # Runs WireGuard Client 5 | # ============================================================================== 6 | declare interface 7 | 8 | s6-svc -O /run/service/wireguard_client 9 | 10 | bashio::log.info "Starting WireGuard Client..." 11 | 12 | # This is alpha software. We need to set this to instruct 13 | # WireGuard we are OK to go. 14 | export WG_I_PREFER_BUGGY_USERSPACE_TO_POLISHED_KMOD=1 15 | 16 | # Get the interface 17 | interface="wg0" 18 | 19 | # Check if interface already exists and clean it up 20 | if ip link show "${interface}" >/dev/null 2>&1; then 21 | bashio::log.warning "Interface ${interface} already exists, cleaning up..." 22 | 23 | # Try to stop existing interface 24 | if wg-quick down "${interface}" 2>/dev/null; then 25 | bashio::log.info "Existing interface stopped successfully" 26 | else 27 | bashio::log.warning "Failed to stop existing interface, performing manual cleanup..." 28 | 29 | # Manual cleanup 30 | ip route del dev "${interface}" 2>/dev/null || true 31 | ip addr flush dev "${interface}" 2>/dev/null || true 32 | ip link set "${interface}" down 2>/dev/null || true 33 | ip link delete "${interface}" 2>/dev/null || true 34 | fi 35 | 36 | # Wait a moment for cleanup to complete 37 | sleep 2 38 | 39 | # Verify interface is removed 40 | if ip link show "${interface}" >/dev/null 2>&1; then 41 | bashio::log.error "Failed to remove existing interface ${interface}" 42 | bashio::exit.nok "Cannot start WireGuard: interface ${interface} already exists" 43 | else 44 | bashio::log.info "Interface ${interface} cleanup completed" 45 | fi 46 | fi 47 | 48 | # Run the WireGuard 49 | bashio::log.info "Starting WireGuard interface ${interface}..." 50 | exec wg-quick up "${interface}" 51 | -------------------------------------------------------------------------------- /.github/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Configuration for probot-stale - https://github.com/probot/stale 3 | 4 | # Number of days of inactivity before an Issue or Pull Request becomes stale 5 | daysUntilStale: 60 6 | 7 | # Number of days of inactivity before a stale Issue or Pull Request is closed. 8 | # Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. 9 | daysUntilClose: 7 10 | 11 | # Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable 12 | exemptLabels: 13 | - "Status: On hold" 14 | - "Status: In progress" 15 | - "Status: Awaiting response" 16 | - "Status: Blocked" 17 | - "Idea" 18 | - "Security" 19 | 20 | # Set to true to ignore issues in a project (defaults to false) 21 | exemptProjects: false 22 | 23 | # Set to true to ignore issues in a milestone (defaults to false) 24 | exemptMilestones: false 25 | 26 | # Label to use when marking as stale 27 | staleLabel: "Status: Stale" 28 | 29 | # Comment to post when marking as stale. Set to `false` to disable 30 | markComment: > 31 | This issue has been automatically marked as stale because it has not had 32 | recent activity. It will be closed if no further activity occurs. Thank you 33 | for your contributions. 34 | 35 | # Comment to post when removing the stale label. 36 | # unmarkComment: > 37 | # Your comment here. 38 | unmarkComment: false 39 | 40 | # Comment to post when closing a stale Issue or Pull Request. 41 | # closeComment: > 42 | # Your comment here. 43 | closeComment: false 44 | 45 | # Limit the number of actions per hour, from 1-30. Default is 30 46 | limitPerRun: 30 47 | 48 | # Limit to only `issues` or `pulls` 49 | only: issues 50 | 51 | # Optionally, specify configuration settings that are specific to just 'issues' or 'pulls': 52 | # pulls: 53 | # daysUntilStale: 30 54 | # markComment: > 55 | # This pull request has been automatically marked as stale because it has not had 56 | # recent activity. It will be closed if no further activity occurs. Thank you 57 | # for your contributions. 58 | 59 | # issues: 60 | # exemptLabels: 61 | # - confirmed 62 | -------------------------------------------------------------------------------- /.github/config.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Configuration for request-info - https://github.com/behaviorbot/request-info 3 | 4 | # *OPTIONAL* Comment to reply with 5 | # Can be either a string : 6 | requestInfoReplyComment: 7 | - "We would appreciate it if you could provide us with more info about this issue/pr!" 8 | - "Hmmm... That issue/PR is kinda low on text. Could you please provide some more content?" 9 | 10 | # *OPTIONAL* default titles to check against for lack of descriptiveness 11 | # MUST BE ALL LOWERCASE 12 | requestInfoDefaultTitles: [] 13 | 14 | # *OPTIONAL* Label to be added to Issues and Pull Requests with insufficient information given 15 | requestInfoLabelToAdd: "Incomplete" 16 | 17 | # *OPTIONAL* Require Pull Requests to contain more information than what is provided in the PR template 18 | # Will fail if the pull request's body is equal to the provided template 19 | checkPullRequestTemplate: true 20 | 21 | # *OPTIONAL* Only warn about insufficient information on these events type 22 | # Keys must be lowercase. Valid values are 'issue' and 'pullRequest' 23 | requestInfoOn: 24 | pullRequest: true 25 | issue: true 26 | 27 | # *OPTIONAL* Add a list of people whose Issues/PRs will not be commented on 28 | # keys must be GitHub usernames 29 | requestInfoUserstoExclude: [] 30 | 31 | # Configuration for new-issue-welcome - https://github.com/behaviorbot/new-issue-welcome 32 | 33 | # Comment to be posted to on first time issues 34 | newIssueWelcomeComment: > 35 | :wave: Thanks for opening your first issue here! 36 | If you're reporting a :bug: bug, please make sure you include steps to reproduce it. 37 | Also, logs, error messages and information about your hardware might be useful. 38 | 39 | # Configuration for new-pr-welcome - https://github.com/behaviorbot/new-pr-welcome 40 | 41 | # Comment to be posted to on PRs from first time contributors in your repository 42 | newPRWelcomeComment: > 43 | :sparkling_heart: Thanks for opening this pull request! :sparkling_heart: 44 | If your PR gets accepted and merged in, we will invite you to the project :tada: 45 | 46 | # Configuration for first-pr-merge - https://github.com/behaviorbot/first-pr-merge 47 | 48 | # Comment to be posted to on pull requests merged by a first time user 49 | firstPRMergeComment: > 50 | Congrats on merging your first pull request! :tada::tada::tada: 51 | -------------------------------------------------------------------------------- /wireguard_client/.README.j2: -------------------------------------------------------------------------------- 1 | # Home Assistant Community Add-on: WireGuard Client 2 | 3 | WireGuard: fast, modern, secure VPN tunnel. 4 | 5 | ## Sponsor 6 | 7 | Please, if You want support this kind of projects: 8 | 9 | Buy Me A Coffee 10 | 11 | Many Thanks, 12 | 13 | Fabio Mauro 14 | 15 | ## Authors & contributors 16 | 17 | Fabio Mauro Bigmoby 18 | 19 | Project forked from [Wireguard add-on][original_project]. 20 | 21 | For a full list of all authors and contributors, 22 | check [the contributor's page][contributors]. 23 | 24 | ## License 25 | 26 | MIT License 27 | 28 | Copyright (c) 2020-2025 Fabio Mauro 29 | 30 | Copyright (c) 2019-2020 Franck Nijhof 31 | 32 | Permission is hereby granted, free of charge, to any person obtaining a copy 33 | of this software and associated documentation files (the "Software"), to deal 34 | in the Software without restriction, including without limitation the rights 35 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 36 | copies of the Software, and to permit persons to whom the Software is 37 | furnished to do so, subject to the following conditions: 38 | 39 | The above copyright notice and this permission notice shall be included in all 40 | copies or substantial portions of the Software. 41 | 42 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 43 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 44 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 45 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 46 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 47 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 48 | SOFTWARE. 49 | 50 | 51 | [original_project]: https://github.com/hassio-addons/addon-wireguard 52 | [contributors]: https://github.com/bigmoby/addon-wireguard-client/graphs/contributors 53 | [aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg 54 | [amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg 55 | [docs]: https://github.com/bigmoby/addon-wireguard-client/blob/master/wireguard/DOCS.md 56 | [license-shield]: https://img.shields.io/github/license/bigmoby/addon-wireguard-client.svg 57 | [maintenance-shield]: https://img.shields.io/maintenance/yes/2022.svg 58 | [project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg 59 | [reddit]: https://reddit.com/r/homeassistant 60 | [wireguard]: https://www.wireguard.com 61 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | "version":"2.0.0", 3 | "tasks":[ 4 | { 5 | "label":"Fix Docker Mounts", 6 | "type":"shell", 7 | "command":"bash -c 'echo \"🔧 Configurazione mount Docker...\" && echo \"\" && mkdir -p /mnt/supervisor/share && if ! mountpoint -q /mnt/supervisor/share 2>/dev/null; then sudo mount -t tmpfs -o size=100M tmpfs /mnt/supervisor/share 2>/dev/null && echo \"✓ tmpfs montato su /mnt/supervisor/share\" || echo \"⚠ Impossibile montare tmpfs (potrebbe richiedere riavvio devcontainer)\"; else echo \"✓ /mnt/supervisor/share è già montato\"; fi && echo \"\" && (sudo mount --make-shared /mnt/supervisor/share 2>/dev/null && echo \"✓ Mount configurato come shared\") || echo \"ℹ Mount propagation potrebbe non essere configurabile\" && echo \"\" && echo \"💡 Se Home Assistant non si avvia ancora, riavvia il devcontainer\"'", 8 | "group":"none", 9 | "presentation":{ 10 | "reveal":"always", 11 | "panel":"new", 12 | "focus":true 13 | } 14 | }, 15 | { 16 | "label":"Start Home Assistant", 17 | "type":"shell", 18 | "command":"bash -c 'echo \"Avvio Home Assistant...\" && supervisor_run'", 19 | "group":{ 20 | "kind":"test", 21 | "isDefault":true 22 | }, 23 | "presentation":{ 24 | "reveal":"always", 25 | "panel":"new", 26 | "focus":true, 27 | "clear":false 28 | }, 29 | "isBackground":true, 30 | "problemMatcher":[ 31 | { 32 | "pattern":{ 33 | "regexp": ".*", 34 | "file":1 35 | }, 36 | "background":{ 37 | "activeOnStart":true, 38 | "beginsPattern":".*Starting.*|.*Waiting.*", 39 | "endsPattern":".*Started.*|.*ready.*" 40 | } 41 | } 42 | ] 43 | }, 44 | { 45 | "label":"Stop Home Assistant", 46 | "type":"shell", 47 | "command":"bash -c 'echo \"Arresto Home Assistant...\" && docker stop hassio_supervisor 2>/dev/null || echo \"Container già fermo o non trovato\"'", 48 | "group":"none", 49 | "presentation":{ 50 | "reveal":"always", 51 | "panel":"new" 52 | } 53 | }, 54 | { 55 | "label":"Check Home Assistant Status", 56 | "type":"shell", 57 | "command":"bash -c 'echo \"Verifica stato Home Assistant...\" && docker ps --filter \"name=hassio\" --format \"{{.Names}}: {{.Status}}\" && echo \"\" && curl -s http://localhost:8123/api/ | head -3 || echo \"Home Assistant non risponde sulla porta 8123\"'", 58 | "group":"none", 59 | "presentation":{ 60 | "reveal":"always", 61 | "panel":"new" 62 | } 63 | } 64 | ] 65 | } -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Code of conduct 2 | 3 | ## Our pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | - Using welcoming and inclusive language 18 | - Being respectful of differing viewpoints and experiences 19 | - Gracefully accepting constructive criticism 20 | - Focusing on what is best for the community 21 | - Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | - The use of sexualized language or imagery and unwelcome sexual attention 26 | or advances 27 | - Trolling, insulting/derogatory comments, and personal or political attacks 28 | - Public or private harassment 29 | - Publishing others' private information, such as a physical or 30 | electronic address, without explicit permission 31 | - Other conduct which could reasonably be considered inappropriate 32 | in a professional setting 33 | 34 | ## Our responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project lead at frenck@addons.community. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project lead is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 71 | version 1.4, available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /.github/settings.yml: -------------------------------------------------------------------------------- 1 | --- 2 | repository: 3 | description: "WireGuard Client - Home Assistant Third Party Add-ons" 4 | homepage: https://addons.community 5 | topics: wireguard, vpn, networking, addon, addons, home-assistant, homeassistant 6 | private: false 7 | has_issues: true 8 | has_projects: false 9 | has_wiki: false 10 | has_downloads: false 11 | default_branch: master 12 | allow_squash_merge: true 13 | allow_merge_commit: false 14 | allow_rebase_merge: true 15 | labels: 16 | # Priority labels 17 | - name: "Priority: Critical" 18 | color: ee0701 19 | description: "This should be dealt with ASAP. Not fixing this issue would be a serious error." 20 | - name: "Priority: High" 21 | color: b60205 22 | description: "After critical issues are fixed, these should be dealt with before any further issues." 23 | - name: "Priority: Medium" 24 | color: 0e8a16 25 | description: "This issue may be useful, and needs some attention." 26 | - name: "Priority: Low" 27 | color: e4ea8a 28 | description: "Nice addition, maybe... someday..." 29 | 30 | # Type labels 31 | - name: "Type: Bug" 32 | color: ee0701 33 | description: "Inconsistencies or issues which will cause a problem for users or implementors." 34 | - name: "Type: Documentation" 35 | color: 0052cc 36 | description: "Solely about the documentation of the project." 37 | - name: "Type: Enhancement" 38 | color: 1d76db 39 | description: "Enhancement of the code, not introducing new features." 40 | - name: "Type: Feature" 41 | color: 0e8a16 42 | description: "New features or options." 43 | - name: "Type: Support" 44 | color: 5319e7 45 | description: "Marks an issue as a support ticket." 46 | - name: "Type: Discussion" 47 | color: d4c5f9 48 | description: "Marks an issue as a generic discussion ticket." 49 | - name: "Type: Maintenance" 50 | color: 2af79e 51 | description: "Generic maintenance tasks, e.g., package updates." 52 | 53 | # Additional markers 54 | - name: "Security" 55 | color: ee0701 56 | description: "Marks a security issue that needs to be resolved asap." 57 | - name: "Idea" 58 | color: fef2c0 59 | description: "Marks an idea, which might be excepted and implemented." 60 | - name: "Incomplete" 61 | color: fef2c0 62 | description: "Marks a PR or issue that is missing information." 63 | - name: "Pull request" 64 | color: fbca04 65 | description: "There is an PR opened for this issue." 66 | - name: "Accepted" 67 | color: c2e0c6 68 | description: "This issue or PR has been accepted." 69 | - name: "Declined" 70 | color: f9d0c4 71 | description: "This issue or PR has been declined." 72 | - name: "Potential duplicate" 73 | color: e6e6e6 74 | description: "This issue has been automatically marked as a potential duplicate." 75 | 76 | # Ongoing Status labels 77 | - name: "Status: Triage" 78 | color: fbca04 79 | description: "This issue needs to be triaged." 80 | - name: "Status: On hold" 81 | color: cccccc 82 | description: "Issue or PR that has been placed on hold for now." 83 | - name: "Status: In progress" 84 | color: fbca04 85 | description: "Issue is currently being resolved by a developer." 86 | - name: "Status: Stale" 87 | color: fef2c0 88 | description: "There has not been activity on this issue or PR for quite some time." 89 | - name: "Status: Awaiting response" 90 | color: fef2c0 91 | description: "Issue or PR awaits response from the creator." 92 | - name: "Status: Blocked" 93 | color: fef2c0 94 | description: "Progress on this issue is currently not possible." 95 | 96 | # Closing status labels 97 | - name: "Closed: Known limitation" 98 | color: e6e6e6 99 | description: "Issue is closed, it is a known limitation." 100 | - name: "Closed: Expected behavior" 101 | color: e6e6e6 102 | description: "Issues is closed, it is expected behavior." 103 | - name: "Closed: Duplicate" 104 | color: e6e6e6 105 | description: "Issue is closed, duplicate of an existing issue." 106 | - name: "Closed: Invalid" 107 | color: e6e6e6 108 | description: "Issue is closed, marked as not a valid issue (e.g., an user error)." 109 | - name: "Closed: Wrong repository" 110 | color: e6e6e6 111 | description: "Issue is closed, was created in the wrong repository." 112 | - name: "Closed: Won't Fix" 113 | color: e6e6e6 114 | description: "Issue is closed, it won't be fixed." 115 | - name: "Closed: Done" 116 | color: c2e0c6 117 | description: "Issue closed, work on this issue has been marked complete." 118 | 119 | # Others 120 | - name: "Beginner Friendly" 121 | color: 0e8a16 122 | description: "Good first issue for people wanting to contribute to the project." 123 | - name: "Help wanted" 124 | color: 0e8a16 125 | description: "We need some extra helping hands or expertise in order to resolve this." 126 | - name: "Hacktoberfest" 127 | description: "Issues/PRs are participating in the Hacktoberfest" 128 | color: fbca04 129 | 130 | branches: 131 | - name: master 132 | protection: 133 | required_pull_request_reviews: 134 | # required_approving_review_count: 1 135 | dismiss_stale_reviews: true 136 | require_code_owner_reviews: true 137 | dismissal_restrictions: 138 | users: [] 139 | teams: 140 | - Admins 141 | - Masters 142 | required_status_checks: 143 | strict: false 144 | contexts: [] 145 | enforce_admins: false 146 | restrictions: 147 | users: [] 148 | teams: 149 | - Admins 150 | - Masters 151 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Home Assistant Bigmoby Add-on: WireGuard Client 2 | 3 | [![GitHub Release][releases-shield]][releases] 4 | ![Project Stage][project-stage-shield] 5 | [![License][license-shield]](LICENSE.md) 6 | 7 | ![Maintenance][maintenance-shield] 8 | [![GitHub Activity][commits-shield]][commits] 9 | 10 | WireGuard: fast, modern, secure VPN tunnel. 11 | 12 | ## About 13 | 14 | [WireGuard®][wireguard] is an extremely simple yet fast and modern VPN that 15 | utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, 16 | and more useful than IPsec, while avoiding the massive headache. 17 | 18 | It intends to be considerably more performant than OpenVPN. WireGuard is 19 | designed as a general-purpose VPN for running on embedded interfaces and 20 | supercomputers alike, fit for many different circumstances. 21 | 22 | Initially released for the Linux kernel, it is now cross-platform (Windows, 23 | macOS, BSD, iOS, Android) and widely deployable, 24 | including via an Hass.io add-on! 25 | 26 | WireGuard is currently under heavy development, but already it might be 27 | regarded as the most secure, easiest to use, and the simplest VPN solution 28 | in the industry. 29 | 30 | ## Breaking Changes 31 | 32 | - **New repository url** 33 | 34 | From version **_0.1.0_** will be dismissed the current repository and this will be the new repository url: 35 | 36 | ```text 37 | https://github.com/bigmoby/hassio-repository-addon 38 | ``` 39 | 40 | - **Docker Hub pre-build add-on** 41 | 42 | Update migration process from version **_0.0.3-SNAPSHOT_** to version **_0.0.4-SNAPSHOT_** fails because of new Docker Hub pre-build support. 43 | **SO YOU MUST REMOVE AND INSTALL THE NEW ADD-ON VERSION** **_MANUALLY._** 44 | 45 | ## Known issues 46 | 47 | - **error setting key 'net.ipv4.conf.all.src_valid_mark'** 48 | 49 | This add-on is not compatible with a catch-all value for allowed_ips, like 0.0.0.0/0. If you encounter this error 50 | 51 | ``` 52 | [#] sysctl -q net.ipv4.conf.all.src_valid_mark=1 53 | sysctl: error setting key 'net.ipv4.conf.all.src_valid_mark': Read-only file system 54 | ``` 55 | 56 | it's because you are trying to create routes to/from every possible IP through the WireGuard interface. Simply use single ips or ip classes that need to be connected. If you don't own a static ip address you can consider a VPN. 57 | 58 | ## Contributing 59 | 60 | This is an active open-source project. We are always open to people who want to 61 | use the code or contribute to it. 62 | 63 | We have set up a separate document containing our 64 | [contribution guidelines](CONTRIBUTING.md). 65 | 66 | Thank you for being involved! :heart_eyes: 67 | 68 | ## Sponsor 69 | 70 | Please, if You want support this kind of projects: 71 | 72 | Buy Me A Coffee 73 | 74 | Many Thanks, 75 | 76 | Fabio Mauro 77 | 78 | ## Authors & contributors 79 | 80 | Fabio Mauro Bigmoby 81 | 82 | Project forked from [Wireguard add-on][original_project]. 83 | 84 | For a full list of all authors and contributors, 85 | check [the contributor's page][contributors]. 86 | 87 | ## License 88 | 89 | MIT License 90 | 91 | Copyright (c) 2020-2025 Fabio Mauro 92 | 93 | Copyright (c) 2019-2020 Franck Nijhof 94 | 95 | Permission is hereby granted, free of charge, to any person obtaining a copy 96 | of this software and associated documentation files (the "Software"), to deal 97 | in the Software without restriction, including without limitation the rights 98 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 99 | copies of the Software, and to permit persons to whom the Software is 100 | furnished to do so, subject to the following conditions: 101 | 102 | The above copyright notice and this permission notice shall be included in all 103 | copies or substantial portions of the Software. 104 | 105 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 106 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 107 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 108 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 109 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 110 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 111 | SOFTWARE. 112 | 113 | [original_project]: https://github.com/hassio-addons/addon-wireguard 114 | [contributors]: https://github.com/bigmoby/addon-wireguard-client/graphs/contributors 115 | [aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg 116 | [amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg 117 | [commits-shield]: https://img.shields.io/github/commit-activity/y/hassio-addons/addon-wireguard.svg 118 | [commits]: https://github.com/bigmoby/addon-wireguard-client/commits/main 119 | [discord-ha]: https://discord.gg/c5DvZ4e 120 | [discord-shield]: https://img.shields.io/discord/478094546522079232.svg 121 | [discord]: https://discord.me/hassioaddons 122 | [docs]: https://github.com/bigmoby/addon-wireguard-client/blob/master/wireguard/DOCS.md 123 | [issue]: https://img.shields.io/github/issues/bigmoby/addon-wireguard-client.svg 124 | [license-shield]: https://img.shields.io/github/license/bigmoby/addon-wireguard-client.svg 125 | [maintenance-shield]: https://img.shields.io/maintenance/yes/2025.svg 126 | [project-stage-shield]: https://img.shields.io/badge/project%20stage-production%20ready-brightgreen.svg 127 | [reddit]: https://reddit.com/r/homeassistant 128 | [releases-shield]: https://img.shields.io/github/release/bigmoby/addon-wireguard-client.svg 129 | [releases]: https://github.com/bigmoby/addon-wireguard-client/releases 130 | [repository]: https://github.com/bigmoby/hassio-repository-addon 131 | [wireguard]: https://www.wireguard.com 132 | -------------------------------------------------------------------------------- /wireguard_client/DOCS.md: -------------------------------------------------------------------------------- 1 | # Home Assistant Community Add-on: WireGuard Client 2 | 3 | [WireGuard®][wireguard] is an extremely simple yet fast and modern VPN that 4 | utilizes state-of-the-art cryptography. It aims to be faster, simpler, leaner, 5 | and more useful than IPsec, while avoiding the massive headache. 6 | 7 | It intends to be considerably more performant than OpenVPN. WireGuard is 8 | designed as a general-purpose VPN for running on embedded interfaces and 9 | supercomputers alike, fit for many different circumstances. 10 | 11 | Initially released for the Linux kernel, it is now cross-platform (Windows, 12 | macOS, BSD, iOS, Android) and widely deployable, 13 | including via an Hass.io add-on! 14 | 15 | WireGuard is currently under heavy development, but already it might be 16 | regarded as the most secure, easiest to use, and the simplest VPN solution 17 | in the industry. 18 | 19 | ## Sponsor 20 | 21 | Please, if You want support this kind of projects: 22 | 23 | Buy Me A Coffee 24 | 25 | Many Thanks, 26 | 27 | Fabio Mauro 28 | 29 | ## Authors & contributors 30 | 31 | Fabio Mauro Bigmoby 32 | 33 | Project forked from [Wireguard add-on][original_project]. 34 | 35 | For a full list of all authors and contributors, 36 | check [the contributor's page][contributors]. 37 | 38 | ## Installation 39 | 40 | WireGuard Client add-on is pretty simple, however, can be quite complex for user that isn't 41 | familiar with all terminology used. The add-on takes care of a lot of things 42 | for you (if you want). 43 | 44 | Follow the following steps for installation & a quick start: 45 | 46 | 1. Search for the "WireGuard Client" add-on in the Supervisor add-on store 47 | and install it. 48 | 1. use the following configuration as example: 49 | 50 | ```yaml 51 | interface: 52 | private_key: your-private-key 53 | address: 10.6.0.2 54 | dns: 55 | - 8.8.8.8 56 | - 8.8.4.4 57 | post_up: "iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE; iptables -A FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu" 58 | post_down: "iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE; iptables -D FORWARD -p tcp --tcp-flags SYN,RST SYN -j TCPMSS --clamp-mss-to-pmtu" 59 | mtu: 1420 60 | peers: 61 | - public_key: your-public-key 62 | pre_shared_key: your-preshared-key 63 | endpoint: "xxxxxxxxxxxxxxx.duckdns.org:51820" 64 | allowed_ips: 65 | - 10.6.0.0/24 66 | persistent_keep_alive: 25 67 | ``` 68 | 69 | Please `0.0.0.0/0` is not allowed as `allowed_ips` value. 70 | 71 | 1. Save the configuration. 72 | 1. Start the "WireGuard" add-on 73 | 74 | ## WireGuard Client Unified API 75 | 76 | This add-on provides a unified API on port 51821 with comprehensive functionality. 77 | 78 | > **📚 Complete Documentation**: For comprehensive API documentation, detailed examples, automation templates, and advanced configurations, see **[API.md](https://github.com/bigmoby/addon-wireguard-client/blob/main/wireguard_client/API.md)**. 79 | 80 | ### 📊 Status Endpoint (GET /) 81 | 82 | Returns detailed WireGuard status information including: 83 | 84 | - Connection status (connected/disconnected) 85 | - Traffic statistics (bytes sent/received) 86 | - Peer count and individual peer details 87 | - Uptime and handshake information 88 | 89 | ### 🔧 Service Endpoints 90 | 91 | Provides VPN control actions: 92 | 93 | - **Reconnect**: Restart WireGuard connection 94 | - **Restart**: Full service restart 95 | - **Test**: Comprehensive connection validation 96 | 97 | ### 🏠 Home Assistant Integration 98 | 99 | With the use of the [Home Assistant RESTful][ha-rest] integration, you can create sensors and services: 100 | 101 | #### Basic Sensor Example: 102 | 103 | ```yaml 104 | rest: 105 | - resource: "http://local-wireguard-client:51821" 106 | scan_interval: 30 107 | timeout: 10 108 | verify_ssl: false 109 | sensor: 110 | - name: "WireGuard Status" 111 | value_template: "{{ value_json.status }}" 112 | icon: "mdi:vpn" 113 | 114 | - name: "WireGuard Traffic Received" 115 | value_template: "{{ value_json.total_traffic_rx }}" 116 | unit_of_measurement: "B" 117 | device_class: "data_size" 118 | icon: "mdi:download" 119 | ``` 120 | 121 | #### Service Commands Example: 122 | 123 | ```yaml 124 | rest_command: 125 | wireguard_test: 126 | url: "http://local-wireguard-client:51821/test" 127 | method: GET 128 | 129 | wireguard_reconnect: 130 | url: "http://local-wireguard-client:51821/reconnect" 131 | method: GET 132 | ``` 133 | 134 | ## Authors & contributors 135 | 136 | The original setup of this repository is by [Fabio Mauro][bigmoby]. 137 | 138 | This is a fork of Wireguard Add-on 139 | 140 | ## License 141 | 142 | MIT License 143 | 144 | Copyright (c) 2020-2025 Fabio Mauro 145 | 146 | Copyright (c) 2019-2020 Franck Nijhof 147 | 148 | Permission is hereby granted, free of charge, to any person obtaining a copy 149 | of this software and associated documentation files (the "Software"), to deal 150 | in the Software without restriction, including without limitation the rights 151 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 152 | copies of the Software, and to permit persons to whom the Software is 153 | furnished to do so, subject to the following conditions: 154 | 155 | The above copyright notice and this permission notice shall be included in all 156 | copies or substantial portions of the Software. 157 | 158 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 159 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 160 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 161 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 162 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 163 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 164 | SOFTWARE. 165 | 166 | [bigmoby]: https://github.com/bigmoby 167 | [wireguard]: https://www.wireguard.com 168 | [original_project]: https://github.com/hassio-addons/addon-wireguard 169 | [contributors]: https://github.com/bigmoby/addon-wireguard-client/graphs/contributors 170 | -------------------------------------------------------------------------------- /wireguard_client/rootfs/etc/cont-init.d/config.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bashio 2 | # ============================================================================== 3 | # Home Assistant Third Party Add-on: WireGuard Client 4 | # Creates the interface configuration 5 | # ============================================================================== 6 | declare -a list 7 | declare address 8 | declare allowed_ips 9 | declare config 10 | declare dns 11 | declare endpoint 12 | declare interface 13 | declare keep_alive 14 | declare peer_public_key 15 | declare post_down 16 | declare post_up 17 | declare mtu 18 | declare pre_shared_key 19 | 20 | if ! bashio::fs.directory_exists '/ssl/wireguard'; then 21 | mkdir -p /ssl/wireguard || 22 | bashio::exit.nok "Could not create wireguard storage folder!" 23 | fi 24 | 25 | # Get interface and config file location 26 | interface="wg0" 27 | 28 | config="/etc/wireguard/${interface}.conf" 29 | 30 | ########################### 31 | # Interface configuration # 32 | ########################### 33 | # Start creation of configuration 34 | echo "[Interface]" > "${config}" 35 | 36 | # Check if at least 1 private key value and if true get the interface private key 37 | if ! bashio::config.has_value 'interface.private_key'; then 38 | bashio::exit.nok 'You need a private_key configured for the interface client' 39 | else 40 | interface_private_key=$(bashio::config 'interface.private_key') 41 | # Validate that private key is not empty 42 | if [ -z "${interface_private_key}" ]; then 43 | bashio::exit.nok 'Private key cannot be empty' 44 | fi 45 | echo "PrivateKey = ${interface_private_key}" >> "${config}" 46 | fi 47 | 48 | # Check if at least 1 address value and if true get the interface address 49 | if ! bashio::config.has_value 'interface.address'; then 50 | bashio::exit.nok 'You need a address configured for the interface client' 51 | else 52 | address=$(bashio::config 'interface.address') 53 | [[ "${address}" == *"/"* ]] || address="${address}/24" 54 | echo "Address = ${address}" >> "${config}" 55 | fi 56 | 57 | # Add all server DNS addresses to the configuration 58 | if bashio::config.has_value "interface.dns"; then 59 | listDns=() 60 | # Use allowed IP's defined by the user. 61 | for address in $(bashio::config "interface.dns"); do 62 | listDns+=("${address}") 63 | done 64 | # Only add DNS if we have at least one DNS server 65 | if [ ${#listDns[@]} -gt 0 ]; then 66 | dns=$(IFS=", "; echo "${listDns[*]}") 67 | echo "DNS = ${dns}" >> "${config}" 68 | fi 69 | fi 70 | 71 | if [[ $(> "${config}" 88 | fi 89 | 90 | # Check if custom post_down value 91 | if bashio::config.has_value 'interface.post_down'; then 92 | post_down=$(bashio::config 'interface.post_down') 93 | echo "PostDown = ${post_down}" >> "${config}" 94 | fi 95 | 96 | # Check if custom mtu value 97 | if bashio::config.has_value 'interface.mtu'; then 98 | mtu=$(bashio::config 'interface.mtu') 99 | echo "MTU = ${mtu}" >> "${config}" 100 | fi 101 | 102 | # Status API Storage 103 | if ! bashio::fs.directory_exists '/var/lib/wireguard'; then 104 | mkdir -p /var/lib/wireguard \ 105 | || bashio::exit.nok "Could not create status API storage folder" 106 | fi 107 | 108 | if ! bashio::config.has_value 'peers'; then 109 | bashio::exit.nok 'Missing required list: peers' 110 | fi 111 | 112 | ###################### 113 | # Peer configuration # 114 | ###################### 115 | # Fetch all the peers 116 | for peer in $(bashio::config 'peers|keys'); do 117 | 118 | # Check if public key value and if true get the peer public key 119 | peer_public_key=$(bashio::config "peers[${peer}].public_key") 120 | # Validate that public key is not empty 121 | if [ -z "${peer_public_key}" ]; then 122 | bashio::exit.nok "Public key cannot be empty for peer ${peer}" 123 | fi 124 | 125 | # Check if pre_shared key value and if true get the peer pre_shared key 126 | pre_shared_key="" 127 | if bashio::config.has_value "peers[${peer}].pre_shared_key"; then 128 | pre_shared_key=$(bashio::config "peers[${peer}].pre_shared_key") 129 | fi 130 | 131 | # Check if endpoint value and if true get the peer endpoint 132 | endpoint="" 133 | if ! bashio::config.has_value "peers[${peer}].endpoint"; then 134 | bashio::exit.nok 'You need a endpoint configured for the peer' 135 | else 136 | endpoint=$(bashio::config "peers[${peer}].endpoint") 137 | fi 138 | 139 | # Check if persistent_keep_alive value and if true get the peer persistent_keep_alive 140 | keep_alive="" 141 | if ! bashio::config.has_value "peers[${peer}].persistent_keep_alive"; then 142 | bashio::exit.nok 'You need a persistent_keep_alive configured for the peer' 143 | else 144 | keep_alive=$(bashio::config "peers[${peer}].persistent_keep_alive") 145 | fi 146 | 147 | # Determine allowed IPs for server side config, by default use 148 | # peer defined addresses. 149 | list=() 150 | if bashio::config.has_value "peers[${peer}].allowed_ips"; then 151 | # Use allowed IP's defined by the user. 152 | for address in $(bashio::config "peers[${peer}].allowed_ips"); do 153 | [[ "${address}" == *"/"* ]] || address="${address}/32" 154 | list+=("${address}") 155 | done 156 | else 157 | bashio::exit.nok 'You need a allowed_ips configured for the peer' 158 | fi 159 | 160 | # Only proceed if we have at least one allowed IP 161 | if [ ${#list[@]} -eq 0 ]; then 162 | bashio::exit.nok 'No valid allowed_ips found for peer' 163 | fi 164 | 165 | allowed_ips=$(IFS=", "; echo "${list[*]}") 166 | 167 | # Start writing peer information in client config 168 | { 169 | echo "" 170 | echo "[Peer]" 171 | echo "PublicKey = ${peer_public_key}" 172 | if [ ! $pre_shared_key == "" ] 173 | then 174 | echo "PreSharedKey = ${pre_shared_key}" 175 | fi 176 | echo "Endpoint = ${endpoint}" 177 | echo "AllowedIPs = ${allowed_ips}" 178 | echo "PersistentKeepalive = ${keep_alive}" 179 | echo "" 180 | } >> "${config}" 181 | done 182 | 183 | bashio::log.info "Ended to write Wireguard configuration into: [${config}]" 184 | -------------------------------------------------------------------------------- /wireguard_client/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## What's changed in Wireguard Client Add-on v0.2.8 2 | 3 | ### 🐛 Fixes 4 | 5 | - **Fixed critical API bug**: Resolved double JSON serialization issue where `peers` field was returned as a string instead of a JSON array. This was causing template errors in Home Assistant sensors (`Template variable warning: 'str object' has no attribute 'peer_1'`). The API now correctly returns `peers` as a proper JSON array. 6 | 7 | - **Fixed Jinja2 templates**: Corrected Home Assistant sensor templates to properly access `peers[0]` instead of `peers.peer_1`, ensuring compatibility with the array structure. 8 | 9 | - **Fixed API response format**: `latest_handshake` now correctly returns `"Never"` (as a string) when no handshake has occurred, instead of an empty string, making template handling more reliable. 10 | 11 | ### ⚠️ Known Issues 12 | 13 | - **Port configuration after update**: If you upgraded from v0.2.6 or earlier and the API port is not working, you may need to restore default network settings in the add-on configuration. This is a migration issue where the port configuration doesn't automatically update during the upgrade process. See v0.2.7 release notes for details. 14 | 15 | ## What's changed in Wireguard Client Add-on v0.2.7 16 | 17 | ### 🚀 Enhancements 18 | 19 | - **Replaced netcat with socat**: Improved HTTP server with persistent connections and better performance 20 | - **Unified API**: Combined status and services into single endpoint on port 51821 21 | 22 | ### ⚠️ Important Note 23 | 24 | - **Port configuration migration**: After updating to v0.2.7, if you experience issues with the API (e.g., "WireGuard Unified API disabled (no port exposed)"), you may need to restore default network settings. Go to the add-on configuration → Network section → Click "Restore defaults" to update the port from the old configuration (e.g., port 80) to the new unified API port (51821). This is a known migration issue when upgrading from previous versions. 25 | 26 | ## What's changed in Wireguard Client Add-on v0.2.6 27 | 28 | ### 🛠 Fixes 29 | 30 | - **Fixed port conflicts**: Separated status API (port 51821) and services API (port 51822) to prevent conflicts 31 | - **Fixed API documentation**: Updated documentation with correct hostname and port configurations 32 | 33 | ### 🚀 Enhancements 34 | 35 | - **Enhanced API**: Extended status API with comprehensive sensor data including traffic statistics, uptime, and peer information 36 | - **Service endpoints**: Added RESTful endpoints for VPN actions (reconnect, restart, test) 37 | - **Smart testing**: Comprehensive connection test that checks interface status, handshake validity, and server connectivity 38 | - **Home Assistant integration**: Full compatibility with RESTful sensor platform for seamless automation 39 | - **Code optimization**: Cleaned up redundant endpoints and unused variables for better performance 40 | - **Non-standard ports**: Uses ports 51821 and 51822 to avoid conflicts with common services 41 | - **Comprehensive documentation**: Added detailed API documentation with examples for sensors and services 42 | 43 | ## What's changed in Wireguard Client Add-on v0.2.5 44 | 45 | ### 🛠 Fixes 46 | 47 | - **Fixed "wg0 already exists" error**: Improved startup and shutdown scripts to handle existing WireGuard interfaces gracefully 48 | - **Enhanced error handling**: Added automatic cleanup of existing interfaces before starting new connections 49 | - **Fixed interface cleanup**: Better handling of stale WireGuard interfaces during addon restarts 50 | 51 | ### 🚀 Enhancements 52 | 53 | - **Improved startup script**: Now detects and cleans up existing WireGuard interfaces automatically 54 | - **Enhanced shutdown script**: Better cleanup process with fallback manual interface removal 55 | - **Better logging**: More detailed logging for troubleshooting interface conflicts 56 | - **Robust interface management**: Automatic detection and cleanup of stale WireGuard interfaces 57 | 58 | ## What's changed in Wireguard Client Add-on v0.2.4 59 | 60 | ### 🛠 Fixes 61 | 62 | - **Fixed wireguard-tools version conflict**: Updated from 1.0.20210914-r4 to 1.0.20250521-r0 to resolve package conflicts 63 | - **Fixed base image compatibility**: Updated from 16.3.4 to 18.1.4 for better Alpine Linux compatibility 64 | 65 | ### 💣 BREAKING CHANGES 66 | 67 | - **Removed support for deprecated architectures**: Following Home Assistant's deprecation notice, removed support for i386, armhf, and armv7 architectures. Only aarch64 and amd64 are now supported. 68 | - Aligned with Home Assistant's official architecture support policy 69 | - Simplified build process by removing legacy architecture support 70 | 71 | ## What's changed in Wireguard Client Add-on v0.2.3 72 | 73 | ### 🛠 Fixs 74 | 75 | - Bump wireguard-tools to 1.0.20210914-r4 76 | 77 | ## What’s changed in Wireguard Client Add-on v0.2.2 78 | 79 | ### 🛠 Fixs 80 | 81 | - Fixed json formatting for api (thanks to @olpal ) 82 | 83 | ## What’s changed in Wireguard Client Add-on v0.2.1 84 | 85 | ## 🚀 Enhancements 86 | 87 | - Add MTU configuration param 88 | - Readme fix 89 | 90 | ## What’s changed in Wireguard Client Add-on v0.2.0 91 | 92 | ## 🚀 Enhancements 93 | 94 | - Migrate JSON config to YAML 95 | - Upgrade add-on base image to 11.0.0 96 | 97 | ### ⬆️ Dependency updates 98 | 99 | - Upgrade wireguard-tools to 1.0.20210914-r0 100 | 101 | ## What’s changed in Wireguard Client Add-on v0.1.9 102 | 103 | ### 💣 BREAKING CHANGES 104 | 105 | - new peers section in order to configure several peer connection (thanks to Stefan Berggren aka "nsg" https://github.com/nsg for suggest me this feature and give me some hints with his PR) 106 | 107 | ```yaml 108 | interface: 109 | private_key: test_key 110 | address: 10.6.0.2 111 | dns: 112 | - 8.8.8.8 113 | - 8.8.4.4 114 | post_up: iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE 115 | post_down: iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE 116 | peer: 117 | public_key: test_key 118 | pre_shared_key: test_key 119 | endpoint: xxxxxxxxxxxxxxx.duckdns.org:51820 120 | allowed_ips: 121 | - 10.6.0.0/24 122 | persistent_keep_alive: 25 123 | ``` 124 | 125 | should be re-configured in 126 | 127 | ```yaml 128 | interface: 129 | private_key: test_key 130 | address: 10.6.0.2 131 | dns: 132 | - 8.8.8.8 133 | - 8.8.4.4 134 | post_up: iptables -t nat -A POSTROUTING -o wg0 -j MASQUERADE 135 | post_down: iptables -t nat -D POSTROUTING -o wg0 -j MASQUERADE 136 | peers: 137 | - public_key: test_key 138 | pre_shared_key: test_key 139 | endpoint: xxxxxxxxxxxxxxx.duckdns.org:51820 140 | allowed_ips: 141 | - 10.6.0.0/24 142 | persistent_keep_alive: "25" 143 | - public_key: test_key 144 | pre_shared_key: test_key 145 | endpoint: yyyyyyyyyyyyyyy.duckdns.org:51820 146 | allowed_ips: 147 | - 10.6.0.1/24 148 | persistent_keep_alive: "26" 149 | ``` 150 | 151 | - `dns`,`post_up`,`post_down` have become optional params 152 | 153 | ## What’s changed in Wireguard Client Add-on v0.1.8 154 | 155 | ### 🛠 Fixs 156 | 157 | - hotfix to REST API service port (thanks to Klaus-Uwe Mitterer aka "Kumi" https://github.com/kumitterer) 158 | 159 | ### 🚀 Improvements 160 | 161 | - Removing unuseful default Wireguard port specification field 162 | - Upgrade add-on base image to 10.0.1 163 | - Upgrade wireguard-tools version to 1.0.20210424-r0 164 | 165 | ## What’s changed in Wireguard Client Add-on v0.1.7 166 | 167 | ### 🛠 Fixs 168 | 169 | - hotfix to REST API service 170 | 171 | ## What’s changed in Wireguard Client Add-on v0.1.6 172 | 173 | ### 🚀 Improvements 174 | 175 | - Optional `pre_shared_key` parameter 176 | - Simple Rest API in order to expose Wireguard status in `sensor` configuration 177 | 178 | ### 🛠 Fixs 179 | 180 | - `interface.address` is not hardcoded to its `/24` mask ~> if mask not specified then `/24`will be applied otherwise it is possible to assign `10.6.0.0/32` 181 | 182 | ### ⬆️ Dependency updates 183 | 184 | - Upgrade add-on base image to 9.2.0 185 | -------------------------------------------------------------------------------- /wireguard_client/rootfs/etc/services.d/api/run: -------------------------------------------------------------------------------- 1 | #!/usr/bin/with-contenv bashio 2 | # shellcheck shell=bash 3 | # ============================================================================== 4 | # Home Assistant Third Party Add-on: WireGuard Client - Unified API (socat) 5 | # s6 service: single 'run' that spawns socat; per-connection handler generated inline. 6 | # Endpoints: 7 | # GET / -> status JSON (peers, totals, timestamp) 8 | # GET /status -> alias di / 9 | # GET /reconnect -> wg-quick down/up 10 | # GET /restart -> wg-quick down/up (wait 3s) 11 | # GET /test -> health check (handshake + optional ping) 12 | # ============================================================================== 13 | 14 | set -euo pipefail 15 | 16 | PORT="$(bashio::addon.port "51821/tcp" || true)" 17 | if [[ -z "${PORT:-}" || "${PORT}" == "0" ]]; then 18 | bashio::log.warning "WireGuard Unified API disabled (no port exposed)." 19 | exit 0 20 | fi 21 | 22 | if ! command -v socat >/dev/null 2>&1; then 23 | bashio::log.error "socat not found. Add it to your image (e.g., apk add --no-cache socat)." 24 | exit 1 25 | fi 26 | 27 | WORKDIR="/run/wg-unified-api" 28 | mkdir -p "$WORKDIR" 29 | 30 | # --- Create per-connection handler (executed by socat via SYSTEM=) --- 31 | HANDLER="${WORKDIR}/handler.sh" 32 | cat > "$HANDLER" <<'EOF' 33 | #!/usr/bin/with-contenv bashio 34 | set -euo pipefail 35 | 36 | # === IMPORTANTISSIMO: separa la socket HTTP da QUALSIASI log === 37 | # 1) Salva la socket (stdout all'avvio) su FD 3 38 | exec 3>&1 39 | # 2) Taglia TUTTO l'output del handler (stdout+stderr) per evitare byte prima delle header 40 | exec 1>/dev/null 41 | exec 2>/dev/null 42 | 43 | # Helper: invia risposta JSON su FD 3 (stessa connessione) 44 | send_json() { 45 | # $1 = status line (es. "200 OK") 46 | # $2 = payload JSON 47 | local _status="${1}" 48 | local _body="${2}" 49 | printf 'HTTP/1.1 %s\r\n' "${_status}" >&3 50 | printf 'Content-Type: application/json\r\n' >&3 51 | printf 'Content-Length: %s\r\n' "${#_body}" >&3 52 | printf 'Connection: close\r\n' >&3 53 | printf '\r\n' >&3 54 | printf '%s' "${_body}" >&3 55 | } 56 | 57 | # Leggi la request line (es. "GET /test HTTP/1.1") 58 | if ! IFS= read -r request_line; then 59 | exit 0 60 | fi 61 | 62 | # Consuma header fino a riga vuota 63 | while IFS= read -r h; do 64 | [[ -z "$h" || "$h" = $'\r' ]] && break 65 | done 66 | 67 | # Determina azione in base al path 68 | action="status" 69 | case "${request_line}" in 70 | *"GET /reconnect"*) action="reconnect" ;; 71 | *"GET /restart"*) action="restart" ;; 72 | *"GET /test"*) action="test" ;; 73 | *"GET /status"*) action="status" ;; 74 | *"GET / "*) action="status" ;; 75 | *) action="unknown" ;; 76 | esac 77 | 78 | case "$action" in 79 | "status") 80 | total_rx=0 81 | total_tx=0 82 | peer_count=0 83 | current_time="$(date +%s)" 84 | peers_json="[]" 85 | 86 | if ip link show wg0 >/dev/null 2>&1; then 87 | interface_status="connected" 88 | else 89 | interface_status="disconnected" 90 | fi 91 | 92 | # wg show all dump: tab-separated 93 | # iface privkey pubkey endpoint allowedips latest_handshake rx tx ... 94 | count=0 95 | while IFS=$'\t' read -r -a line; do 96 | if [[ "${#line[@]}" -ge 8 ]]; then 97 | endpoint="${line[3]}" 98 | handshake_epoch="${line[5]}" 99 | transfer_rx="${line[6]}" 100 | transfer_tx="${line[7]}" 101 | 102 | [[ "${transfer_rx}" =~ ^[0-9]+$ ]] || transfer_rx=0 103 | [[ "${transfer_tx}" =~ ^[0-9]+$ ]] || transfer_tx=0 104 | [[ "${handshake_epoch}" =~ ^[0-9]+$ ]] || handshake_epoch=0 105 | 106 | if [[ "${handshake_epoch}" != "0" ]]; then 107 | latest_handshake="$(date -u -d "@${handshake_epoch}" +'%Y-%m-%dT%H:%M:%SZ' 2>/dev/null || date -u -r "${handshake_epoch}" +'%Y-%m-%dT%H:%M:%SZ')" 108 | uptime_seconds=$(( current_time - handshake_epoch )) 109 | (( uptime_seconds < 0 )) && uptime_seconds=0 110 | else 111 | latest_handshake="Never" 112 | uptime_seconds=0 113 | fi 114 | 115 | # Create peer object (always include latest_handshake) 116 | peer_obj="$(bashio::var.json \ 117 | 'endpoint' "${endpoint}" \ 118 | 'latest_handshake' "${latest_handshake}" \ 119 | 'transfer_rx' "^${transfer_rx}" \ 120 | 'transfer_tx' "^${transfer_tx}" \ 121 | 'uptime_seconds' "^${uptime_seconds}")" 122 | 123 | total_rx=$(( total_rx + transfer_rx )) 124 | total_tx=$(( total_tx + transfer_tx )) 125 | peer_count=$(( peer_count + 1 )) 126 | count=$(( count + 1 )) 127 | 128 | if [[ "${peers_json}" == "[]" ]]; then 129 | peers_json="[${peer_obj}]" 130 | else 131 | peers_json="${peers_json%]}"', '"${peer_obj}"']' 132 | fi 133 | fi 134 | done <<< "$(wg show all dump 2>/dev/null || true)" 135 | 136 | # Build JSON manually to avoid double-serialization of peers array 137 | # bashio::var.json would serialize peers_json as a string, but we need it as an array 138 | json_base="$(bashio::var.json \ 139 | 'status' "${interface_status}" \ 140 | 'total_traffic_rx' "^${total_rx}" \ 141 | 'total_traffic_tx' "^${total_tx}" \ 142 | 'peer_count' "^${peer_count}" \ 143 | 'timestamp' "^${current_time}")" 144 | 145 | # Insert peers array directly into JSON (remove closing brace, add peers, close) 146 | # json_base ends with }, so we replace the } with , "peers": [...]} 147 | json="${json_base%'}'}"', "peers": '"${peers_json}"'}' 148 | 149 | send_json "200 OK" "${json}" 150 | ;; 151 | 152 | "reconnect") 153 | if wg-quick down wg0 2>/dev/null && sleep 2 && wg-quick up wg0 2>/dev/null; then 154 | send_json "200 OK" '{"action":"reconnect","result":"success","message":"WireGuard reconnected successfully"}' 155 | else 156 | send_json "200 OK" '{"action":"reconnect","result":"error","message":"Failed to reconnect WireGuard"}' 157 | fi 158 | ;; 159 | 160 | "restart") 161 | if wg-quick down wg0 2>/dev/null && sleep 3 && wg-quick up wg0 2>/dev/null; then 162 | send_json "200 OK" '{"action":"restart","result":"success","message":"WireGuard restarted successfully"}' 163 | else 164 | send_json "200 OK" '{"action":"restart","result":"error","message":"Failed to restart WireGuard"}' 165 | fi 166 | ;; 167 | 168 | "test") 169 | if ! ip link show wg0 >/dev/null 2>&1; then 170 | send_json "200 OK" '{"action":"test","result":"error","message":"WireGuard interface wg0 not found"}' 171 | exit 0 172 | fi 173 | if ! wg show wg0 >/dev/null 2>&1; then 174 | send_json "200 OK" '{"action":"test","result":"error","message":"WireGuard interface wg0 not configured"}' 175 | exit 0 176 | fi 177 | 178 | latest_handshake="$(wg show wg0 dump | awk 'NR==1{print $5}')" 179 | [[ "${latest_handshake}" =~ ^[0-9]+$ ]] || latest_handshake=0 180 | current_time="$(date +%s)" 181 | 182 | if (( latest_handshake != 0 )) && (( current_time - latest_handshake < 300 )); then 183 | vpn_server_ip="$(wg show wg0 allowed-ips | awk 'NR==1{print $2}' | cut -d'/' -f1)" 184 | if [[ -n "${vpn_server_ip:-}" ]] && ping -c 1 -W 2 "${vpn_server_ip}" >/dev/null 2>&1; then 185 | send_json "200 OK" '{"action":"test","result":"success","message":"WireGuard active with recent handshake and server reachable"}' 186 | else 187 | send_json "200 OK" '{"action":"test","result":"success","message":"WireGuard active with recent handshake"}' 188 | fi 189 | else 190 | send_json "200 OK" '{"action":"test","result":"error","message":"WireGuard interface exists but no recent handshake"}' 191 | fi 192 | ;; 193 | 194 | *) 195 | send_json "404 Not Found" '{"error":"Not found"}' 196 | ;; 197 | esac 198 | EOF 199 | chmod +x "$HANDLER" 200 | 201 | # Clean up on exit 202 | cleanup() { 203 | rm -f "$HANDLER" 204 | } 205 | trap cleanup EXIT 206 | 207 | bashio::log.info "Starting WireGuard Unified API on port ${PORT}..." 208 | 209 | # -T 5: inactivity timeout; -t 5: connection timeout 210 | # fork: 1 processo per connessione; reuseaddr: restart veloci; keepalive: TCP keepalive 211 | # (per debug: aggiungi -d -d subito dopo 'socat') 212 | exec socat -T 5 -t 5 TCP-LISTEN:${PORT},fork,reuseaddr,keepalive SYSTEM:"${HANDLER}" 213 | -------------------------------------------------------------------------------- /wireguard_client/API.md: -------------------------------------------------------------------------------- 1 | # WireGuard Client Addon - Unified API 2 | 3 | This addon provides a unified API with comprehensive sensor data and service endpoints on a single port (51821). 4 | 5 | ## 📊 Status API Endpoint 6 | 7 | ### **GET /** (Port 51821) 8 | 9 | Returns comprehensive WireGuard status information. 10 | 11 | #### **Response Format:** 12 | 13 | ```json 14 | { 15 | "status": "connected|disconnected", 16 | "total_traffic_rx": 1234567, 17 | "total_traffic_tx": 2345678, 18 | "peer_count": 2, 19 | "timestamp": 1696789123, 20 | "peers": [ 21 | { 22 | "endpoint": "server.com:51820", 23 | "latest_handshake": "2023-10-09T19:45:23Z", 24 | "transfer_rx": 1234567, 25 | "transfer_tx": 2345678, 26 | "uptime_seconds": 3600 27 | }, 28 | { 29 | "endpoint": "server2.com:51820", 30 | "latest_handshake": "2023-10-09T19:44:15Z", 31 | "transfer_rx": 0, 32 | "transfer_tx": 0, 33 | "uptime_seconds": 0 34 | } 35 | ] 36 | } 37 | ``` 38 | 39 | #### **Fields Description:** 40 | 41 | - **status**: Current connection status (`connected` or `disconnected`) 42 | - **total_traffic_rx**: Total bytes received across all peers 43 | - **total_traffic_tx**: Total bytes sent across all peers 44 | - **peer_count**: Number of configured peers 45 | - **timestamp**: Current Unix timestamp 46 | - **peers**: Array containing individual peer information (indexed from 0) 47 | - **endpoint**: Peer server endpoint 48 | - **latest_handshake**: Last successful handshake timestamp in ISO format (YYYY-MM-DDTHH:MM:SSZ), or `"Never"` if no handshake has occurred yet 49 | - **transfer_rx**: Bytes received from this peer 50 | - **transfer_tx**: Bytes sent to this peer 51 | - **uptime_seconds**: Connection uptime in seconds (0 if no handshake occurred) 52 | 53 | ## 🔧 Service Endpoints 54 | 55 | ### **GET /reconnect** 56 | 57 | Reconnects the WireGuard interface. 58 | 59 | #### **Response:** 60 | 61 | ```json 62 | { 63 | "action": "reconnect", 64 | "result": "success|error", 65 | "message": "Description of the result" 66 | } 67 | ``` 68 | 69 | ### **GET /restart** 70 | 71 | Restarts the WireGuard service. 72 | 73 | #### **Response:** 74 | 75 | ```json 76 | { 77 | "action": "restart", 78 | "result": "success|error", 79 | "message": "Description of the result" 80 | } 81 | ``` 82 | 83 | ### **GET /test** 84 | 85 | Comprehensive WireGuard connection test that checks: 86 | 87 | - Interface existence and configuration 88 | - Handshake validity (within last 3 minutes) 89 | - Server connectivity (when possible) 90 | 91 | #### **Response:** 92 | 93 | ```json 94 | { 95 | "action": "test", 96 | "result": "success|error", 97 | "message": "Description of the result" 98 | } 99 | ``` 100 | 101 | ## 🏠 Home Assistant Integration 102 | 103 | ### **RESTful Sensor Configuration** 104 | 105 | Add to your `configuration.yaml`: 106 | 107 | > **Note**: Sensor names are automatically converted to entity IDs by Home Assistant. For example, `"WireGuard Status"` becomes `sensor.wireguard_status` (lowercase with spaces replaced by underscores). 108 | 109 | ```yaml 110 | rest: 111 | - resource: "http://local-wireguard-client:51821" 112 | scan_interval: 30 113 | timeout: 10 114 | verify_ssl: false 115 | sensor: 116 | - name: "WireGuard Status" 117 | value_template: "{{ value_json.status }}" 118 | icon: "mdi:vpn" 119 | 120 | - name: "WireGuard Traffic Received" 121 | value_template: "{{ value_json.total_traffic_rx }}" 122 | unit_of_measurement: "B" 123 | device_class: "data_size" 124 | icon: "mdi:download" 125 | 126 | - name: "WireGuard Traffic Sent" 127 | value_template: "{{ value_json.total_traffic_tx }}" 128 | unit_of_measurement: "B" 129 | device_class: "data_size" 130 | icon: "mdi:upload" 131 | 132 | - name: "WireGuard Peer Count" 133 | value_template: "{{ value_json.peer_count }}" 134 | icon: "mdi:account-multiple" 135 | 136 | - name: "WireGuard Uptime" 137 | value_template: >- 138 | {%- if value_json.peers is defined and value_json.peers|length > 0 -%} 139 | {{ value_json.peers[0].uptime_seconds | default(0) }} 140 | {%- else -%} 141 | 0 142 | {%- endif -%} 143 | unit_of_measurement: "s" 144 | device_class: "duration" 145 | icon: "mdi:clock" 146 | 147 | - name: "WireGuard Last Handshake" 148 | value_template: >- 149 | {%- if value_json.peers is defined and value_json.peers|length > 0 -%} 150 | {{ value_json.peers[0].latest_handshake | default('Never') }} 151 | {%- else -%} 152 | Never 153 | {%- endif -%} 154 | icon: "mdi:handshake" 155 | ``` 156 | 157 | ### **RESTful Binary Sensor Configuration** 158 | 159 | For simple on/off status monitoring: 160 | 161 | ```yaml 162 | rest: 163 | - resource: "http://local-wireguard-client:51821" 164 | scan_interval: 30 165 | timeout: 10 166 | verify_ssl: false 167 | binary_sensor: 168 | - name: "WireGuard Connected" 169 | value_template: "{{ value_json.status == 'connected' }}" 170 | device_class: "connectivity" 171 | icon: "mdi:vpn" 172 | ``` 173 | 174 | ### **RESTful Command Configuration** 175 | 176 | Add to your `configuration.yaml`: 177 | 178 | ```yaml 179 | rest_command: 180 | wireguard_reconnect: 181 | url: "http://local-wireguard-client:51821/reconnect" 182 | method: GET 183 | 184 | wireguard_restart: 185 | url: "http://local-wireguard-client:51821/restart" 186 | method: GET 187 | 188 | wireguard_test: 189 | url: "http://local-wireguard-client:51821/test" 190 | method: GET 191 | ``` 192 | 193 | ## 📝 Jinja2 Template Examples for Peers 194 | 195 | ### **Understanding `value_json`** 196 | 197 | `value_json` is an automatic variable provided by Home Assistant's RESTful integration. Here's how the connection works: 198 | 199 | #### **How Home Assistant Links REST Response to `value_json`** 200 | 201 | When you configure a REST sensor in `configuration.yaml`: 202 | 203 | ```yaml 204 | rest: 205 | - resource: "http://local-wireguard-client:51821" # ← Step 1: API endpoint 206 | sensor: 207 | - name: "WireGuard Status" 208 | value_template: "{{ value_json.status }}" # ← Step 4: Use value_json 209 | ``` 210 | 211 | **The process works as follows:** 212 | 213 | 1. **Home Assistant makes HTTP request**: 214 | 215 | - Periodically (based on `scan_interval`), Home Assistant sends a GET request to `http://local-wireguard-client:51821` 216 | - Your WireGuard addon API responds with JSON 217 | 218 | 2. **API Response** (from WireGuard addon): 219 | 220 | - Returns JSON response (see "Response Format" section above for complete structure) 221 | 222 | 3. **Home Assistant parses JSON**: 223 | 224 | - Home Assistant receives the HTTP response 225 | - It automatically parses the JSON body 226 | - The parsed JSON object is stored internally 227 | 228 | 4. **`value_json` is created automatically**: 229 | 230 | - Home Assistant automatically creates the `value_json` variable 231 | - `value_json` contains the entire parsed JSON object from the API response 232 | - This happens **automatically** - you don't need to define it 233 | 234 | 5. **Template evaluation**: 235 | - When your `value_template` is evaluated, `value_json` is already available 236 | - `value_json.status` → `"connected"` 237 | - `value_json.peers[0].endpoint` → `"server.com:51820"` 238 | 239 | #### **Visual Flow** 240 | 241 | ``` 242 | ┌─────────────────┐ 243 | │ Home Assistant │ 244 | │ REST Sensor │ 245 | └────────┬────────┘ 246 | │ 247 | │ 1. GET http://local-wireguard-client:51821 248 | │ (every scan_interval seconds) 249 | ▼ 250 | ┌─────────────────┐ 251 | │ WireGuard Addon │ 252 | │ API (port 51821)│ 253 | └────────┬────────┘ 254 | │ 255 | │ 2. JSON Response: 256 | │ {"status": "connected", "peers": [...]} 257 | ▼ 258 | ┌─────────────────┐ 259 | │ Home Assistant │ 260 | │ JSON Parser │ 261 | └────────┬────────┘ 262 | │ 263 | │ 3. Parse JSON → Create value_json object 264 | ▼ 265 | ┌─────────────────┐ 266 | │ Jinja2 Template │ 267 | │ value_json.* │ ← 4. Use value_json in templates 268 | └─────────────────┘ 269 | ``` 270 | 271 | #### **Key Points** 272 | 273 | - **Automatic**: `value_json` is created automatically by Home Assistant - you never define it 274 | - **Scope**: Only available in `value_template` of REST sensors (not in automations or other templates) 275 | - **Content**: Contains the entire JSON response from the API endpoint 276 | - **Timing**: Updated every `scan_interval` seconds (default: 30 seconds) 277 | 278 | > **Important**: `value_json` is only available in templates within REST sensor configurations. For other contexts (automations, template sensors), see the "Using Peers Data in Other Contexts" section below. 279 | 280 | ### **Template Examples** 281 | 282 | Here are practical examples of how to extract information from the `peers` array using Jinja2 templates in Home Assistant: 283 | 284 | ### **Accessing the First Peer** 285 | 286 | ```yaml 287 | # Get endpoint of first peer 288 | {{ value_json.peers[0].endpoint | default('Unknown') }} 289 | 290 | # Get uptime of first peer 291 | {{ value_json.peers[0].uptime_seconds | default(0) }} 292 | 293 | # Get latest handshake of first peer 294 | {{ value_json.peers[0].latest_handshake | default('Never') }} 295 | 296 | # Get received traffic from first peer 297 | {{ value_json.peers[0].transfer_rx | default(0) }} 298 | 299 | # Get sent traffic to first peer 300 | {{ value_json.peers[0].transfer_tx | default(0) }} 301 | ``` 302 | 303 | ### **Iterating Over All Peers** 304 | 305 | ```yaml 306 | # Count active peers (with handshake) 307 | {%- set active_peers = value_json.peers | selectattr('latest_handshake', 'ne', 'Never') | list -%} 308 | {{ active_peers | length }} 309 | 310 | # Get all endpoints as comma-separated list 311 | {%- for peer in value_json.peers -%} 312 | {{ peer.endpoint }}{% if not loop.last %}, {% endif %} 313 | {%- endfor -%} 314 | 315 | # Get total traffic across all peers 316 | {%- set total_rx = value_json.peers | sum(attribute='transfer_rx') -%} 317 | {%- set total_tx = value_json.peers | sum(attribute='transfer_tx') -%} 318 | RX: {{ total_rx }} B, TX: {{ total_tx }} B 319 | ``` 320 | 321 | ### **Finding Specific Peer by Endpoint** 322 | 323 | ```yaml 324 | # Find peer by endpoint (case-insensitive) 325 | {%- set target_endpoint = 'server.com:51820' -%} 326 | {%- set peer = value_json.peers | selectattr('endpoint', 'equalto', target_endpoint) | first -%} 327 | {%- if peer -%} 328 | Endpoint: {{ peer.endpoint }}, Uptime: {{ peer.uptime_seconds }}s 329 | {%- else -%} 330 | Peer not found 331 | {%- endif -%} 332 | ``` 333 | 334 | ### **Advanced Template Examples** 335 | 336 | #### **Sensor: WireGuard Active Peer Endpoint** 337 | 338 | ```yaml 339 | sensor: 340 | - name: "WireGuard Active Peer Endpoint" 341 | value_template: >- 342 | {%- if value_json.peers is defined and value_json.peers|length > 0 -%} 343 | {%- set active_peer = value_json.peers | selectattr('latest_handshake', 'ne', 'Never') | first -%} 344 | {%- if active_peer -%} 345 | {{ active_peer.endpoint }} 346 | {%- else -%} 347 | No active peer 348 | {%- endif -%} 349 | {%- else -%} 350 | No peers configured 351 | {%- endif -%} 352 | icon: "mdi:server-network" 353 | ``` 354 | 355 | #### **Sensor: WireGuard Peer Traffic (Total)** 356 | 357 | ```yaml 358 | sensor: 359 | - name: "WireGuard Peer Traffic Total" 360 | value_template: >- 361 | {%- if value_json.peers is defined and value_json.peers|length > 0 -%} 362 | {%- set total_rx = value_json.peers | sum(attribute='transfer_rx') | default(0) -%} 363 | {%- set total_tx = value_json.peers | sum(attribute='transfer_tx') | default(0) -%} 364 | RX: {{ (total_rx / 1024 / 1024) | round(2) }} MB, TX: {{ (total_tx / 1024 / 1024) | round(2) }} MB 365 | {%- else -%} 366 | No data 367 | {%- endif -%} 368 | icon: "mdi:network" 369 | ``` 370 | 371 | #### **Sensor: WireGuard Peer Count (Active)** 372 | 373 | ```yaml 374 | sensor: 375 | - name: "WireGuard Active Peer Count" 376 | value_template: >- 377 | {%- if value_json.peers is defined -%} 378 | {%- set active = value_json.peers | selectattr('latest_handshake', 'ne', 'Never') | list -%} 379 | {{ active | length }} / {{ value_json.peers | length }} 380 | {%- else -%} 381 | 0 / 0 382 | {%- endif -%} 383 | icon: "mdi:account-multiple-check" 384 | ``` 385 | 386 | #### **Template: Format Uptime as Human-Readable** 387 | 388 | ```yaml 389 | # In a template sensor or automation 390 | value_template: >- 391 | {%- if value_json.peers[0].uptime_seconds is defined -%} 392 | {%- set uptime = value_json.peers[0].uptime_seconds | int -%} 393 | {%- if uptime >= 86400 -%} 394 | {{ (uptime / 86400) | round(1) }} days 395 | {%- elif uptime >= 3600 -%} 396 | {{ (uptime / 3600) | round(1) }} hours 397 | {%- elif uptime >= 60 -%} 398 | {{ (uptime / 60) | round(0) | int }} minutes 399 | {%- else -%} 400 | {{ uptime }} seconds 401 | {%- endif -%} 402 | {%- else -%} 403 | 0 seconds 404 | {%- endif -%} 405 | ``` 406 | 407 | #### **Template: Check if Handshake is Recent (within 3 minutes)** 408 | 409 | ```yaml 410 | # In an automation condition 411 | condition: template 412 | value_template: >- 413 | {%- if value_json.peers[0].latest_handshake is defined and value_json.peers[0].latest_handshake != 'Never' -%} 414 | {%- set handshake_str = value_json.peers[0].latest_handshake -%} 415 | {%- set handshake_time = handshake_str | as_datetime -%} 416 | {%- set now = now() -%} 417 | {%- set diff = (now - handshake_time).total_seconds() -%} 418 | {{ diff < 180 }} 419 | {%- else -%} 420 | false 421 | {%- endif -%} 422 | ``` 423 | 424 | ### **Common Jinja2 Filters for Peers** 425 | 426 | ```yaml 427 | # Check if peers array exists and has items 428 | {%- if value_json.peers is defined and value_json.peers|length > 0 -%} 429 | 430 | # Get first peer safely 431 | {{ value_json.peers[0] | default({}) }} 432 | 433 | # Get last peer 434 | {{ value_json.peers[-1] }} 435 | 436 | # Filter peers with active handshake 437 | {{ value_json.peers | selectattr('latest_handshake', 'ne', 'Never') | list }} 438 | 439 | # Sort peers by uptime (descending) 440 | {{ value_json.peers | sort(attribute='uptime_seconds', reverse=true) | list }} 441 | 442 | # Get peer with highest traffic 443 | {{ value_json.peers | max(attribute='transfer_rx') }} 444 | 445 | # Format bytes to human-readable 446 | {{ (value_json.peers[0].transfer_rx / 1024 / 1024) | round(2) }} MB 447 | ``` 448 | 449 | ### **Using Peers Data in Other Contexts** 450 | 451 | When `value_json` is not available (e.g., in automations, template sensors, or scripts), you have several options: 452 | 453 | #### **Option 1: Create a dedicated REST sensor for full JSON** 454 | 455 | Create a separate REST sensor that stores the entire JSON response: 456 | 457 | ```yaml 458 | rest: 459 | - resource: "http://local-wireguard-client:51821" 460 | scan_interval: 30 461 | sensor: 462 | - name: "WireGuard API JSON" 463 | value_template: "{{ value_json | to_json }}" 464 | json_attributes: 465 | - status 466 | - peers 467 | - peer_count 468 | - total_traffic_rx 469 | - total_traffic_tx 470 | ``` 471 | 472 | Then access it in other templates: 473 | 474 | ```yaml 475 | # In automations or template sensors 476 | {%- set peers = state_attr('sensor.wireguard_api_json', 'peers') -%} 477 | {{ peers[0].endpoint if peers else 'No peers' }} 478 | ``` 479 | 480 | #### **Option 2: Use individual REST sensors** 481 | 482 | Access data from the individual REST sensors you've already configured: 483 | 484 | ```yaml 485 | # Get status 486 | {{ states('sensor.wireguard_status') }} 487 | 488 | # Get peer count 489 | {{ states('sensor.wireguard_peer_count') | int }} 490 | 491 | # Get uptime 492 | {{ states('sensor.wireguard_uptime') | int }} 493 | ``` 494 | 495 | #### **Option 3: Make a new HTTP request** 496 | 497 | Use `rest_command` or make a direct HTTP request in your template/automation: 498 | 499 | ```yaml 500 | # In a script or automation 501 | rest_command: 502 | wireguard_get_status: 503 | url: "http://local-wireguard-client:51821" 504 | method: GET 505 | ``` 506 | 507 | > **Note**: REST sensors with `value_template` only store the extracted value (e.g., `"connected"`), not the full JSON. To access the complete JSON, you need a dedicated sensor (Option 1) or make a new request (Option 3). 508 | 509 | ### **Tips** 510 | 511 | - **In REST sensor templates**: Use `value_json` directly (automatically available, see "Understanding `value_json`" section) 512 | - **In other contexts**: Access data via `state_attr()` or parse from sensor states (see "Using Peers Data in Other Contexts" section) 513 | - Always check if `peers` is defined and has items before accessing: `{%- if value_json.peers is defined and value_json.peers|length > 0 -%}` 514 | - Use `| default()` to provide fallback values 515 | - Use `| int` or `| float` to ensure numeric operations work correctly 516 | - Use `selectattr()` and `rejectattr()` filters to filter arrays 517 | - Use `| list` to convert generator results to lists when needed 518 | - For date comparisons, use `| as_datetime` to parse ISO date strings 519 | 520 | ## 🤖 Automation Examples 521 | 522 | ### **Auto-reconnect on Disconnection** 523 | 524 | ```yaml 525 | - id: vpn_auto_reconnect 526 | alias: "VPN Auto Reconnect" 527 | trigger: 528 | - platform: state 529 | entity_id: sensor.wireguard_status 530 | to: "disconnected" 531 | action: 532 | - service: rest_command.wireguard_reconnect 533 | - delay: 30 534 | - service: notify.persistent_notification 535 | data: 536 | message: "VPN reconnected automatically" 537 | title: "VPN Status" 538 | ``` 539 | 540 | ### **High Traffic Alert** 541 | 542 | ```yaml 543 | - id: vpn_high_traffic 544 | alias: "VPN High Traffic Alert" 545 | trigger: 546 | - platform: numeric_state 547 | entity_id: sensor.wireguard_traffic_sent 548 | above: 1000000000 # 1GB 549 | action: 550 | - service: notify.persistent_notification 551 | data: 552 | message: "High VPN traffic detected: {{ states('sensor.wireguard_traffic_sent') }}" 553 | title: "VPN Traffic Alert" 554 | ``` 555 | 556 | ### **Connection Test** 557 | 558 | ```yaml 559 | - id: vpn_connection_test 560 | alias: "VPN Connection Test" 561 | trigger: 562 | - platform: time 563 | at: "09:00:00" 564 | action: 565 | - service: rest_command.wireguard_test 566 | ``` 567 | 568 | ## 📱 Lovelace Card Example 569 | 570 | ```yaml 571 | type: entities 572 | title: "WireGuard Status" 573 | entities: 574 | - sensor.wireguard_status 575 | - sensor.wireguard_traffic_sent 576 | - sensor.wireguard_traffic_received 577 | - sensor.wireguard_peer_count 578 | - sensor.wireguard_uptime 579 | - sensor.wireguard_last_handshake 580 | ``` 581 | 582 | ## 🔍 Testing the API 583 | 584 | ### **Using curl:** 585 | 586 | ```bash 587 | # Get status 588 | curl http://local-wireguard-client:51821 589 | 590 | # Reconnect VPN 591 | curl http://local-wireguard-client:51821/reconnect 592 | 593 | # Restart WireGuard 594 | curl http://local-wireguard-client:51821/restart 595 | 596 | # Test WireGuard connection (comprehensive) 597 | curl http://local-wireguard-client:51821/test 598 | ``` 599 | 600 | ### **Using Home Assistant Developer Tools:** 601 | 602 | 1. Go to **Developer Tools** → **Services** 603 | 2. Call `rest_command.wireguard_reconnect` 604 | 3. Check the response in the logs 605 | 606 | ## 📝 Notes 607 | 608 | - **API URL**: `http://local-wireguard-client:51821` (port 51821) 609 | - **Replace URL**: Replace `local-wireguard-client` with your addon hostname 610 | - **Development vs Production**: In development environments, you may need to use `localhost` instead of the addon hostname 611 | - **Update Frequency**: Status updates every few seconds 612 | - **Error Handling**: All endpoints return JSON with success/error status 613 | - **Compatibility**: Works with Home Assistant 0.7.4+ (RESTful integration introduced in 0.7.4) 614 | - **Security**: API is only accessible from localhost by default 615 | - **Timeout**: Default 10 seconds, configurable via `timeout` parameter 616 | - **SSL**: Set `verify_ssl: false` for local addon communication 617 | - **Templates**: Use `value_json` to access JSON data in REST sensor templates (see "Understanding `value_json`" section above for details) 618 | - **Peers Array**: The `peers` field is a JSON array (not an object or string), use `peers[0]` to access the first peer (not `peers.peer_1`). See "Template Examples" section for detailed usage. 619 | - **Empty Handshake**: When no handshake has occurred, the API returns `"Never"` in the `latest_handshake` field (as documented in the Fields Description above). After configuring sensors, restart Home Assistant or reload the REST integration to see changes 620 | - **Troubleshooting**: If sensors show "Never" or are missing, verify that WireGuard is running and has peers configured. Check the API response with: `curl http://local-wireguard-client:51821 | jq '.peers'` to see if the peers array has data 621 | 622 | ## 🚀 Benefits 623 | 624 | - **Native Integration**: Uses Home Assistant's RESTful sensor platform 625 | - **Real-time Data**: Live updates of WireGuard status and statistics 626 | - **Service Actions**: Direct control over WireGuard operations 627 | - **Comprehensive Data**: Detailed peer information and traffic statistics 628 | - **Automation Ready**: Perfect for creating smart VPN automations 629 | --------------------------------------------------------------------------------