diff --git a/.goreleaser-linux-amd64.yml b/.goreleaser-linux-amd64.yml
index aeaf8074..9c3766d0 100644
--- a/.goreleaser-linux-amd64.yml
+++ b/.goreleaser-linux-amd64.yml
@@ -109,6 +109,9 @@ nfpms:
- dst: /lib/systemd/system/
src: ./nfpm/zrok-share.service
+ - dst: /usr/lib/systemd/user/
+ src: ./nfpm/zrok-share@.service
+
- dst: /etc/systemd/system/zrok-share.service.d/override.conf
src: ./nfpm/zrok-share.service.override.conf
diff --git a/.goreleaser-linux-arm64.yml b/.goreleaser-linux-arm64.yml
index d63b87fa..e4c5454f 100644
--- a/.goreleaser-linux-arm64.yml
+++ b/.goreleaser-linux-arm64.yml
@@ -113,6 +113,9 @@ nfpms:
- dst: /lib/systemd/system/
src: ./nfpm/zrok-share.service
+ - dst: /usr/lib/systemd/user/
+ src: ./nfpm/zrok-share@.service
+
- dst: /etc/systemd/system/zrok-share.service.d/override.conf
src: ./nfpm/zrok-share.service.override.conf
diff --git a/.goreleaser-linux-armel.yml b/.goreleaser-linux-armel.yml
index 80bdbc26..748360dd 100644
--- a/.goreleaser-linux-armel.yml
+++ b/.goreleaser-linux-armel.yml
@@ -117,6 +117,12 @@ nfpms:
- dst: /lib/systemd/system/
src: ./nfpm/zrok-share.service
+ - dst: /usr/lib/systemd/user/
+ src: ./nfpm/zrok-share@.service
+
+ - dst: /usr/lib/systemd/user/
+ src: ./nfpm/zrok-share@.service
+
- dst: /etc/systemd/system/zrok-share.service.d/override.conf
src: ./nfpm/zrok-share.service.override.conf
diff --git a/.goreleaser-linux-armhf.yml b/.goreleaser-linux-armhf.yml
index c7275d81..67518309 100644
--- a/.goreleaser-linux-armhf.yml
+++ b/.goreleaser-linux-armhf.yml
@@ -115,6 +115,9 @@ nfpms:
- dst: /lib/systemd/system/
src: ./nfpm/zrok-share.service
+ - dst: /usr/lib/systemd/user/
+ src: ./nfpm/zrok-share@.service
+
- dst: /etc/systemd/system/zrok-share.service.d/override.conf
src: ./nfpm/zrok-share.service.override.conf
diff --git a/docs/guides/frontdoor.mdx b/docs/guides/frontdoor.mdx
index 5e19aa43..dec7bd5f 100644
--- a/docs/guides/frontdoor.mdx
+++ b/docs/guides/frontdoor.mdx
@@ -46,7 +46,25 @@ the detected OS of the visitor's browser */}
On Linux, zrok frontdoor is implemented natively as a system service provided by the `zrok-share` DEB or RPM package.
-
+## Goal
+
+Proxy a reserved public subdomain to a backend target with an always-on Linux system service.
+
+## How it Works
+
+The `zrok-share` package creates a `zrok-share.service` unit in systemd. The administrator edits the service's configuration file to specify the:
+
+1. zrok account token
+1. target URL or files to be shared and backend mode, e.g. `proxy`
+1. authentication options, if wanted
+
+When the service starts it will:
+
+1. enable the zrok environment unless `/var/lib/zrok-share/.zrok/environment.json` exists
+1. reserve a public subdomain for the service unless `/var/lib/zrok-share/.zrok/reserved.json` exists
+1. start sharing the target specified as `ZROK_TARGET` in the environment file
+
+
diff --git a/nfpm/zrok-share.bash b/nfpm/zrok-share.bash
index d46edb8f..6f5bd9a3 100644
--- a/nfpm/zrok-share.bash
+++ b/nfpm/zrok-share.bash
@@ -36,28 +36,20 @@ fi
echo "DEBUG: zrok state directory is ${HOME}/.zrok"
: "${ZROK_SHARE_RESERVED:=true}"
-
echo "DEBUG: ZROK_SHARE_RESERVED=${ZROK_SHARE_RESERVED}"
-if (( $# )); then
- if [[ -s "$1" ]]; then
+while (( $# )); do
+ if [[ "${1:0:1}" == @ ]]; then
+ ZROK_INSTANCE="${1:1}"
+ shift
+ elif [[ -s "$1" ]]; then
echo "INFO: reading share configuration from $1"
source "$1"
shift
- else
- echo "ERROR: '$1' is empty or not readable" >&2
- exit 1
fi
-else
- # TODO: consider defining a default environment file
- # if [[ -s /opt/openziti/etc/zrok.env ]]; then
- # source /opt/openziti/etc/zrok.env
- # else
- # echo "ERROR: need /opt/openziti/etc/zrok.env or filename argument to read share configuration" >&2
- # exit 1
- # fi
- echo "INFO: reading share configuration from environment variables"
-fi
+done
+
+ZROK_RESERVATION_FILE="${HOME}/.zrok/reserved${ZROK_INSTANCE:+@${ZROK_INSTANCE}}.json"
[[ -n "${ZROK_TARGET:-}" ]] || {
echo "ERROR: ZROK_TARGET is not defined." >&2
@@ -70,14 +62,14 @@ if [[ "${ZROK_FRONTEND_MODE:-}" == temp-public ]]; then
ZROK_CMD="share public"
elif [[ "${ZROK_FRONTEND_MODE:-}" == temp-private ]]; then
ZROK_CMD="share private"
-elif [[ -s ~/.zrok/reserved.json ]]; then
- ZROK_RESERVED_TOKEN="$(jq -r '.token' ~/.zrok/reserved.json 2>/dev/null)"
- if [[ -z "${ZROK_RESERVED_TOKEN}" || "${ZROK_RESERVED_TOKEN}" == null ]]; then
- echo "ERROR: invalid reserved.json: '$(jq -c . ~/.zrok/reserved.json)'" >&2
+elif [[ -s "${ZROK_RESERVATION_FILE}" ]]; then
+ ZROK_RESERVATION_TOKEN="$(jq -r '.token' "${ZROK_RESERVATION_FILE}" 2>/dev/null)"
+ if [[ -z "${ZROK_RESERVATION_TOKEN}" || "${ZROK_RESERVATION_TOKEN}" == null ]]; then
+ echo "ERROR: invalid reservation file: '$(jq -c . "${ZROK_RESERVATION_FILE}")'" >&2
exit 1
else
- echo "INFO: zrok backend is already reserved: ${ZROK_RESERVED_TOKEN}"
- ZROK_CMD="${ZROK_RESERVED_TOKEN} ${ZROK_TARGET}"
+ echo "INFO: zrok backend is already reserved: ${ZROK_RESERVATION_TOKEN}"
+ ZROK_CMD="${ZROK_RESERVATION_TOKEN} ${ZROK_TARGET}"
if [[ "${ZROK_SHARE_RESERVED}" == true ]]; then
exec_share_reserved ${ZROK_CMD}
else
@@ -208,30 +200,30 @@ if [[ "${ZROK_FRONTEND_MODE:-}" =~ ^temp- ]]; then
exec_with_common_opts ${ZROK_CMD}
else
# reserve and continue
- zrok ${ZROK_CMD} > ~/.zrok/reserved.json
+ zrok ${ZROK_CMD} > "${ZROK_RESERVATION_FILE}"
# share the reserved backend target until exit
- if ! [[ -s ~/.zrok/reserved.json ]]; then
- echo "ERROR: empty or missing $(realpath ~/.zrok)/reserved.json" >&2
+ if ! [[ -s "${ZROK_RESERVATION_FILE}" ]]; then
+ echo "ERROR: empty or missing $(realpath "${ZROK_RESERVATION_FILE}")" >&2
exit 1
- elif ! jq . < ~/.zrok/reserved.json &>/dev/null; then
- echo "ERROR: invalid JSON in $(realpath ~/.zrok)/reserved.json" >&2
+ elif ! jq . < "${ZROK_RESERVATION_FILE}" &>/dev/null; then
+ echo "ERROR: invalid JSON in $(realpath "${ZROK_RESERVATION_FILE}")" >&2
exit 1
else
if [[ "${ZROK_FRONTEND_MODE:-}" == reserved-public ]]; then
- ZROK_PUBLIC_URLS=$(jq -cr '.frontend_endpoints' ~/.zrok/reserved.json 2>/dev/null)
+ ZROK_PUBLIC_URLS=$(jq -cr '.frontend_endpoints' "${ZROK_RESERVATION_FILE}" 2>/dev/null)
if [[ -z "${ZROK_PUBLIC_URLS}" || "${ZROK_PUBLIC_URLS}" == null ]]; then
- echo "ERROR: frontend endpoints not defined in $(realpath ~/.zrok)/reserved.json" >&2
+ echo "ERROR: frontend endpoints not defined in $(realpath "${ZROK_RESERVATION_FILE}")" >&2
exit 1
else
echo "INFO: zrok public URLs: ${ZROK_PUBLIC_URLS}"
fi
fi
- ZROK_RESERVED_TOKEN=$(jq -r '.token' ~/.zrok/reserved.json 2>/dev/null)
- if [[ -z "${ZROK_RESERVED_TOKEN}" || "${ZROK_RESERVED_TOKEN}" == null ]]; then
- echo "ERROR: zrok reservation token not defined in $(realpath ~/.zrok)/reserved.json" >&2
+ ZROK_RESERVATION_TOKEN=$(jq -r '.token' "${ZROK_RESERVATION_FILE}" 2>/dev/null)
+ if [[ -z "${ZROK_RESERVATION_TOKEN}" || "${ZROK_RESERVATION_TOKEN}" == null ]]; then
+ echo "ERROR: zrok reservation token not defined in $(realpath "${ZROK_RESERVATION_FILE}")" >&2
exit 1
fi
- ZROK_CMD="${ZROK_RESERVED_TOKEN} ${ZROK_TARGET}"
+ ZROK_CMD="${ZROK_RESERVATION_TOKEN} ${ZROK_TARGET}"
if [[ "${ZROK_SHARE_RESERVED}" == true ]]; then
exec_share_reserved ${ZROK_CMD}
else
diff --git a/nfpm/zrok-share.env b/nfpm/zrok-share.env
index c1b0b8f4..c2c67ac3 100644
--- a/nfpm/zrok-share.env
+++ b/nfpm/zrok-share.env
@@ -3,8 +3,10 @@
#
## ZROK ENVIRONMENT
#
-# You MUST enable a zrok environment by setting the environment enable token here. This file must be readable by
-# 'other'. Obtain the enable token from the zrok console after accepting your invitation and creating a password.
+# The variables in this section are not used by user units, i.e., systemctl --user, because it is assumed the user's
+# environment in ~/.zrok is already enabled. The variables in this section are required by system-wide service units.
+# For system services, you MUST enable a zrok environment by setting the account token here. This file must
+# be readable by 'other'. Obtain the account token from the zrok console.
#
# WARNING: changing these values has no effect if /var/lib/zrok-share/.zrok/environment.json exists. Remove that file to
# enable a new environment and /var/lib/zrok-share/.zrok/reserved.json to provision a new frontend URL for the specified
diff --git a/nfpm/zrok-share.service b/nfpm/zrok-share.service
index f8fb4072..fdb528f3 100644
--- a/nfpm/zrok-share.service
+++ b/nfpm/zrok-share.service
@@ -1,5 +1,5 @@
[Unit]
-Description=zrok reserved public share service
+Description=zrok share service
After=network-online.target
[Service]
@@ -7,8 +7,9 @@ Type=simple
DynamicUser=yes
StateDirectory=zrok-share
UMask=0007
-ExecStartPre=/opt/openziti/bin/zrok-enable.bash /opt/openziti/etc/zrok/zrok-share.env
-ExecStart=/opt/openziti/bin/zrok-share.bash /opt/openziti/etc/zrok/zrok-share.env
+EnvironmentFile=/opt/openziti/etc/zrok/zrok-share.env
+ExecStartPre=/opt/openziti/bin/zrok-enable.bash
+ExecStart=/opt/openziti/bin/zrok-share.bash
Restart=always
RestartSec=3
diff --git a/nfpm/zrok-share.service.override.conf b/nfpm/zrok-share.service.override.conf
index 57f4a796..9cf870a9 100644
--- a/nfpm/zrok-share.service.override.conf
+++ b/nfpm/zrok-share.service.override.conf
@@ -6,5 +6,3 @@
# allow adding tun device and IP routes and iptables rules; required when ZROK_BACKEND_MODE=vpn
# AmbientCapabilities=CAP_NET_ADMIN
-
-# you must run 'systemctl daemon-reload' after modifying this file
diff --git a/nfpm/zrok-share@.service b/nfpm/zrok-share@.service
new file mode 100644
index 00000000..ad242bf6
--- /dev/null
+++ b/nfpm/zrok-share@.service
@@ -0,0 +1,17 @@
+
+# /usr/lib/systemd/user/zrok-share@.service
+
+[Unit]
+Description=zrok share user service unit @%i
+After=network-online.target
+
+[Service]
+Type=simple
+UMask=0007
+EnvironmentFile=%h/.zrok/zrok-share@%i.env
+ExecStart=/opt/openziti/bin/zrok-share.bash @%i
+Restart=always
+RestartSec=3
+
+[Install]
+WantedBy=multi-user.target