From b44f690ab2e67cbd7efb41b25429af4599274426 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Thu, 2 Jan 2025 00:45:01 -0500 Subject: [PATCH 1/2] add systemd user unit instance template --- .goreleaser-linux-amd64.yml | 3 ++ .goreleaser-linux-arm64.yml | 3 ++ .goreleaser-linux-armel.yml | 6 +++ .goreleaser-linux-armhf.yml | 3 ++ docs/guides/frontdoor.mdx | 20 ++++++++- nfpm/zrok-share.bash | 58 ++++++++++++--------------- nfpm/zrok-share.env | 6 ++- nfpm/zrok-share.service | 7 ++-- nfpm/zrok-share.service.override.conf | 2 - nfpm/zrok-share@.service | 17 ++++++++ 10 files changed, 84 insertions(+), 41 deletions(-) create mode 100644 nfpm/zrok-share@.service 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 From af0367e36b584660cfe0e89cd33338b8110d16a4 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Thu, 2 Jan 2025 01:52:50 -0500 Subject: [PATCH 2/2] document and announce Linux user shares --- .goreleaser-linux-armel.yml | 3 -- CHANGELOG.md | 4 +- docs/guides/_frontdoor-linux.mdx | 36 +--------------- docs/guides/_linux-share-install.mdx | 17 ++++++++ docs/guides/linux-user-share/_category_.json | 8 ++++ docs/guides/linux-user-share/index.mdx | 44 ++++++++++++++++++++ 6 files changed, 74 insertions(+), 38 deletions(-) create mode 100644 docs/guides/_linux-share-install.mdx create mode 100644 docs/guides/linux-user-share/_category_.json create mode 100644 docs/guides/linux-user-share/index.mdx diff --git a/.goreleaser-linux-armel.yml b/.goreleaser-linux-armel.yml index 748360dd..86526dec 100644 --- a/.goreleaser-linux-armel.yml +++ b/.goreleaser-linux-armel.yml @@ -120,9 +120,6 @@ nfpms: - 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/CHANGELOG.md b/CHANGELOG.md index 74ff2044..3f997524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,9 +4,11 @@ FIX: Docker share examples had incorrect default path for zrok environment mountpoint +FIX: Clarify how to use DNS providers like Route53 with the zrok Docker instance sample. + CHANGE: Use port 80 for the default Ziti API endpoint in the zrok Docker instance sample (https://github.com/openziti/zrok/issues/793). -FIX: Clarify how to use DNS providers like Route53 with the zrok Docker instance sample. +FEATURE: Linux service template for systemd user units (https://github.com/openziti/zrok/pull/818) ## v0.4.45 diff --git a/docs/guides/_frontdoor-linux.mdx b/docs/guides/_frontdoor-linux.mdx index 23977db9..ac3b6fe7 100644 --- a/docs/guides/_frontdoor-linux.mdx +++ b/docs/guides/_frontdoor-linux.mdx @@ -1,42 +1,10 @@ +import LinuxShareInstall from './_linux-share-install.mdx' import AnsibleRepoSetup from './install/_ansible_repo_setup.yaml' import ConcatenateYamlSnippets from '@site/src/components/cat-yaml.jsx' -## 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 environment enable 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 - ## Installation -1. Set up `zrok`'s Linux package repository by following [the Linux install guide](/guides/install/linux.mdx#install-zrok-from-the-repository), or run this one-liner to complete the repo setup and install packages. - - ```bash - curl -sSLf https://get.openziti.io/install.bash \ - | sudo bash -s zrok-share - ``` - -1. If you set up the repository by following the guide, then also install the `zrok-share` package. This package provides the systemd service. - - ```bash title="Ubuntu, Debian" - sudo apt install zrok-share - ``` - - ```bash title="Fedora, Rocky" - sudo dnf install zrok-share - ``` +
Ansible Playbook diff --git a/docs/guides/_linux-share-install.mdx b/docs/guides/_linux-share-install.mdx new file mode 100644 index 00000000..acea45b2 --- /dev/null +++ b/docs/guides/_linux-share-install.mdx @@ -0,0 +1,17 @@ + +1. Set up `zrok`'s Linux package repository by following [the Linux install guide](/guides/install/linux.mdx#install-zrok-from-the-repository), or run this one-liner to complete the repo setup and install packages. + + ```bash + curl -sSLf https://get.openziti.io/install.bash \ + | sudo bash -s zrok-share + ``` + +1. If you set up the repository by following the guide, then also install the `zrok-share` package. This package provides the systemd service. + + ```bash title="Ubuntu, Debian" + sudo apt install zrok-share + ``` + + ```bash title="Fedora, Rocky" + sudo dnf install zrok-share + ``` diff --git a/docs/guides/linux-user-share/_category_.json b/docs/guides/linux-user-share/_category_.json new file mode 100644 index 00000000..16892730 --- /dev/null +++ b/docs/guides/linux-user-share/_category_.json @@ -0,0 +1,8 @@ +{ + "label": "Linux User Share", + "position": 40, + "link": { + "type": "doc", + "id": "guides/linux-user-share/index" + } +} diff --git a/docs/guides/linux-user-share/index.mdx b/docs/guides/linux-user-share/index.mdx new file mode 100644 index 00000000..6b9679e2 --- /dev/null +++ b/docs/guides/linux-user-share/index.mdx @@ -0,0 +1,44 @@ +--- +title: Linux User Share +--- + +import LinuxShareInstall from '/../docs/guides/_linux-share-install.mdx' + +## Overview + +You can run any number of zrok share services as `systemd --user` units with your Linux user's zrok environment in `~/.zrok`. This is like [zrok frontdoor](/guides/frontdoor.mdx) except that frontdoor is a system service managed by root separately from your user's login. Linux user shares, Linux system services, and Docker shares all use the same configuration environment variables. + +## Install the Linux Package + +The package provides the `zrok` executable and service unit template. + + + +## Create a User Share Configuration File + +Substitute a name for your instance in place of `my-instance` in the following example. To avoid character escaping problems, use only letters, numbers, hyphens, and underscores in the instance name, not spaces or other special characters. + +```bash +ZROK_INSTANCE="my-instance" +cp /opt/openziti/etc/zrok/zrok-share.env ~/.zrok/zrok-share@${ZROK_INSTANCE}.env +``` + +## Edit the User Share Configuration File + +Edit the configuration file in `~/.zrok/zrok-share@${ZROK_INSTANCE}.env` as you would for [zrok frontdoor](/guides/frontdoor.mdx), except ignore the first section "ZROK ENVIRONMENT" because user shares re-use `~/.zrok` and do not need a separate zrok environment. + +## Start the User Share Service + +```bash +systemctl --user enable --now zrok-share@${ZROK_INSTANCE}.service +``` + +## Check the User Share Journal + +```bash +journalctl --user -lfu zrok-share@${ZROK_INSTANCE}.service +``` + +## Add Another User Share + +To create another user share, choose another instance name, copy the `zrok-share.env` file, edit the configuration file, and start the service.