Netmaker add-on: pull prebuilt image from Gitea registry
Home Assistant add-on that joins the instance to a Netmaker network via netclient. Distributed as a repository add-on using a prebuilt arm64 image (gitea.savant.io/homeassistant/netmaker) to avoid local buildx. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
21
LICENSE.md
Normal file
21
LICENSE.md
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
# MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2026 Savant Coder
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
46
README.md
Normal file
46
README.md
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
# Home Assistant Community App: Netmaker
|
||||||
|
|
||||||
|
![Project Stage][project-stage-shield]
|
||||||
|
|
||||||
|
![Supports aarch64 Architecture][aarch64-shield]
|
||||||
|
![Supports amd64 Architecture][amd64-shield]
|
||||||
|
|
||||||
|
Join your Home Assistant instance to a Netmaker WireGuard network.
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
[Netmaker][netmaker] is a platform for creating and managing fast, secure
|
||||||
|
WireGuard networks. This app joins your Home Assistant instance to a Netmaker
|
||||||
|
network using the Netmaker client, `netclient`.
|
||||||
|
|
||||||
|
You provide an enrollment token generated in your Netmaker dashboard, and the
|
||||||
|
app registers your device and keeps the connection running in the background.
|
||||||
|
The host's WireGuard identity is persisted to the app's `/data` volume, so your
|
||||||
|
device remains the same node across restarts and updates.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
The installation of this app is straightforward and not different in comparison
|
||||||
|
to installing any other Home Assistant app.
|
||||||
|
|
||||||
|
1. Install this app.
|
||||||
|
1. Generate an enrollment token in your Netmaker dashboard (**Enrollment Keys**).
|
||||||
|
1. Set the token in the app's `enrollment_token` configuration option.
|
||||||
|
1. Start the "Netmaker" app.
|
||||||
|
1. Check the logs of the app to confirm it joined the network.
|
||||||
|
|
||||||
|
See the [documentation][docs] for full configuration details.
|
||||||
|
|
||||||
|
## Support
|
||||||
|
|
||||||
|
This add-on is a community port and is not affiliated with Netmaker.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT License — see [LICENSE.md](LICENSE.md).
|
||||||
|
|
||||||
|
[aarch64-shield]: https://img.shields.io/badge/aarch64-yes-green.svg
|
||||||
|
[amd64-shield]: https://img.shields.io/badge/amd64-yes-green.svg
|
||||||
|
[docs]: netmaker/DOCS.md
|
||||||
|
[netmaker]: https://www.netmaker.io/
|
||||||
|
[project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg
|
||||||
39
netmaker/.README.j2
Normal file
39
netmaker/.README.j2
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
# Home Assistant Community App: Netmaker
|
||||||
|
|
||||||
|
![Project Stage][project-stage-shield] ![Project Maintenance][maintenance-shield]
|
||||||
|
|
||||||
|
Join your Home Assistant instance to a Netmaker WireGuard network.
|
||||||
|
|
||||||
|
## About
|
||||||
|
|
||||||
|
Netmaker is a platform for creating and managing fast, secure WireGuard
|
||||||
|
networks. This app joins your Home Assistant instance to a Netmaker network
|
||||||
|
using the Netmaker client, `netclient`.
|
||||||
|
|
||||||
|
You provide an enrollment token from your Netmaker dashboard, and the app
|
||||||
|
registers your device and keeps the connection running in the background.
|
||||||
|
|
||||||
|
{% if channel == "edge" %}
|
||||||
|
## WARNING! THIS IS AN EDGE VERSION!
|
||||||
|
|
||||||
|
This Home Assistant Apps repository contains edge builds of apps.
|
||||||
|
Edge builds apps are based upon the latest development version.
|
||||||
|
|
||||||
|
- They may not work at all.
|
||||||
|
- They might stop working at any time.
|
||||||
|
- They could have a negative impact on your system.
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% if channel == "beta" %}
|
||||||
|
## WARNING! THIS IS A BETA VERSION!
|
||||||
|
|
||||||
|
This Home Assistant Apps repository contains beta releases of apps.
|
||||||
|
|
||||||
|
- They might stop working at any time.
|
||||||
|
- They could have a negative impact on your system.
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
[maintenance-shield]: https://img.shields.io/maintenance/yes/2026.svg
|
||||||
|
[project-stage-shield]: https://img.shields.io/badge/project%20stage-experimental-yellow.svg
|
||||||
|
[release-shield]: https://img.shields.io/badge/version-{{ version }}-blue.svg
|
||||||
|
[release]: {{ repo }}/tree/{{ version }}
|
||||||
95
netmaker/DOCS.md
Normal file
95
netmaker/DOCS.md
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Home Assistant Community App: Netmaker
|
||||||
|
|
||||||
|
Netmaker is a platform for creating and managing fast, secure WireGuard
|
||||||
|
networks. This app joins your Home Assistant instance to a Netmaker network
|
||||||
|
using the Netmaker client, `netclient`.
|
||||||
|
|
||||||
|
## Prerequisites
|
||||||
|
|
||||||
|
In order to use this app, you'll need access to a Netmaker server and an
|
||||||
|
**enrollment token** (also called an enrollment key) for the network you want
|
||||||
|
to join.
|
||||||
|
|
||||||
|
You can run your own Netmaker server or use a hosted one. See the Netmaker
|
||||||
|
documentation for details:
|
||||||
|
|
||||||
|
<https://docs.netmaker.io/>
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
1. Install this app.
|
||||||
|
1. Generate an enrollment token in your Netmaker dashboard, under
|
||||||
|
**Enrollment Keys**.
|
||||||
|
1. Open the app's **Configuration** tab and paste the token into the
|
||||||
|
`enrollment_token` option.
|
||||||
|
1. Start the "Netmaker" app.
|
||||||
|
1. Check the logs of the "Netmaker" app to confirm it joined the network.
|
||||||
|
1. Done!
|
||||||
|
|
||||||
|
The host's WireGuard identity and keys are stored on the app's persistent
|
||||||
|
`/data` volume, so your device stays the same node across restarts and updates.
|
||||||
|
|
||||||
|
## Configuration
|
||||||
|
|
||||||
|
Example configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
enrollment_token: "eyJ...your-token..."
|
||||||
|
log_level: info
|
||||||
|
host_name: ""
|
||||||
|
port: 0
|
||||||
|
interface: ""
|
||||||
|
endpoint: ""
|
||||||
|
endpoint6: ""
|
||||||
|
firewall: ""
|
||||||
|
```
|
||||||
|
|
||||||
|
### Option: `enrollment_token` (required)
|
||||||
|
|
||||||
|
The enrollment token used to join your Netmaker network. Generate one in your
|
||||||
|
Netmaker dashboard under **Enrollment Keys**. The token already contains the
|
||||||
|
address of your Netmaker server, so no separate server URL is needed.
|
||||||
|
|
||||||
|
### Option: `log_level`
|
||||||
|
|
||||||
|
Controls the verbosity of the netclient logs. One of `trace`, `debug`, `info`,
|
||||||
|
`notice`, `warning`, `error`, `fatal`. Use `debug` or `trace` when
|
||||||
|
troubleshooting connection problems.
|
||||||
|
|
||||||
|
### Option: `host_name`
|
||||||
|
|
||||||
|
Optional name to register this device as within your Netmaker network. Leave
|
||||||
|
empty to use the default host name.
|
||||||
|
|
||||||
|
### Option: `port`
|
||||||
|
|
||||||
|
Optional UDP port for WireGuard to listen on. Leave at `0` to let netclient
|
||||||
|
choose a port. If you set a specific port, expose the same port in the
|
||||||
|
**Network** section of the app.
|
||||||
|
|
||||||
|
### Option: `interface`
|
||||||
|
|
||||||
|
Optional name for the WireGuard network interface created on the host. On Linux
|
||||||
|
the name must start with `netmaker`. Leave empty for the default.
|
||||||
|
|
||||||
|
### Option: `endpoint` / `endpoint6`
|
||||||
|
|
||||||
|
Optional static public IPv4/IPv6 endpoint for this host. Leave empty to let
|
||||||
|
Netmaker detect the endpoint automatically.
|
||||||
|
|
||||||
|
### Option: `firewall`
|
||||||
|
|
||||||
|
Optional firewall backend to use for ACL enforcement: `iptables` or `nftables`.
|
||||||
|
Leave empty to let netclient auto-detect. If neither tool is available,
|
||||||
|
netclient runs without firewall/ACL enforcement (the connection still works).
|
||||||
|
|
||||||
|
## Network
|
||||||
|
|
||||||
|
This app runs on the host network. netclient uses WireGuard over UDP; by
|
||||||
|
default port `51821/udp` is used. If you configure a specific `port`, expose it
|
||||||
|
in the **Network** section.
|
||||||
|
|
||||||
|
## Leaving a network
|
||||||
|
|
||||||
|
To leave the Netmaker network, uninstall the app. To rotate the node's
|
||||||
|
identity, clear the app's data (uninstall and reinstall) before joining again.
|
||||||
63
netmaker/Dockerfile
Normal file
63
netmaker/Dockerfile
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
ARG BUILD_FROM=ghcr.io/hassio-addons/base/amd64:10.1.1
|
||||||
|
# hadolint ignore=DL3006
|
||||||
|
FROM ${BUILD_FROM}
|
||||||
|
|
||||||
|
# Set shell
|
||||||
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
|
||||||
|
# Setup base
|
||||||
|
ARG BUILD_ARCH=amd64
|
||||||
|
ARG NETCLIENT_VERSION="v1.6.0"
|
||||||
|
# hadolint ignore=DL3018
|
||||||
|
RUN \
|
||||||
|
apk add --no-cache \
|
||||||
|
iproute2 \
|
||||||
|
iptables \
|
||||||
|
ip6tables \
|
||||||
|
nftables \
|
||||||
|
wireguard-tools \
|
||||||
|
openresolv \
|
||||||
|
coreutils \
|
||||||
|
\
|
||||||
|
&& ln -sf /sbin/xtables-nft-multi /sbin/ip6tables \
|
||||||
|
&& ln -sf /sbin/xtables-nft-multi /sbin/iptables \
|
||||||
|
\
|
||||||
|
&& if [ "${BUILD_ARCH}" = "aarch64" ]; then ARCH="arm64"; fi \
|
||||||
|
&& if [ "${BUILD_ARCH}" = "amd64" ]; then ARCH="amd64"; fi \
|
||||||
|
\
|
||||||
|
&& curl -L -s -o /opt/netclient \
|
||||||
|
"https://github.com/gravitl/netclient/releases/download/${NETCLIENT_VERSION}/netclient-linux-${ARCH}" \
|
||||||
|
&& chmod 0755 /opt/netclient \
|
||||||
|
&& ln -sf /opt/netclient /usr/bin/netclient
|
||||||
|
|
||||||
|
# Copy root filesystem
|
||||||
|
COPY rootfs /
|
||||||
|
|
||||||
|
HEALTHCHECK \
|
||||||
|
CMD healthcheck
|
||||||
|
|
||||||
|
# Build arguments
|
||||||
|
ARG BUILD_ARCH
|
||||||
|
ARG BUILD_DATE
|
||||||
|
ARG BUILD_DESCRIPTION
|
||||||
|
ARG BUILD_NAME
|
||||||
|
ARG BUILD_REF
|
||||||
|
ARG BUILD_REPOSITORY
|
||||||
|
ARG BUILD_VERSION
|
||||||
|
|
||||||
|
# Labels
|
||||||
|
LABEL \
|
||||||
|
io.hass.name="${BUILD_NAME}" \
|
||||||
|
io.hass.description="${BUILD_DESCRIPTION}" \
|
||||||
|
io.hass.arch="${BUILD_ARCH}" \
|
||||||
|
io.hass.type="addon" \
|
||||||
|
io.hass.version=${BUILD_VERSION} \
|
||||||
|
org.opencontainers.image.title="${BUILD_NAME}" \
|
||||||
|
org.opencontainers.image.description="${BUILD_DESCRIPTION}" \
|
||||||
|
org.opencontainers.image.vendor="Home Assistant Community Apps" \
|
||||||
|
org.opencontainers.image.licenses="MIT" \
|
||||||
|
org.opencontainers.image.source="https://github.com/${BUILD_REPOSITORY}" \
|
||||||
|
org.opencontainers.image.documentation="https://github.com/${BUILD_REPOSITORY}/blob/main/README.md" \
|
||||||
|
org.opencontainers.image.created=${BUILD_DATE} \
|
||||||
|
org.opencontainers.image.revision=${BUILD_REF} \
|
||||||
|
org.opencontainers.image.version=${BUILD_VERSION}
|
||||||
45
netmaker/apparmor.txt
Normal file
45
netmaker/apparmor.txt
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
#include <tunables/global>
|
||||||
|
|
||||||
|
profile netmaker flags=(attach_disconnected,mediate_deleted) {
|
||||||
|
#include <abstractions/base>
|
||||||
|
|
||||||
|
# Capabilities
|
||||||
|
file,
|
||||||
|
signal (send) set=(kill,term,int,hup,cont),
|
||||||
|
|
||||||
|
# S6-Overlay
|
||||||
|
/init ix,
|
||||||
|
/bin/** ix,
|
||||||
|
/usr/bin/** ix,
|
||||||
|
/run/{s6,s6-rc*,service}/** ix,
|
||||||
|
/package/** ix,
|
||||||
|
/command/** ix,
|
||||||
|
/etc/services.d/** rwix,
|
||||||
|
/etc/cont-init.d/** rwix,
|
||||||
|
/etc/cont-finish.d/** rwix,
|
||||||
|
/run/{,**} rwk,
|
||||||
|
/dev/tty rw,
|
||||||
|
|
||||||
|
# Bashio
|
||||||
|
/usr/lib/bashio/** ix,
|
||||||
|
/tmp/** rwk,
|
||||||
|
|
||||||
|
# Access to options.json and other files within your app
|
||||||
|
/data/** rw,
|
||||||
|
|
||||||
|
# Netclient configuration/state (symlinked to /data/netclient)
|
||||||
|
/etc/netclient/{,**} rw,
|
||||||
|
|
||||||
|
# General - capabilities derived from complain mode analysis
|
||||||
|
capability net_bind_service,
|
||||||
|
capability dac_override,
|
||||||
|
capability fsetid,
|
||||||
|
capability setgid,
|
||||||
|
capability setuid,
|
||||||
|
capability chown,
|
||||||
|
capability kill,
|
||||||
|
|
||||||
|
# General - based on Config.yaml
|
||||||
|
capability net_admin,
|
||||||
|
capability net_raw,
|
||||||
|
}
|
||||||
4
netmaker/build.yaml
Normal file
4
netmaker/build.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
build_from:
|
||||||
|
aarch64: ghcr.io/hassio-addons/base/aarch64:10.1.1
|
||||||
|
amd64: ghcr.io/hassio-addons/base/amd64:10.1.1
|
||||||
46
netmaker/config.yaml
Normal file
46
netmaker/config.yaml
Normal file
@ -0,0 +1,46 @@
|
|||||||
|
---
|
||||||
|
name: Netmaker
|
||||||
|
version: dev
|
||||||
|
slug: netmaker
|
||||||
|
description: Join your Home Assistant instance to a Netmaker WireGuard network
|
||||||
|
url: https://github.com/hassio-addons/app-netmaker
|
||||||
|
image: gitea.savant.io/homeassistant/netmaker
|
||||||
|
startup: services
|
||||||
|
panel_icon: mdi:lan-connect
|
||||||
|
arch:
|
||||||
|
- aarch64
|
||||||
|
- amd64
|
||||||
|
init: false
|
||||||
|
hassio_api: true
|
||||||
|
host_network: true
|
||||||
|
privileged:
|
||||||
|
- NET_ADMIN
|
||||||
|
- NET_RAW
|
||||||
|
devices:
|
||||||
|
- /dev/net/tun
|
||||||
|
map:
|
||||||
|
- type: homeassistant_config
|
||||||
|
read_only: false
|
||||||
|
path: /config
|
||||||
|
- type: share
|
||||||
|
read_only: false
|
||||||
|
ports:
|
||||||
|
51821/udp: null
|
||||||
|
options:
|
||||||
|
enrollment_token: ""
|
||||||
|
log_level: info
|
||||||
|
host_name: ""
|
||||||
|
port: 0
|
||||||
|
interface: ""
|
||||||
|
endpoint: ""
|
||||||
|
endpoint6: ""
|
||||||
|
firewall: ""
|
||||||
|
schema:
|
||||||
|
enrollment_token: str
|
||||||
|
log_level: list(trace|debug|info|notice|warning|error|fatal)
|
||||||
|
host_name: str?
|
||||||
|
port: int(0,65535)?
|
||||||
|
interface: "match(^netmaker[a-zA-Z0-9_-]*$)?"
|
||||||
|
endpoint: str?
|
||||||
|
endpoint6: str?
|
||||||
|
firewall: list(iptables|nftables)?
|
||||||
14
netmaker/rootfs/command/with-contenv-merge
Normal file
14
netmaker/rootfs/command/with-contenv-merge
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
#!/command/execlineb -S0
|
||||||
|
|
||||||
|
ifelse
|
||||||
|
{
|
||||||
|
importas -D 0 S6_KEEP_ENV S6_KEEP_ENV
|
||||||
|
eltest 0${S6_KEEP_ENV} -eq 0
|
||||||
|
}
|
||||||
|
{
|
||||||
|
s6-envdir -Lf -- /run/s6/container_environment
|
||||||
|
exec
|
||||||
|
$@
|
||||||
|
}
|
||||||
|
|
||||||
|
$@
|
||||||
29
netmaker/rootfs/etc/s6-overlay/s6-rc.d/local-network/run
Normal file
29
netmaker/rootfs/etc/s6-overlay/s6-rc.d/local-network/run
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
#!/command/with-contenv bashio
|
||||||
|
# shellcheck shell=bash
|
||||||
|
export LOG_FD
|
||||||
|
# ==============================================================================
|
||||||
|
# Wait for the local network (default HA interface) to be ready
|
||||||
|
# ==============================================================================
|
||||||
|
|
||||||
|
readonly WAIT_DELAY=5 # 5s
|
||||||
|
readonly WAIT_COUNT=60 # 60*5s = 300s = 5m
|
||||||
|
|
||||||
|
declare wait_counter=0
|
||||||
|
|
||||||
|
# Some services need a working local network to function properly.
|
||||||
|
# They can mark this service as dependency, and wait for the successful startup.
|
||||||
|
|
||||||
|
# Until HA has no default interface, we wait a little
|
||||||
|
while ! bashio::api.supervisor GET "/network/interface/default/info" false &> /dev/null; do
|
||||||
|
if (( wait_counter++ == WAIT_COUNT )); then
|
||||||
|
# We emit only a warning to let the app start, maybe this is the only connection to access the device, better to start than not.
|
||||||
|
# Let netclient figure out a way to connect to the network if the local network is temporarily down.
|
||||||
|
bashio::log.warning "Local network (default Home Assistant interface) is unreachable"
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
bashio::log.info "Waiting for the local network (default Home Assistant interface) to be ready..."
|
||||||
|
sleep $WAIT_DELAY
|
||||||
|
done
|
||||||
|
if (( wait_counter != 0 && wait_counter <= WAIT_COUNT )); then
|
||||||
|
bashio::log.info "Local network is ready"
|
||||||
|
fi
|
||||||
@ -0,0 +1 @@
|
|||||||
|
oneshot
|
||||||
1
netmaker/rootfs/etc/s6-overlay/s6-rc.d/local-network/up
Normal file
1
netmaker/rootfs/etc/s6-overlay/s6-rc.d/local-network/up
Normal file
@ -0,0 +1 @@
|
|||||||
|
/etc/s6-overlay/s6-rc.d/local-network/run
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
#!/command/with-contenv bashio
|
||||||
|
# shellcheck shell=bash
|
||||||
|
export LOG_FD
|
||||||
|
# ==============================================================================
|
||||||
|
# Home Assistant Community App: Netmaker
|
||||||
|
# Cleans up on daemon exit and takes down the S6 supervision tree on failure
|
||||||
|
# ==============================================================================
|
||||||
|
readonly exit_code_container=$(</run/s6-linux-init-container-results/exitcode)
|
||||||
|
readonly exit_code_service="${1}"
|
||||||
|
readonly exit_code_signal="${2}"
|
||||||
|
readonly service="netclient-daemon"
|
||||||
|
|
||||||
|
declare iface
|
||||||
|
|
||||||
|
bashio::log.info \
|
||||||
|
"Service ${service} exited with code ${exit_code_service}" \
|
||||||
|
"(by signal ${exit_code_signal})"
|
||||||
|
|
||||||
|
# Replicate the cleanup netclient's container entrypoint performs on shutdown.
|
||||||
|
# The daemon is exec'd, so its own shell trap does not survive; do it here.
|
||||||
|
iface=$(bashio::config 'interface')
|
||||||
|
if bashio::var.is_empty "${iface}"; then
|
||||||
|
iface="netmaker"
|
||||||
|
fi
|
||||||
|
ip rule delete pref 3000 2> /dev/null || true
|
||||||
|
ip rule delete pref 2500 2> /dev/null || true
|
||||||
|
ip rule delete pref 2000 2> /dev/null || true
|
||||||
|
ip link del "${iface}" 2> /dev/null || true
|
||||||
|
|
||||||
|
if [[ "${exit_code_service}" -eq 256 ]]; then
|
||||||
|
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||||
|
echo $((128 + $exit_code_signal)) > /run/s6-linux-init-container-results/exitcode
|
||||||
|
fi
|
||||||
|
[[ "${exit_code_signal}" -eq 15 ]] && exec /run/s6/basedir/bin/halt
|
||||||
|
elif [[ "${exit_code_service}" -ne 0 ]]; then
|
||||||
|
if [[ "${exit_code_container}" -eq 0 ]]; then
|
||||||
|
echo "${exit_code_service}" > /run/s6-linux-init-container-results/exitcode
|
||||||
|
fi
|
||||||
|
exec /run/s6/basedir/bin/halt
|
||||||
|
fi
|
||||||
39
netmaker/rootfs/etc/s6-overlay/s6-rc.d/netclient-daemon/run
Normal file
39
netmaker/rootfs/etc/s6-overlay/s6-rc.d/netclient-daemon/run
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
#!/command/with-contenv bashio
|
||||||
|
# shellcheck shell=bash
|
||||||
|
export LOG_FD
|
||||||
|
# ==============================================================================
|
||||||
|
# Home Assistant Community App: Netmaker
|
||||||
|
# Runs the netclient daemon
|
||||||
|
# ==============================================================================
|
||||||
|
declare log_level
|
||||||
|
declare verbosity
|
||||||
|
|
||||||
|
bashio::log.info 'Starting Netmaker netclient...'
|
||||||
|
|
||||||
|
# Netclient stores its host identity, keys and node/server config under the
|
||||||
|
# hard-coded path /etc/netclient/. Persist it across restarts by pointing that
|
||||||
|
# path at the add-on's /data volume so the node is not re-registered each boot.
|
||||||
|
mkdir -p /data/netclient
|
||||||
|
if [ ! -L /etc/netclient ]; then
|
||||||
|
# Migrate anything an earlier image may have written to the real directory
|
||||||
|
if [ -d /etc/netclient ] && [ ! -d /data/netclient/.migrated ]; then
|
||||||
|
cp -a /etc/netclient/. /data/netclient/ 2>/dev/null || true
|
||||||
|
rm -rf /etc/netclient
|
||||||
|
fi
|
||||||
|
ln -sf /data/netclient /etc/netclient
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Map the app's log_level to netclient's numeric verbosity (0..4)
|
||||||
|
log_level=$(bashio::config 'log_level')
|
||||||
|
case "${log_level}" in
|
||||||
|
trace) verbosity=4 ;;
|
||||||
|
debug) verbosity=3 ;;
|
||||||
|
info|notice) verbosity=2 ;;
|
||||||
|
warning) verbosity=1 ;;
|
||||||
|
error|fatal) verbosity=0 ;;
|
||||||
|
*) verbosity=2 ;;
|
||||||
|
esac
|
||||||
|
|
||||||
|
# Run the daemon in the foreground so s6 supervises it directly.
|
||||||
|
# functions.Daemon() blocks and handles SIGTERM (clean shutdown) / SIGHUP (reload).
|
||||||
|
exec /opt/netclient daemon -v "${verbosity}"
|
||||||
@ -0,0 +1 @@
|
|||||||
|
longrun
|
||||||
68
netmaker/rootfs/etc/s6-overlay/s6-rc.d/netclient-join/run
Normal file
68
netmaker/rootfs/etc/s6-overlay/s6-rc.d/netclient-join/run
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
#!/command/with-contenv bashio
|
||||||
|
# shellcheck shell=bash
|
||||||
|
export LOG_FD
|
||||||
|
# ==============================================================================
|
||||||
|
# Home Assistant Community App: Netmaker
|
||||||
|
# Joins the netmaker network once, using an enrollment token
|
||||||
|
# ==============================================================================
|
||||||
|
declare token
|
||||||
|
declare -a options=()
|
||||||
|
declare value
|
||||||
|
declare wait_counter=0
|
||||||
|
|
||||||
|
readonly NODES_FILE="/etc/netclient/nodes.json"
|
||||||
|
readonly WAIT_DELAY=2
|
||||||
|
readonly WAIT_COUNT=30 # 30*2s = 60s
|
||||||
|
|
||||||
|
# An enrollment token is required for a headless join. The token is a base64
|
||||||
|
# blob that already embeds the netmaker server address, so nothing else is
|
||||||
|
# needed to reach the server.
|
||||||
|
token=$(bashio::config 'enrollment_token')
|
||||||
|
if bashio::var.is_empty "${token}"; then
|
||||||
|
bashio::exit.nok \
|
||||||
|
"No 'enrollment_token' configured. Generate one in your Netmaker dashboard" \
|
||||||
|
"(Enrollment Keys) and set it in this app's configuration."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# If this host is already registered to a network, don't join again. The daemon
|
||||||
|
# keeps nodes.json populated; a non-empty object means we have joined before.
|
||||||
|
if bashio::fs.file_exists "${NODES_FILE}" \
|
||||||
|
&& jq --exit-status 'length > 0' "${NODES_FILE}" > /dev/null 2>&1; then
|
||||||
|
bashio::log.info "netclient is already joined to a network, skipping join"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the optional join flags from configuration.
|
||||||
|
if value=$(bashio::config 'host_name') && bashio::var.has_value "${value}"; then
|
||||||
|
options+=(-o "${value}")
|
||||||
|
fi
|
||||||
|
if bashio::config.has_value 'port' && [[ "$(bashio::config 'port')" -ne 0 ]]; then
|
||||||
|
options+=(-p "$(bashio::config 'port')")
|
||||||
|
fi
|
||||||
|
if value=$(bashio::config 'interface') && bashio::var.has_value "${value}"; then
|
||||||
|
options+=(-I "${value}")
|
||||||
|
fi
|
||||||
|
if value=$(bashio::config 'endpoint') && bashio::var.has_value "${value}"; then
|
||||||
|
options+=(-e "${value}")
|
||||||
|
fi
|
||||||
|
if value=$(bashio::config 'endpoint6') && bashio::var.has_value "${value}"; then
|
||||||
|
options+=(-E "${value}")
|
||||||
|
fi
|
||||||
|
if value=$(bashio::config 'firewall') && bashio::var.has_value "${value}"; then
|
||||||
|
options+=(-f "${value}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Give the daemon a moment to initialize its config directory before joining.
|
||||||
|
while [ ! -d /etc/netclient ]; do
|
||||||
|
if (( wait_counter++ == WAIT_COUNT )); then
|
||||||
|
break
|
||||||
|
fi
|
||||||
|
sleep "${WAIT_DELAY}"
|
||||||
|
done
|
||||||
|
|
||||||
|
bashio::log.info "Joining netmaker network..."
|
||||||
|
if ! /opt/netclient join -t "${token}" "${options[@]}"; then
|
||||||
|
bashio::exit.nok "Failed to join the netmaker network"
|
||||||
|
fi
|
||||||
|
|
||||||
|
bashio::log.info "Successfully joined the netmaker network"
|
||||||
@ -0,0 +1 @@
|
|||||||
|
oneshot
|
||||||
1
netmaker/rootfs/etc/s6-overlay/s6-rc.d/netclient-join/up
Normal file
1
netmaker/rootfs/etc/s6-overlay/s6-rc.d/netclient-join/up
Normal file
@ -0,0 +1 @@
|
|||||||
|
/etc/s6-overlay/s6-rc.d/netclient-join/run
|
||||||
35
netmaker/rootfs/usr/bin/healthcheck
Normal file
35
netmaker/rootfs/usr/bin/healthcheck
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#!/command/with-contenv bashio
|
||||||
|
# shellcheck shell=bash
|
||||||
|
export LOG_FD
|
||||||
|
|
||||||
|
# Netclient has no queryable status socket, so this treats the app as:
|
||||||
|
# - healthy while it is still trying to join (within HEALTHCHECK_RESTART_TIMEOUT)
|
||||||
|
# - healthy once it has joined a network and the daemon process is running
|
||||||
|
# - unhealthy if it never manages to join within HEALTHCHECK_RESTART_TIMEOUT
|
||||||
|
|
||||||
|
# Redirect outputs to the log (&1 and &2 are not initialized by Docker)
|
||||||
|
exec &> /proc/1/fd/1
|
||||||
|
if [[ "${LOG_FD:-}" =~ ^[0-9]+$ ]]; then
|
||||||
|
eval "exec ${LOG_FD}>&1" || true
|
||||||
|
fi
|
||||||
|
|
||||||
|
readonly HEALTHCHECK_RESTART_TIMEOUT=3600 # 1 hour
|
||||||
|
readonly NODES_FILE="/etc/netclient/nodes.json"
|
||||||
|
|
||||||
|
# STARTED_TIMESTAMP is stored in contenv at /var/run/s6/container_environment
|
||||||
|
if ! bashio::var.has_value "${STARTED_TIMESTAMP-}"; then
|
||||||
|
STARTED_TIMESTAMP=$(date +"%s")
|
||||||
|
printf "%s" "${STARTED_TIMESTAMP}" > /var/run/s6/container_environment/STARTED_TIMESTAMP
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Healthy as soon as we are joined to at least one network.
|
||||||
|
if bashio::fs.file_exists "${NODES_FILE}" \
|
||||||
|
&& jq --exit-status 'length > 0' "${NODES_FILE}" > /dev/null 2>&1; then
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Not joined yet: stay healthy during the initial window, then fail so the
|
||||||
|
# container is restarted.
|
||||||
|
if (( $(date +"%s") - STARTED_TIMESTAMP > HEALTHCHECK_RESTART_TIMEOUT )); then
|
||||||
|
bashio::exit.nok "netclient has not joined a network within the expected time"
|
||||||
|
fi
|
||||||
45
netmaker/translations/en.yaml
Normal file
45
netmaker/translations/en.yaml
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
---
|
||||||
|
configuration:
|
||||||
|
enrollment_token:
|
||||||
|
name: Enrollment token
|
||||||
|
description: >-
|
||||||
|
The enrollment token (enrollment key) used to join your Netmaker network.
|
||||||
|
Generate one in your Netmaker dashboard under "Enrollment Keys" and paste
|
||||||
|
it here. The token already contains the address of your Netmaker server.
|
||||||
|
log_level:
|
||||||
|
name: Log level
|
||||||
|
description: >-
|
||||||
|
Controls the verbosity of the netclient logs. Set to "debug" or "trace"
|
||||||
|
when troubleshooting connection problems.
|
||||||
|
host_name:
|
||||||
|
name: Host name
|
||||||
|
description: >-
|
||||||
|
Optional name to register this device as within your Netmaker network.
|
||||||
|
Leave empty to use the default host name.
|
||||||
|
port:
|
||||||
|
name: WireGuard port
|
||||||
|
description: >-
|
||||||
|
Optional UDP port for WireGuard to listen on. Leave at 0 to let netclient
|
||||||
|
choose a port. If you set this, expose the same port in the Network section.
|
||||||
|
interface:
|
||||||
|
name: Interface name
|
||||||
|
description: >-
|
||||||
|
Optional name for the WireGuard network interface created on the host.
|
||||||
|
On Linux the name must start with "netmaker". Leave empty for the default.
|
||||||
|
endpoint:
|
||||||
|
name: Static endpoint (IPv4)
|
||||||
|
description: >-
|
||||||
|
Optional static public IPv4 endpoint for this host. Leave empty to let
|
||||||
|
Netmaker detect the endpoint automatically.
|
||||||
|
endpoint6:
|
||||||
|
name: Static endpoint (IPv6)
|
||||||
|
description: >-
|
||||||
|
Optional static public IPv6 endpoint for this host. Leave empty to let
|
||||||
|
Netmaker detect the endpoint automatically.
|
||||||
|
firewall:
|
||||||
|
name: Firewall
|
||||||
|
description: >-
|
||||||
|
Optional firewall backend to use for ACL enforcement (iptables or
|
||||||
|
nftables). Leave empty to let netclient auto-detect.
|
||||||
|
network:
|
||||||
|
51821/udp: WireGuard port used by netclient
|
||||||
4
repository.yaml
Normal file
4
repository.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
---
|
||||||
|
name: Savant Home Assistant Add-ons
|
||||||
|
url: https://gitea.savant.io/homeassistant/hassio-addons
|
||||||
|
maintainer: Savant Coder <ramirez.philip@gmail.com>
|
||||||
Reference in New Issue
Block a user