Merge branch 'main' into v1.next_canary
2
.github/workflows/node-sdk.yml
vendored
@ -72,7 +72,7 @@ jobs:
|
|||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
registry-url: 'https://registry.npmjs.org'
|
registry-url: https://registry.npmjs.org
|
||||||
|
|
||||||
- name: Build the zrok NodeJS-SDK
|
- name: Build the zrok NodeJS-SDK
|
||||||
shell: bash
|
shell: bash
|
||||||
|
1
.github/workflows/promote-downstreams.yml
vendored
@ -63,6 +63,7 @@ jobs:
|
|||||||
package_name:
|
package_name:
|
||||||
- zrok
|
- zrok
|
||||||
- zrok-share
|
- zrok-share
|
||||||
|
- zrok-agent
|
||||||
arch:
|
arch:
|
||||||
- deb: amd64
|
- deb: amd64
|
||||||
rpm: x86_64
|
rpm: x86_64
|
||||||
|
4
.github/workflows/release.yml
vendored
@ -103,7 +103,7 @@ jobs:
|
|||||||
else
|
else
|
||||||
SEMVER_PRE=${SEMVER#*-}
|
SEMVER_PRE=${SEMVER#*-}
|
||||||
fi
|
fi
|
||||||
for PAX in zrok{,-share}; do
|
for PAX in zrok{,-share,-agent}; do
|
||||||
_pattern="./dist/${PAX}-${SEMVER_CORE}${SEMVER_PRE:+~${SEMVER_PRE}}*.${ARCH}.rpm"
|
_pattern="./dist/${PAX}-${SEMVER_CORE}${SEMVER_PRE:+~${SEMVER_PRE}}*.${ARCH}.rpm"
|
||||||
if ! compgen -G "$_pattern" > /dev/null; then
|
if ! compgen -G "$_pattern" > /dev/null; then
|
||||||
echo "ERROR: No RPM files found matching pattern '${_pattern}'" >&2
|
echo "ERROR: No RPM files found matching pattern '${_pattern}'" >&2
|
||||||
@ -139,7 +139,7 @@ jobs:
|
|||||||
else
|
else
|
||||||
SEMVER_PRE=${SEMVER#*-}
|
SEMVER_PRE=${SEMVER#*-}
|
||||||
fi
|
fi
|
||||||
for PAX in zrok{,-share}; do
|
for PAX in zrok{,-share,-agent}; do
|
||||||
_pattern="./dist/${PAX}_${SEMVER_CORE}${SEMVER_PRE:+~${SEMVER_PRE}}*_${ARCH}.deb"
|
_pattern="./dist/${PAX}_${SEMVER_CORE}${SEMVER_PRE:+~${SEMVER_PRE}}*_${ARCH}.deb"
|
||||||
if ! compgen -G "$_pattern" > /dev/null; then
|
if ! compgen -G "$_pattern" > /dev/null; then
|
||||||
echo "ERROR: No DEB files found matching pattern '${_pattern}'" >&2
|
echo "ERROR: No DEB files found matching pattern '${_pattern}'" >&2
|
||||||
|
@ -23,7 +23,7 @@ nfpms:
|
|||||||
license: Apache 2.0
|
license: Apache 2.0
|
||||||
|
|
||||||
# Build IDs for the builds you want to create NFPM packages for.
|
# Build IDs for the builds you want to create NFPM packages for.
|
||||||
builds:
|
ids:
|
||||||
- zrok-amd64
|
- zrok-amd64
|
||||||
|
|
||||||
# Formats to be generated.
|
# Formats to be generated.
|
||||||
@ -137,3 +137,56 @@ nfpms:
|
|||||||
- dst: /opt/openziti/etc/zrok/
|
- dst: /opt/openziti/etc/zrok/
|
||||||
src: ./etc/caddy/multiple_upstream.Caddyfile
|
src: ./etc/caddy/multiple_upstream.Caddyfile
|
||||||
type: config|noreplace
|
type: config|noreplace
|
||||||
|
|
||||||
|
- package_name: zrok-agent
|
||||||
|
id: zrok-agent
|
||||||
|
vendor: NetFoundry
|
||||||
|
homepage: https://zrok.io/
|
||||||
|
maintainer: support@zrok.io
|
||||||
|
description: |
|
||||||
|
This package provides zrok-agent.service. Enable your zrok account on this device with "zrok enable". Run
|
||||||
|
"systemctl enable --user --now zrok-agent.service" to enable the service for the current user and visit the agent
|
||||||
|
UI by running "zrok agent console".
|
||||||
|
license: Apache 2.0
|
||||||
|
|
||||||
|
# do not bundle the built binaries, only supporting files
|
||||||
|
meta: true
|
||||||
|
|
||||||
|
# Formats to be generated.
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
|
||||||
|
# {{ .ConventionalFileName }} satisfies the RPM name convention.
|
||||||
|
file_name_template: "{{ .ConventionalFileName }}"
|
||||||
|
|
||||||
|
# Umask to be used on files without explicit mode set. (overridable)
|
||||||
|
umask: 0o002
|
||||||
|
|
||||||
|
# Package version within this release version.
|
||||||
|
release: 1
|
||||||
|
|
||||||
|
# Section.
|
||||||
|
section: default
|
||||||
|
|
||||||
|
# Priority.
|
||||||
|
priority: optional
|
||||||
|
|
||||||
|
# GoReleaser will automatically add the binaries here
|
||||||
|
dependencies:
|
||||||
|
- zrok
|
||||||
|
|
||||||
|
# this allows users to satisfy the requirement for jq another way, not with the package manager, e.g.
|
||||||
|
# apt install --no-recommends zrok-share
|
||||||
|
recommends: []
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
# yum and dnf do not automatically install "weak deps" aka "recommends", so we need to add them as a dependency
|
||||||
|
rpm:
|
||||||
|
dependencies:
|
||||||
|
- zrok
|
||||||
|
|
||||||
|
# Contents to add to the package.
|
||||||
|
contents:
|
||||||
|
- dst: /usr/lib/systemd/user/
|
||||||
|
src: ./nfpm/zrok-agent.service
|
||||||
|
@ -27,7 +27,7 @@ nfpms:
|
|||||||
license: Apache 2.0
|
license: Apache 2.0
|
||||||
|
|
||||||
# Build IDs for the builds you want to create NFPM packages for.
|
# Build IDs for the builds you want to create NFPM packages for.
|
||||||
builds:
|
ids:
|
||||||
- zrok-armv8
|
- zrok-armv8
|
||||||
|
|
||||||
# Formats to be generated.
|
# Formats to be generated.
|
||||||
@ -141,3 +141,56 @@ nfpms:
|
|||||||
- dst: /opt/openziti/etc/zrok/
|
- dst: /opt/openziti/etc/zrok/
|
||||||
src: ./etc/caddy/multiple_upstream.Caddyfile
|
src: ./etc/caddy/multiple_upstream.Caddyfile
|
||||||
type: config|noreplace
|
type: config|noreplace
|
||||||
|
|
||||||
|
- package_name: zrok-agent
|
||||||
|
id: zrok-agent
|
||||||
|
vendor: NetFoundry
|
||||||
|
homepage: https://zrok.io/
|
||||||
|
maintainer: support@zrok.io
|
||||||
|
description: |
|
||||||
|
This package provides zrok-agent.service. Enable your zrok account on this device with "zrok enable". Run
|
||||||
|
"systemctl enable --user --now zrok-agent.service" to enable the service for the current user and visit the agent
|
||||||
|
UI by running "zrok agent console".
|
||||||
|
license: Apache 2.0
|
||||||
|
|
||||||
|
# do not bundle the built binaries, only supporting files
|
||||||
|
meta: true
|
||||||
|
|
||||||
|
# Formats to be generated.
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
|
||||||
|
# {{ .ConventionalFileName }} satisfies the RPM name convention.
|
||||||
|
file_name_template: "{{ .ConventionalFileName }}"
|
||||||
|
|
||||||
|
# Umask to be used on files without explicit mode set. (overridable)
|
||||||
|
umask: 0o002
|
||||||
|
|
||||||
|
# Package version within this release version.
|
||||||
|
release: 1
|
||||||
|
|
||||||
|
# Section.
|
||||||
|
section: default
|
||||||
|
|
||||||
|
# Priority.
|
||||||
|
priority: optional
|
||||||
|
|
||||||
|
# GoReleaser will automatically add the binaries here
|
||||||
|
dependencies:
|
||||||
|
- zrok
|
||||||
|
|
||||||
|
# this allows users to satisfy the requirement for jq another way, not with the package manager, e.g.
|
||||||
|
# apt install --no-recommends zrok-share
|
||||||
|
recommends: []
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
# yum and dnf do not automatically install "weak deps" aka "recommends", so we need to add them as a dependency
|
||||||
|
rpm:
|
||||||
|
dependencies:
|
||||||
|
- zrok
|
||||||
|
|
||||||
|
# Contents to add to the package.
|
||||||
|
contents:
|
||||||
|
- dst: /usr/lib/systemd/user/
|
||||||
|
src: ./nfpm/zrok-agent.service
|
||||||
|
@ -31,7 +31,7 @@ nfpms:
|
|||||||
license: Apache 2.0
|
license: Apache 2.0
|
||||||
|
|
||||||
# Build IDs for the builds you want to create NFPM packages for.
|
# Build IDs for the builds you want to create NFPM packages for.
|
||||||
builds:
|
ids:
|
||||||
- zrok-armel
|
- zrok-armel
|
||||||
|
|
||||||
# Formats to be generated.
|
# Formats to be generated.
|
||||||
@ -145,3 +145,56 @@ nfpms:
|
|||||||
- dst: /opt/openziti/etc/zrok/
|
- dst: /opt/openziti/etc/zrok/
|
||||||
src: ./etc/caddy/multiple_upstream.Caddyfile
|
src: ./etc/caddy/multiple_upstream.Caddyfile
|
||||||
type: config|noreplace
|
type: config|noreplace
|
||||||
|
|
||||||
|
- package_name: zrok-agent
|
||||||
|
id: zrok-agent
|
||||||
|
vendor: NetFoundry
|
||||||
|
homepage: https://zrok.io/
|
||||||
|
maintainer: support@zrok.io
|
||||||
|
description: |
|
||||||
|
This package provides zrok-agent.service. Enable your zrok account on this device with "zrok enable". Run
|
||||||
|
"systemctl enable --user --now zrok-agent.service" to enable the service for the current user and visit the agent
|
||||||
|
UI by running "zrok agent console".
|
||||||
|
license: Apache 2.0
|
||||||
|
|
||||||
|
# do not bundle the built binaries, only supporting files
|
||||||
|
meta: true
|
||||||
|
|
||||||
|
# Formats to be generated.
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
|
||||||
|
# {{ .ConventionalFileName }} satisfies the RPM name convention.
|
||||||
|
file_name_template: "{{ .ConventionalFileName }}"
|
||||||
|
|
||||||
|
# Umask to be used on files without explicit mode set. (overridable)
|
||||||
|
umask: 0o002
|
||||||
|
|
||||||
|
# Package version within this release version.
|
||||||
|
release: 1
|
||||||
|
|
||||||
|
# Section.
|
||||||
|
section: default
|
||||||
|
|
||||||
|
# Priority.
|
||||||
|
priority: optional
|
||||||
|
|
||||||
|
# GoReleaser will automatically add the binaries here
|
||||||
|
dependencies:
|
||||||
|
- zrok
|
||||||
|
|
||||||
|
# this allows users to satisfy the requirement for jq another way, not with the package manager, e.g.
|
||||||
|
# apt install --no-recommends zrok-share
|
||||||
|
recommends: []
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
# yum and dnf do not automatically install "weak deps" aka "recommends", so we need to add them as a dependency
|
||||||
|
rpm:
|
||||||
|
dependencies:
|
||||||
|
- zrok
|
||||||
|
|
||||||
|
# Contents to add to the package.
|
||||||
|
contents:
|
||||||
|
- dst: /usr/lib/systemd/user/
|
||||||
|
src: ./nfpm/zrok-agent.service
|
||||||
|
@ -29,7 +29,7 @@ nfpms:
|
|||||||
license: Apache 2.0
|
license: Apache 2.0
|
||||||
|
|
||||||
# Build IDs for the builds you want to create NFPM packages for.
|
# Build IDs for the builds you want to create NFPM packages for.
|
||||||
builds:
|
ids:
|
||||||
- zrok-armhf
|
- zrok-armhf
|
||||||
|
|
||||||
# Formats to be generated.
|
# Formats to be generated.
|
||||||
@ -143,3 +143,56 @@ nfpms:
|
|||||||
- dst: /opt/openziti/etc/zrok/
|
- dst: /opt/openziti/etc/zrok/
|
||||||
src: ./etc/caddy/multiple_upstream.Caddyfile
|
src: ./etc/caddy/multiple_upstream.Caddyfile
|
||||||
type: config|noreplace
|
type: config|noreplace
|
||||||
|
|
||||||
|
- package_name: zrok-agent
|
||||||
|
id: zrok-agent
|
||||||
|
vendor: NetFoundry
|
||||||
|
homepage: https://zrok.io/
|
||||||
|
maintainer: support@zrok.io
|
||||||
|
description: |
|
||||||
|
This package provides zrok-agent.service. Enable your zrok account on this device with "zrok enable". Run
|
||||||
|
"systemctl enable --user --now zrok-agent.service" to enable the service for the current user and visit the agent
|
||||||
|
UI by running "zrok agent console".
|
||||||
|
license: Apache 2.0
|
||||||
|
|
||||||
|
# do not bundle the built binaries, only supporting files
|
||||||
|
meta: true
|
||||||
|
|
||||||
|
# Formats to be generated.
|
||||||
|
formats:
|
||||||
|
- deb
|
||||||
|
- rpm
|
||||||
|
|
||||||
|
# {{ .ConventionalFileName }} satisfies the RPM name convention.
|
||||||
|
file_name_template: "{{ .ConventionalFileName }}"
|
||||||
|
|
||||||
|
# Umask to be used on files without explicit mode set. (overridable)
|
||||||
|
umask: 0o002
|
||||||
|
|
||||||
|
# Package version within this release version.
|
||||||
|
release: 1
|
||||||
|
|
||||||
|
# Section.
|
||||||
|
section: default
|
||||||
|
|
||||||
|
# Priority.
|
||||||
|
priority: optional
|
||||||
|
|
||||||
|
# GoReleaser will automatically add the binaries here
|
||||||
|
dependencies:
|
||||||
|
- zrok
|
||||||
|
|
||||||
|
# this allows users to satisfy the requirement for jq another way, not with the package manager, e.g.
|
||||||
|
# apt install --no-recommends zrok-share
|
||||||
|
recommends: []
|
||||||
|
|
||||||
|
overrides:
|
||||||
|
# yum and dnf do not automatically install "weak deps" aka "recommends", so we need to add them as a dependency
|
||||||
|
rpm:
|
||||||
|
dependencies:
|
||||||
|
- zrok
|
||||||
|
|
||||||
|
# Contents to add to the package.
|
||||||
|
contents:
|
||||||
|
- dst: /usr/lib/systemd/user/
|
||||||
|
src: ./nfpm/zrok-agent.service
|
||||||
|
12
CHANGELOG.md
@ -1,5 +1,17 @@
|
|||||||
# CHANGELOG
|
# CHANGELOG
|
||||||
|
|
||||||
|
## v1.0.1
|
||||||
|
|
||||||
|
FEATURE: The zrok Agent now persists private accesses and reserved shares between executions. Any `zrok access private` instances or `zrok share reserved` instances created using the agent are now persisted to a registry stored in `${HOME}/.zrok`. When restarting the agent these accesses and reserved shares are re-created from the data in this registry (https://github.com/openziti/zrok/pull/922)
|
||||||
|
|
||||||
|
FEATURE: zrok-agent Linux package runs the agent as a user service (https://github.com/openziti/zrok/issues/883)
|
||||||
|
|
||||||
|
CHANGE: Updated the "Getting Started" guide to be slightly more streamlined and reflect the `v1.0` changes (https://github.com/openziti/zrok/issues/877)
|
||||||
|
|
||||||
|
CHANGE: let the Docker instance set the Caddy HTTPS port (https://github.com/openziti/zrok/pull/920)
|
||||||
|
|
||||||
|
CHANGE: Add Traefik option for TLS termination in the Docker instance (https://github.com/openziti/zrok/issues/808)
|
||||||
|
|
||||||
## v1.0.0
|
## v1.0.0
|
||||||
|
|
||||||
MAJOR RELEASE: zrok reaches version 1.0.0!
|
MAJOR RELEASE: zrok reaches version 1.0.0!
|
||||||
|
20
README.md
@ -1,12 +1,20 @@
|
|||||||

|

|
||||||
|
|
||||||
|
**Note: If you upgrade to `v1.0.0` and you receive an error message like this:**
|
||||||
|
|
||||||
|
```
|
||||||
|
[ERROR]: unable to create share (error getting zrok client: client version error accessing api endpoint 'https://api.zrok.io': [POST /clientVersionCheck] clientVersionCheck (status 404): {}: [POST /clientVersionCheck] clientVersionCheck (status 404): {})
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the command `zrok rebase apiEndpoint https://api-v1.zrok.io/` to update your environment for the `v1.0.0` release.
|
||||||
|
|
||||||
|
## Your Secure Internet Sharing Perimeter
|
||||||
|
|
||||||
`zrok` is a next-generation, peer-to-peer sharing platform built on top of [OpenZiti](https://docs.openziti.io/docs/learn/introduction/), a programmable zero-trust network overlay. `zrok` is a _Ziti Native Application_.
|
`zrok` is a next-generation, peer-to-peer sharing platform built on top of [OpenZiti](https://docs.openziti.io/docs/learn/introduction/), a programmable zero-trust network overlay. `zrok` is a _Ziti Native Application_.
|
||||||
|
|
||||||
`zrok` facilitates sharing resources both publicly and privately. Public sharing allows you to share `zrok` resources with non-`zrok` users over the public internet. Private sharing allows you to directly share your resources peer-to-peer with other `zrok` users without changing your security or firewall settings.
|
`zrok` facilitates both public and private sharing. Public sharing allows you to share securely with non-`zrok` users over the public internet. Private sharing allows you to directly share peer-to-peer with other `zrok` users. No security or firewall changes are required for either type of sharing. No inbound connectivity is required. The OpenZiti overlay provides peer-to-peer connectivity without IP addresses, and employs end-to-end encryption for world-class security.
|
||||||
|
|
||||||
Like other offerings in this space, `zrok` allows users to share tunnels for HTTP, TCP and UDP network resources. `zrok` additionally allows users to easily and rapidly share files, web content, and custom resources in a peer-to-peer manner.
|
Like other offerings in this space, `zrok` allows users to create tunnels for HTTP, TCP and UDP network resources. `zrok` additionally allows users to easily and rapidly share files, web content, and custom resources in a peer-to-peer manner.
|
||||||
|
|
||||||
`zrok` is an extensible platform for sharing. Initially we're targeting technical users. Super-simple sharing for end users is planned and in the backlog.
|
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
@ -15,7 +23,7 @@ Like other offerings in this space, `zrok` allows users to share tunnels for HTT
|
|||||||
You can be up and sharing using the `zrok.io` service in minutes. Here is a synopsis of what's involved:
|
You can be up and sharing using the `zrok.io` service in minutes. Here is a synopsis of what's involved:
|
||||||
|
|
||||||
* [Install the package or download the binary for your platform](https://docs.zrok.io/docs/guides/install/).
|
* [Install the package or download the binary for your platform](https://docs.zrok.io/docs/guides/install/).
|
||||||
* `zrok invite` to create an account with the service
|
* `zrok invite` to create an account with the service (use the [NetFoundry hosted zrok.io service](https://docs.zrok.io/docs/getting-started/))
|
||||||
* `zrok enable` to enable your shell environment for sharing with the service
|
* `zrok enable` to enable your shell environment for sharing with the service
|
||||||
|
|
||||||
### And then... sharing...
|
### And then... sharing...
|
||||||
|
40
RELEASING.md
@ -35,3 +35,43 @@ Pre-release version strings must contain exactly one hyphen, and may not contain
|
|||||||
## Rolling Back Downstreams
|
## Rolling Back Downstreams
|
||||||
|
|
||||||
The concepts, tools, and procedures for managing existing downstream artifacts in Artifactory and Docker Hub are identical for zrok and ziti. Here's the [RELEASING.md document for ziti](https://github.com/openziti/ziti/blob/main/RELEASING.md#rolling-back-downstreams).
|
The concepts, tools, and procedures for managing existing downstream artifacts in Artifactory and Docker Hub are identical for zrok and ziti. Here's the [RELEASING.md document for ziti](https://github.com/openziti/ziti/blob/main/RELEASING.md#rolling-back-downstreams).
|
||||||
|
|
||||||
|
## Updating the Homebrew Formula
|
||||||
|
|
||||||
|
[`zrok.rb`](https://github.com/Homebrew/homebrew-core/blob/master/Formula/z/zrok.rb) is a Ruby script in `Homebrew/homebrew-core` that defines the build procedure for the `zrok` binary. The Homebrew workflow triggered by the "released" event in GitHub sends a pull request to update the zrok formula. Usually, the only differences are the HTTP URL of the release's source code archive and it's checksum. It's also necessary to send a PR for the Ruby script when the zrok build procedure changes ([example PR](https://github.com/Homebrew/homebrew-core/pull/210917)).
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Clone the Homebrew/homebrew-core repository
|
||||||
|
brew tap --force homebrew/core
|
||||||
|
cd $(brew --repo homebrew/core)
|
||||||
|
|
||||||
|
# if already cloned then fetch
|
||||||
|
git fetch origin master
|
||||||
|
|
||||||
|
# if you're patching a PR HEAD that failed to build, then branch from that PR branch's HEAD
|
||||||
|
git fetch origin pull/<pr-number>/head # e.g. `git fetch origin pull/1234/head`
|
||||||
|
git checkout -b fix-homebrew FETCH_HEAD
|
||||||
|
|
||||||
|
# Disable API-based installation to enable local build and testing
|
||||||
|
export HOMEBREW_NO_INSTALL_FROM_API=1
|
||||||
|
|
||||||
|
# Edit ./Formula/z/zrok.rb
|
||||||
|
brew edit zrok
|
||||||
|
|
||||||
|
# Build from source
|
||||||
|
brew install --verbose --formula --build-bottle zrok
|
||||||
|
|
||||||
|
# run the test section of the formula
|
||||||
|
brew test zrok
|
||||||
|
|
||||||
|
# Audit the the formula
|
||||||
|
brew audit --strict zrok
|
||||||
|
|
||||||
|
# Check formula styles
|
||||||
|
brew style zrok
|
||||||
|
```
|
||||||
|
|
||||||
|
Finally, if correcting a failed GitHub Actions check on a PR based on Homebrew/homebrew-core master branch, then push commits as the ziti-ci user with the "gh_ci_key" SSH key to update the PR. A valid commit message is just the formula name and new version string, e.g., "zrok 1.0.0".
|
||||||
|
|
||||||
|
[Homebrew Documentation](https://docs.brew.sh/FAQ#can-i-edit-formulae-myself)
|
||||||
|
|
||||||
|
@ -6,6 +6,16 @@ import (
|
|||||||
"github.com/openziti/zrok/cmd/zrok/subordinate"
|
"github.com/openziti/zrok/cmd/zrok/subordinate"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type AccessPrivateRequest struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
BindAddress string `json:"bind_address"`
|
||||||
|
AutoMode bool `json:"auto_mode"`
|
||||||
|
AutoAddress string `json:"auto_address"`
|
||||||
|
AutoStartPort uint16 `json:"auto_start_port"`
|
||||||
|
AutoEndPort uint16 `json:"auto_end_port"`
|
||||||
|
ResponseHeaders []string `json:"response_headers"`
|
||||||
|
}
|
||||||
|
|
||||||
type access struct {
|
type access struct {
|
||||||
frontendToken string
|
frontendToken string
|
||||||
token string
|
token string
|
||||||
@ -16,6 +26,8 @@ type access struct {
|
|||||||
autoEndPort uint16
|
autoEndPort uint16
|
||||||
responseHeaders []string
|
responseHeaders []string
|
||||||
|
|
||||||
|
request *AccessPrivateRequest
|
||||||
|
|
||||||
process *proctree.Child
|
process *proctree.Child
|
||||||
sub *subordinate.MessageHandler
|
sub *subordinate.MessageHandler
|
||||||
|
|
||||||
|
@ -12,14 +12,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPrivateRequest) (*agentGrpc.AccessPrivateResponse, error) {
|
func (a *Agent) AccessPrivate(req *AccessPrivateRequest) (frontendToken string, err error) {
|
||||||
root, err := environment.LoadRoot()
|
root, err := environment.LoadRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !root.IsEnabled() {
|
if !root.IsEnabled() {
|
||||||
return nil, errors.New("unable to load environment; did you 'zrok enable'?")
|
return "", errors.New("unable to load environment; did you 'zrok enable'?")
|
||||||
}
|
}
|
||||||
|
|
||||||
accCmd := []string{os.Args[0], "access", "private", "--subordinate", "-b", req.BindAddress, req.Token}
|
accCmd := []string{os.Args[0], "access", "private", "--subordinate", "-b", req.BindAddress, req.Token}
|
||||||
@ -34,11 +34,12 @@ func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPr
|
|||||||
bindAddress: req.BindAddress,
|
bindAddress: req.BindAddress,
|
||||||
autoMode: req.AutoMode,
|
autoMode: req.AutoMode,
|
||||||
autoAddress: req.AutoAddress,
|
autoAddress: req.AutoAddress,
|
||||||
autoStartPort: uint16(req.AutoStartPort),
|
autoStartPort: req.AutoStartPort,
|
||||||
autoEndPort: uint16(req.AutoEndPort),
|
autoEndPort: req.AutoEndPort,
|
||||||
responseHeaders: req.ResponseHeaders,
|
responseHeaders: req.ResponseHeaders,
|
||||||
|
request: req,
|
||||||
sub: subordinate.NewMessageHandler(),
|
sub: subordinate.NewMessageHandler(),
|
||||||
agent: i.agent,
|
agent: a,
|
||||||
}
|
}
|
||||||
acc.sub.MessageHandler = func(msg subordinate.Message) {
|
acc.sub.MessageHandler = func(msg subordinate.Message) {
|
||||||
logrus.Info(msg)
|
logrus.Info(msg)
|
||||||
@ -74,20 +75,36 @@ func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPr
|
|||||||
|
|
||||||
acc.process, err = proctree.StartChild(acc.sub.Tail, accCmd...)
|
acc.process, err = proctree.StartChild(acc.sub.Tail, accCmd...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
<-acc.sub.BootComplete
|
<-acc.sub.BootComplete
|
||||||
|
|
||||||
if bootErr == nil {
|
if bootErr == nil {
|
||||||
go acc.monitor()
|
go acc.monitor()
|
||||||
i.agent.addAccess <- acc
|
a.addAccess <- acc
|
||||||
return &agentGrpc.AccessPrivateResponse{FrontendToken: acc.frontendToken}, nil
|
return acc.frontendToken, nil
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if err := proctree.WaitChild(acc.process); err != nil {
|
if err := proctree.WaitChild(acc.process); err != nil {
|
||||||
logrus.Errorf("error joining: %v", err)
|
logrus.Errorf("error joining: %v", err)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unable to start access: %v", bootErr)
|
return "", fmt.Errorf("unable to start access: %v", bootErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *agentGrpcImpl) AccessPrivate(_ context.Context, req *agentGrpc.AccessPrivateRequest) (*agentGrpc.AccessPrivateResponse, error) {
|
||||||
|
if frontendToken, err := i.agent.AccessPrivate(&AccessPrivateRequest{
|
||||||
|
Token: req.Token,
|
||||||
|
BindAddress: req.BindAddress,
|
||||||
|
AutoMode: req.AutoMode,
|
||||||
|
AutoAddress: req.AutoAddress,
|
||||||
|
AutoStartPort: uint16(req.AutoStartPort),
|
||||||
|
AutoEndPort: uint16(req.AutoEndPort),
|
||||||
|
ResponseHeaders: req.ResponseHeaders,
|
||||||
|
}); err == nil {
|
||||||
|
return &agentGrpc.AccessPrivateResponse{FrontendToken: frontendToken}, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ type Agent struct {
|
|||||||
accesses map[string]*access
|
accesses map[string]*access
|
||||||
addAccess chan *access
|
addAccess chan *access
|
||||||
rmAccess chan *access
|
rmAccess chan *access
|
||||||
|
persistRegistry bool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAgent(cfg *AgentConfig, root env_core.Root) (*Agent, error) {
|
func NewAgent(cfg *AgentConfig, root env_core.Root) (*Agent, error) {
|
||||||
@ -67,6 +68,12 @@ func (a *Agent) Run() error {
|
|||||||
go a.manager()
|
go a.manager()
|
||||||
go a.gateway(a.cfg)
|
go a.gateway(a.cfg)
|
||||||
|
|
||||||
|
a.persistRegistry = false
|
||||||
|
if err := a.ReloadRegistry(); err != nil {
|
||||||
|
logrus.Errorf("error reloading registry '%v'", err)
|
||||||
|
}
|
||||||
|
a.persistRegistry = true
|
||||||
|
|
||||||
srv := grpc.NewServer()
|
srv := grpc.NewServer()
|
||||||
agentGrpc.RegisterAgentServer(srv, &agentGrpcImpl{agent: a})
|
agentGrpc.RegisterAgentServer(srv, &agentGrpcImpl{agent: a})
|
||||||
if err := srv.Serve(l); err != nil {
|
if err := srv.Serve(l); err != nil {
|
||||||
@ -79,6 +86,7 @@ func (a *Agent) Run() error {
|
|||||||
func (a *Agent) Shutdown() {
|
func (a *Agent) Shutdown() {
|
||||||
logrus.Infof("stopping")
|
logrus.Infof("stopping")
|
||||||
|
|
||||||
|
a.persistRegistry = false
|
||||||
if err := os.Remove(a.agentSocket); err != nil {
|
if err := os.Remove(a.agentSocket); err != nil {
|
||||||
logrus.Warnf("unable to remove agent socket: %v", err)
|
logrus.Warnf("unable to remove agent socket: %v", err)
|
||||||
}
|
}
|
||||||
@ -96,6 +104,60 @@ func (a *Agent) Config() *AgentConfig {
|
|||||||
return a.cfg
|
return a.cfg
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *Agent) ReloadRegistry() error {
|
||||||
|
registryPath, err := a.root.AgentRegistry()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
registry, err := LoadRegistry(registryPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
logrus.Infof("loaded %d reserved shares, %d accesses", len(registry.ReservedShares), len(registry.PrivateAccesses))
|
||||||
|
for _, req := range registry.ReservedShares {
|
||||||
|
if resp, err := a.ShareReserved(req); err == nil {
|
||||||
|
logrus.Infof("restarted reserved share '%v' -> '%v'", req, resp)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("error restarting reserved share '%v': %v", req, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, req := range registry.PrivateAccesses {
|
||||||
|
if resp, err := a.AccessPrivate(req); err == nil {
|
||||||
|
logrus.Infof("restarted private access '%v' -> '%v'", req, resp)
|
||||||
|
} else {
|
||||||
|
logrus.Errorf("error restarting private access '%v': %v", req, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logrus.Infof("reload complete")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Agent) SaveRegistry() error {
|
||||||
|
r := &Registry{}
|
||||||
|
for _, shr := range a.shares {
|
||||||
|
if shr.request != nil {
|
||||||
|
switch shr.request.(type) {
|
||||||
|
case *ShareReservedRequest:
|
||||||
|
logrus.Infof("persisting reserved share '%v'", shr.token)
|
||||||
|
r.ReservedShares = append(r.ReservedShares, shr.request.(*ShareReservedRequest))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, acc := range a.accesses {
|
||||||
|
if acc.request != nil {
|
||||||
|
r.PrivateAccesses = append(r.PrivateAccesses, acc.request)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
registryPath, err := a.root.AgentRegistry()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := r.Save(registryPath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func (a *Agent) gateway(cfg *AgentConfig) {
|
func (a *Agent) gateway(cfg *AgentConfig) {
|
||||||
logrus.Info("started")
|
logrus.Info("started")
|
||||||
defer logrus.Warn("exited")
|
defer logrus.Warn("exited")
|
||||||
@ -132,6 +194,12 @@ func (a *Agent) manager() {
|
|||||||
logrus.Infof("adding new share '%v'", inShare.token)
|
logrus.Infof("adding new share '%v'", inShare.token)
|
||||||
a.shares[inShare.token] = inShare
|
a.shares[inShare.token] = inShare
|
||||||
|
|
||||||
|
if a.persistRegistry {
|
||||||
|
if err := a.SaveRegistry(); err != nil {
|
||||||
|
logrus.Errorf("unable to persist registry: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case outShare := <-a.rmShare:
|
case outShare := <-a.rmShare:
|
||||||
if shr, found := a.shares[outShare.token]; found {
|
if shr, found := a.shares[outShare.token]; found {
|
||||||
logrus.Infof("removing share '%v'", shr.token)
|
logrus.Infof("removing share '%v'", shr.token)
|
||||||
@ -147,6 +215,13 @@ func (a *Agent) manager() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
delete(a.shares, shr.token)
|
delete(a.shares, shr.token)
|
||||||
|
|
||||||
|
if a.persistRegistry {
|
||||||
|
if err := a.SaveRegistry(); err != nil {
|
||||||
|
logrus.Errorf("unable to persist registry: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
logrus.Debug("skipping unidentified (orphaned) share removal")
|
logrus.Debug("skipping unidentified (orphaned) share removal")
|
||||||
}
|
}
|
||||||
@ -155,6 +230,12 @@ func (a *Agent) manager() {
|
|||||||
logrus.Infof("adding new access '%v'", inAccess.frontendToken)
|
logrus.Infof("adding new access '%v'", inAccess.frontendToken)
|
||||||
a.accesses[inAccess.frontendToken] = inAccess
|
a.accesses[inAccess.frontendToken] = inAccess
|
||||||
|
|
||||||
|
if a.persistRegistry {
|
||||||
|
if err := a.SaveRegistry(); err != nil {
|
||||||
|
logrus.Errorf("unable to persist registry: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
case outAccess := <-a.rmAccess:
|
case outAccess := <-a.rmAccess:
|
||||||
if acc, found := a.accesses[outAccess.frontendToken]; found {
|
if acc, found := a.accesses[outAccess.frontendToken]; found {
|
||||||
logrus.Infof("removing access '%v'", acc.frontendToken)
|
logrus.Infof("removing access '%v'", acc.frontendToken)
|
||||||
@ -168,6 +249,13 @@ func (a *Agent) manager() {
|
|||||||
logrus.Errorf("error deleting access '%v': %v", acc.frontendToken, err)
|
logrus.Errorf("error deleting access '%v': %v", acc.frontendToken, err)
|
||||||
}
|
}
|
||||||
delete(a.accesses, acc.frontendToken)
|
delete(a.accesses, acc.frontendToken)
|
||||||
|
|
||||||
|
if a.persistRegistry {
|
||||||
|
if err := a.SaveRegistry(); err != nil {
|
||||||
|
logrus.Errorf("unable to persist registry: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
logrus.Debug("skipping unidentified (orphaned) access removal")
|
logrus.Debug("skipping unidentified (orphaned) access removal")
|
||||||
}
|
}
|
||||||
|
48
agent/agentUi/package-lock.json
generated
@ -201,26 +201,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helpers": {
|
"node_modules/@babel/helpers": {
|
||||||
"version": "7.26.0",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
|
||||||
"integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
|
"integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.25.9",
|
"@babel/template": "^7.27.0",
|
||||||
"@babel/types": "^7.26.0"
|
"@babel/types": "^7.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.26.2",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
|
||||||
"integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
|
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.26.0"
|
"@babel/types": "^7.27.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@ -262,9 +262,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.26.0",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
|
||||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerator-runtime": "^0.14.0"
|
"regenerator-runtime": "^0.14.0"
|
||||||
@ -274,14 +274,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.25.9",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
|
||||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.25.9",
|
"@babel/code-frame": "^7.26.2",
|
||||||
"@babel/parser": "^7.25.9",
|
"@babel/parser": "^7.27.0",
|
||||||
"@babel/types": "^7.25.9"
|
"@babel/types": "^7.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@ -315,9 +315,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.26.0",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||||
"integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
|
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.25.9",
|
"@babel/helper-string-parser": "^7.25.9",
|
||||||
@ -3873,9 +3873,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.4.tgz",
|
||||||
"integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==",
|
"integrity": "sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
39
agent/registry.go
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
package agent
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
const RegistryV = "1"
|
||||||
|
|
||||||
|
type Registry struct {
|
||||||
|
V string `json:"v"`
|
||||||
|
ReservedShares []*ShareReservedRequest `json:"reserved_shares"`
|
||||||
|
PrivateAccesses []*AccessPrivateRequest `json:"private_accesses"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadRegistry(path string) (*Registry, error) {
|
||||||
|
data, err := os.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
r := &Registry{}
|
||||||
|
if err := json.Unmarshal(data, r); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if r.V != RegistryV {
|
||||||
|
return nil, fmt.Errorf("invalid registry version '%v'; expected '%v", r.V, RegistryV)
|
||||||
|
}
|
||||||
|
return r, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Registry) Save(path string) error {
|
||||||
|
r.V = RegistryV
|
||||||
|
data, err := json.MarshalIndent(r, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return os.WriteFile(path, data, 0644)
|
||||||
|
}
|
@ -7,13 +7,16 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *agentGrpcImpl) ReleaseAccess(_ context.Context, req *agentGrpc.ReleaseAccessRequest) (*agentGrpc.ReleaseAccessResponse, error) {
|
func (a *Agent) ReleaseAccess(frontendToken string) error {
|
||||||
if acc, found := i.agent.accesses[req.FrontendToken]; found {
|
if acc, found := a.accesses[frontendToken]; found {
|
||||||
i.agent.rmAccess <- acc
|
a.rmAccess <- acc
|
||||||
logrus.Infof("released access '%v'", acc.frontendToken)
|
logrus.Infof("released access '%v'", acc.frontendToken)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.Errorf("agent has no access with frontend token '%v'", req.FrontendToken)
|
return errors.Errorf("agent has no access with frontend token '%v'", frontendToken)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *agentGrpcImpl) ReleaseAccess(_ context.Context, req *agentGrpc.ReleaseAccessRequest) (*agentGrpc.ReleaseAccessResponse, error) {
|
||||||
|
return nil, i.agent.ReleaseAccess(req.FrontendToken)
|
||||||
}
|
}
|
||||||
|
@ -7,13 +7,16 @@ import (
|
|||||||
"github.com/sirupsen/logrus"
|
"github.com/sirupsen/logrus"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *agentGrpcImpl) ReleaseShare(_ context.Context, req *agentGrpc.ReleaseShareRequest) (*agentGrpc.ReleaseShareResponse, error) {
|
func (a *Agent) ReleaseShare(shareToken string) error {
|
||||||
if shr, found := i.agent.shares[req.Token]; found {
|
if shr, found := a.shares[shareToken]; found {
|
||||||
i.agent.rmShare <- shr
|
a.rmShare <- shr
|
||||||
logrus.Infof("released share '%v'", shr.token)
|
logrus.Infof("released share '%v'", shr.token)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
return nil, errors.Errorf("agent has no share with token '%v'", req.Token)
|
errors.Errorf("agent has no share with token '%v'", shareToken)
|
||||||
}
|
}
|
||||||
return nil, nil
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *agentGrpcImpl) ReleaseShare(_ context.Context, req *agentGrpc.ReleaseShareRequest) (*agentGrpc.ReleaseShareResponse, error) {
|
||||||
|
return nil, i.agent.ReleaseShare(req.Token)
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,41 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type SharePrivateRequest struct {
|
||||||
|
Target string `json:"target"`
|
||||||
|
BackendMode string `json:"backend_mode"`
|
||||||
|
Insecure bool `json:"insecure"`
|
||||||
|
Closed bool `json:"closed"`
|
||||||
|
AccessGrants []string `json:"access_grants"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SharePublicRequest struct {
|
||||||
|
Target string `json:"target"`
|
||||||
|
BasicAuth []string `json:"basic_auth"`
|
||||||
|
FrontendSelection []string `json:"frontend_selection"`
|
||||||
|
BackendMode string `json:"backend_mode"`
|
||||||
|
Insecure bool `json:"insecure"`
|
||||||
|
OauthProvider string `json:"oauth_provider"`
|
||||||
|
OauthEmailAddressPatterns []string `json:"oauth_email_address_patterns"`
|
||||||
|
OauthCheckInterval string `json:"oauth_check_interval"`
|
||||||
|
Closed bool `json:"closed"`
|
||||||
|
AccessGrants []string `json:"access_grants"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShareReservedRequest struct {
|
||||||
|
Token string `json:"token"`
|
||||||
|
OverrideEndpoint string `json:"override_endpoint"`
|
||||||
|
Insecure bool `json:"insecure"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type ShareReservedResponse struct {
|
||||||
|
Token string
|
||||||
|
BackendMode string
|
||||||
|
ShareMode string
|
||||||
|
FrontendEndpoints []string
|
||||||
|
Target string
|
||||||
|
}
|
||||||
|
|
||||||
type share struct {
|
type share struct {
|
||||||
token string
|
token string
|
||||||
frontendEndpoints []string
|
frontendEndpoints []string
|
||||||
@ -25,6 +60,8 @@ type share struct {
|
|||||||
closed bool
|
closed bool
|
||||||
accessGrants []string
|
accessGrants []string
|
||||||
|
|
||||||
|
request interface{}
|
||||||
|
|
||||||
process *proctree.Child
|
process *proctree.Child
|
||||||
sub *subordinate.MessageHandler
|
sub *subordinate.MessageHandler
|
||||||
|
|
||||||
|
@ -13,22 +13,23 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *agentGrpcImpl) SharePrivate(_ context.Context, req *agentGrpc.SharePrivateRequest) (*agentGrpc.SharePrivateResponse, error) {
|
func (a *Agent) SharePrivate(req *SharePrivateRequest) (shareToken string, err error) {
|
||||||
root, err := environment.LoadRoot()
|
root, err := environment.LoadRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !root.IsEnabled() {
|
if !root.IsEnabled() {
|
||||||
return nil, errors.New("unable to load environment; did you 'zrok enable'?")
|
return "", errors.New("unable to load environment; did you 'zrok enable'?")
|
||||||
}
|
}
|
||||||
|
|
||||||
shrCmd := []string{os.Args[0], "share", "private", "--subordinate", "-b", req.BackendMode}
|
shrCmd := []string{os.Args[0], "share", "private", "--subordinate", "-b", req.BackendMode}
|
||||||
shr := &share{
|
shr := &share{
|
||||||
shareMode: sdk.PrivateShareMode,
|
shareMode: sdk.PrivateShareMode,
|
||||||
backendMode: sdk.BackendMode(req.BackendMode),
|
backendMode: sdk.BackendMode(req.BackendMode),
|
||||||
|
request: req,
|
||||||
sub: subordinate.NewMessageHandler(),
|
sub: subordinate.NewMessageHandler(),
|
||||||
agent: i.agent,
|
agent: a,
|
||||||
}
|
}
|
||||||
shr.sub.MessageHandler = func(msg subordinate.Message) {
|
shr.sub.MessageHandler = func(msg subordinate.Message) {
|
||||||
logrus.Info(msg)
|
logrus.Info(msg)
|
||||||
@ -63,20 +64,34 @@ func (i *agentGrpcImpl) SharePrivate(_ context.Context, req *agentGrpc.SharePriv
|
|||||||
|
|
||||||
shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...)
|
shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
<-shr.sub.BootComplete
|
<-shr.sub.BootComplete
|
||||||
|
|
||||||
if bootErr == nil {
|
if bootErr == nil {
|
||||||
go shr.monitor()
|
go shr.monitor()
|
||||||
i.agent.addShare <- shr
|
a.addShare <- shr
|
||||||
return &agentGrpc.SharePrivateResponse{Token: shr.token}, nil
|
return shr.token, nil
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if err := proctree.WaitChild(shr.process); err != nil {
|
if err := proctree.WaitChild(shr.process); err != nil {
|
||||||
logrus.Errorf("error joining: %v", err)
|
logrus.Errorf("error joining: %v", err)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unable to start share: %v", bootErr)
|
return "", fmt.Errorf("unable to start share: %v", bootErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *agentGrpcImpl) SharePrivate(_ context.Context, req *agentGrpc.SharePrivateRequest) (*agentGrpc.SharePrivateResponse, error) {
|
||||||
|
if shareToken, err := i.agent.SharePrivate(&SharePrivateRequest{
|
||||||
|
Target: req.Target,
|
||||||
|
BackendMode: req.BackendMode,
|
||||||
|
Insecure: req.Insecure,
|
||||||
|
Closed: req.Closed,
|
||||||
|
AccessGrants: req.AccessGrants,
|
||||||
|
}); err == nil {
|
||||||
|
return &agentGrpc.SharePrivateResponse{Token: shareToken}, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -13,22 +13,23 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *agentGrpcImpl) SharePublic(_ context.Context, req *agentGrpc.SharePublicRequest) (*agentGrpc.SharePublicResponse, error) {
|
func (a *Agent) SharePublic(req *SharePublicRequest) (shareToken string, frontendEndpoint []string, err error) {
|
||||||
root, err := environment.LoadRoot()
|
root, err := environment.LoadRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !root.IsEnabled() {
|
if !root.IsEnabled() {
|
||||||
return nil, errors.New("unable to load environment; did you 'zrok enable'?")
|
return "", nil, errors.New("unable to load environment; did you 'zrok enable'?")
|
||||||
}
|
}
|
||||||
|
|
||||||
shrCmd := []string{os.Args[0], "share", "public", "--subordinate", "-b", req.BackendMode}
|
shrCmd := []string{os.Args[0], "share", "public", "--subordinate", "-b", req.BackendMode}
|
||||||
shr := &share{
|
shr := &share{
|
||||||
shareMode: sdk.PublicShareMode,
|
shareMode: sdk.PublicShareMode,
|
||||||
backendMode: sdk.BackendMode(req.BackendMode),
|
backendMode: sdk.BackendMode(req.BackendMode),
|
||||||
|
request: req,
|
||||||
sub: subordinate.NewMessageHandler(),
|
sub: subordinate.NewMessageHandler(),
|
||||||
agent: i.agent,
|
agent: a,
|
||||||
}
|
}
|
||||||
shr.sub.MessageHandler = func(msg subordinate.Message) {
|
shr.sub.MessageHandler = func(msg subordinate.Message) {
|
||||||
logrus.Info(msg)
|
logrus.Info(msg)
|
||||||
@ -87,23 +88,39 @@ func (i *agentGrpcImpl) SharePublic(_ context.Context, req *agentGrpc.SharePubli
|
|||||||
|
|
||||||
shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...)
|
shr.process, err = proctree.StartChild(shr.sub.Tail, shrCmd...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
<-shr.sub.BootComplete
|
<-shr.sub.BootComplete
|
||||||
|
|
||||||
if bootErr == nil {
|
if bootErr == nil {
|
||||||
go shr.monitor()
|
go shr.monitor()
|
||||||
i.agent.addShare <- shr
|
a.addShare <- shr
|
||||||
return &agentGrpc.SharePublicResponse{
|
return shr.token, shr.frontendEndpoints, nil
|
||||||
Token: shr.token,
|
|
||||||
FrontendEndpoints: shr.frontendEndpoints,
|
|
||||||
}, nil
|
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if err := proctree.WaitChild(shr.process); err != nil {
|
if err := proctree.WaitChild(shr.process); err != nil {
|
||||||
logrus.Errorf("error joining: %v", err)
|
logrus.Errorf("error joining: %v", err)
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("unable to start share: %v", bootErr)
|
return "", nil, fmt.Errorf("unable to start share: %v", bootErr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (i *agentGrpcImpl) SharePublic(_ context.Context, req *agentGrpc.SharePublicRequest) (*agentGrpc.SharePublicResponse, error) {
|
||||||
|
if shareToken, frontendEndpoints, err := i.agent.SharePublic(&SharePublicRequest{
|
||||||
|
Target: req.Target,
|
||||||
|
BasicAuth: req.BasicAuth,
|
||||||
|
FrontendSelection: req.FrontendSelection,
|
||||||
|
BackendMode: req.BackendMode,
|
||||||
|
Insecure: req.Insecure,
|
||||||
|
OauthProvider: req.OauthProvider,
|
||||||
|
OauthEmailAddressPatterns: req.OauthEmailAddressPatterns,
|
||||||
|
OauthCheckInterval: req.OauthCheckInterval,
|
||||||
|
Closed: req.Closed,
|
||||||
|
AccessGrants: req.AccessGrants,
|
||||||
|
}); err == nil {
|
||||||
|
return &agentGrpc.SharePublicResponse{Token: shareToken, FrontendEndpoints: frontendEndpoints}, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareReservedRequest) (*agentGrpc.ShareReservedResponse, error) {
|
func (a *Agent) ShareReserved(req *ShareReservedRequest) (*ShareReservedResponse, error) {
|
||||||
root, err := environment.LoadRoot()
|
root, err := environment.LoadRoot()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -25,8 +25,9 @@ func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareRes
|
|||||||
shrCmd := []string{os.Args[0], "share", "reserved", "--subordinate"}
|
shrCmd := []string{os.Args[0], "share", "reserved", "--subordinate"}
|
||||||
shr := &share{
|
shr := &share{
|
||||||
reserved: true,
|
reserved: true,
|
||||||
|
request: req,
|
||||||
sub: subordinate.NewMessageHandler(),
|
sub: subordinate.NewMessageHandler(),
|
||||||
agent: i.agent,
|
agent: a,
|
||||||
}
|
}
|
||||||
shr.sub.MessageHandler = func(msg subordinate.Message) {
|
shr.sub.MessageHandler = func(msg subordinate.Message) {
|
||||||
logrus.Info(msg)
|
logrus.Info(msg)
|
||||||
@ -60,8 +61,8 @@ func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareRes
|
|||||||
|
|
||||||
if bootErr == nil {
|
if bootErr == nil {
|
||||||
go shr.monitor()
|
go shr.monitor()
|
||||||
i.agent.addShare <- shr
|
a.addShare <- shr
|
||||||
return &agentGrpc.ShareReservedResponse{
|
return &ShareReservedResponse{
|
||||||
Token: shr.token,
|
Token: shr.token,
|
||||||
BackendMode: string(shr.backendMode),
|
BackendMode: string(shr.backendMode),
|
||||||
ShareMode: string(shr.shareMode),
|
ShareMode: string(shr.shareMode),
|
||||||
@ -76,3 +77,21 @@ func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareRes
|
|||||||
return nil, fmt.Errorf("unable to start share: %v", bootErr)
|
return nil, fmt.Errorf("unable to start share: %v", bootErr)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (i *agentGrpcImpl) ShareReserved(_ context.Context, req *agentGrpc.ShareReservedRequest) (*agentGrpc.ShareReservedResponse, error) {
|
||||||
|
if resp, err := i.agent.ShareReserved(&ShareReservedRequest{
|
||||||
|
Token: req.Token,
|
||||||
|
OverrideEndpoint: req.OverrideEndpoint,
|
||||||
|
Insecure: req.Insecure,
|
||||||
|
}); err == nil {
|
||||||
|
return &agentGrpc.ShareReservedResponse{
|
||||||
|
Token: resp.Token,
|
||||||
|
BackendMode: resp.BackendMode,
|
||||||
|
ShareMode: resp.ShareMode,
|
||||||
|
FrontendEndpoints: resp.FrontendEndpoints,
|
||||||
|
Target: resp.Target,
|
||||||
|
}, nil
|
||||||
|
} else {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -25,8 +25,6 @@ func init() {
|
|||||||
adminCmd.AddCommand(adminListCmd)
|
adminCmd.AddCommand(adminListCmd)
|
||||||
adminCmd.AddCommand(adminUpdateCmd)
|
adminCmd.AddCommand(adminUpdateCmd)
|
||||||
rootCmd.AddCommand(agentCmd)
|
rootCmd.AddCommand(agentCmd)
|
||||||
agentCmd.AddCommand(agentAccessCmd)
|
|
||||||
agentCmd.AddCommand(agentShareCmd)
|
|
||||||
agentCmd.AddCommand(agentReleaseCmd)
|
agentCmd.AddCommand(agentReleaseCmd)
|
||||||
rootCmd.AddCommand(adminCmd)
|
rootCmd.AddCommand(adminCmd)
|
||||||
rootCmd.AddCommand(configCmd)
|
rootCmd.AddCommand(configCmd)
|
||||||
@ -84,22 +82,12 @@ var adminUpdateCmd = &cobra.Command{
|
|||||||
Short: "Update global resources",
|
Short: "Update global resources",
|
||||||
}
|
}
|
||||||
|
|
||||||
var agentAccessCmd = &cobra.Command{
|
|
||||||
Use: "access",
|
|
||||||
Short: "zrok Agent access commands",
|
|
||||||
}
|
|
||||||
|
|
||||||
var agentCmd = &cobra.Command{
|
var agentCmd = &cobra.Command{
|
||||||
Use: "agent",
|
Use: "agent",
|
||||||
Short: "zrok Agent commands",
|
Short: "zrok Agent commands",
|
||||||
Aliases: []string{"daemon"},
|
Aliases: []string{"daemon"},
|
||||||
}
|
}
|
||||||
|
|
||||||
var agentShareCmd = &cobra.Command{
|
|
||||||
Use: "share",
|
|
||||||
Short: "zrok Agent sharing commands",
|
|
||||||
}
|
|
||||||
|
|
||||||
var agentReleaseCmd = &cobra.Command{
|
var agentReleaseCmd = &cobra.Command{
|
||||||
Use: "release",
|
Use: "release",
|
||||||
Short: "zrok Agent release commands",
|
Short: "zrok Agent release commands",
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
# redir https://{host}{uri} permanent
|
# redir https://{host}{uri} permanent
|
||||||
# }
|
# }
|
||||||
|
|
||||||
*.{$ZROK_DNS_ZONE} {
|
*.{$ZROK_DNS_ZONE}:{$CADDY_HTTPS_PORT} {
|
||||||
tls {
|
tls {
|
||||||
dns {$CADDY_DNS_PLUGIN} {$CADDY_DNS_PLUGIN_TOKEN}
|
dns {$CADDY_DNS_PLUGIN} {$CADDY_DNS_PLUGIN_TOKEN}
|
||||||
propagation_timeout 60m
|
propagation_timeout 60m
|
||||||
|
@ -1,27 +1,18 @@
|
|||||||
|
|
||||||
## Docker Instance
|
## Docker Instance
|
||||||
|
|
||||||
<iframe width="100%" height="315" src="https://www.youtube.com/embed/70zJ_h4uiD8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
<iframe width="100%" height="315" src="https://www.youtube.com/embed/70zJ_h4uiD8" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen></iframe>
|
||||||
|
|
||||||
This Docker Compose project creates a zrok instance and includes a ziti controller and router. An optional Caddy container is included to provide HTTPS and reverse proxy services for the zrok API and public shares.
|
This Docker Compose project creates a zrok instance supported by a OpenZiti controller and router. It supports flexible deployment configurations:
|
||||||
|
|
||||||
### DNS Configuration
|
1. **Basic Configuration**: Services exposed on localhost only (no TLS)
|
||||||
|
2. **With Caddy**: Services published using Caddy (TLS)
|
||||||
1. A wildcard record exists for the IP address where the zrok instance will run, e.g. if your DNS zone is `share.example.com`, then your wildcard record is `*.share.example.com`.
|
3. **With Traefik**: Services published using Traefik (TLS)
|
||||||
|
|
||||||
#### Additional DNS Configuration for Caddy TLS
|
|
||||||
|
|
||||||
The included Caddy container can automatically manage a wildcard certificate for your zrok instance. You can enable Caddy in this compose project by renaming `compose.caddy.yml` as `compose.override.yml`.
|
|
||||||
|
|
||||||
1. Ensure A Caddy DNS plugin is available for your DNS provider (see [github.com/caddy-dns](https://github.com/orgs/caddy-dns/repositories?type=all&q=sort%3Aname-asc)).
|
|
||||||
1. Designate A DNS zone for zrok, e.g. `example.com` or `share.example.com` and create the zone on your DNS provider's platform.
|
|
||||||
1. Created an API token in your DNS provider that has permission to manage zrok's DNS zone.
|
|
||||||
|
|
||||||
### Create the Docker Compose Project
|
### Create the Docker Compose Project
|
||||||
|
|
||||||
Create a working directory on your Docker host and save these Docker Compose project files.
|
Create a working directory on your Docker host and save these Docker Compose project files.
|
||||||
|
|
||||||
#### Shortcut Option
|
#### YOLO
|
||||||
|
|
||||||
1. Run this script to download the files in the current directory.
|
1. Run this script to download the files in the current directory.
|
||||||
|
|
||||||
@ -35,7 +26,7 @@ Create a working directory on your Docker host and save these Docker Compose pro
|
|||||||
curl https://get.openziti.io/zrok-instance/fetch.bash | bash -s /path/to/compose/project/dir
|
curl https://get.openziti.io/zrok-instance/fetch.bash | bash -s /path/to/compose/project/dir
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Manual Option
|
#### I'll Do it Myself
|
||||||
|
|
||||||
1. Get the zrok repo ZIP file.
|
1. Get the zrok repo ZIP file.
|
||||||
|
|
||||||
@ -49,81 +40,117 @@ Create a working directory on your Docker host and save these Docker Compose pro
|
|||||||
unzip -j -d . main.zip '*/docker/compose/zrok-instance/*'
|
unzip -j -d . main.zip '*/docker/compose/zrok-instance/*'
|
||||||
```
|
```
|
||||||
|
|
||||||
### Configure the Docker Compose Project Environment
|
### Basic Configuration (No TLS, Localhost Only)
|
||||||
|
|
||||||
Create an `.env` file in the working directory.
|
This is the simplest way to get started with zrok, exposing services on localhost only, without TLS.
|
||||||
|
|
||||||
```bash title=".env required"
|
#### DNS Configuration (Optional for localhost-only setup)
|
||||||
|
|
||||||
|
1. If you plan to use this beyond localhost, set up a wildcard record for the IP address where the zrok instance will run
|
||||||
|
(e.g., if your DNS zone is `share.example.com`, then your wildcard record is `*.share.example.com`).
|
||||||
|
|
||||||
|
#### Configure the Docker Compose Project Environment
|
||||||
|
|
||||||
|
Create an `.env` file in the working directory with the minimal required configuration:
|
||||||
|
|
||||||
|
```bash title=".env minimal configuration"
|
||||||
|
# Required settings
|
||||||
ZROK_DNS_ZONE=share.example.com
|
ZROK_DNS_ZONE=share.example.com
|
||||||
|
|
||||||
ZROK_USER_EMAIL=me@example.com
|
ZROK_USER_EMAIL=me@example.com
|
||||||
ZROK_USER_PWD=zrokuserpw
|
ZROK_USER_PWD=zrokuserpw
|
||||||
|
|
||||||
ZITI_PWD=zitiadminpw
|
ZITI_PWD=zitiadminpw
|
||||||
ZROK_ADMIN_TOKEN=zroktoken
|
ZROK_ADMIN_TOKEN=zroktoken
|
||||||
```
|
|
||||||
|
|
||||||
```bash title=".env options"
|
# Expose services only on localhost (default)
|
||||||
# Caddy TLS option: rename compose.caddy.yml to compose.override.yml and set these vars; allow 80,443 in firewall
|
ZROK_INSECURE_INTERFACE=127.0.0.1
|
||||||
|
|
||||||
#
|
# Service ports
|
||||||
## set these in .env for providers other than Route53
|
|
||||||
#
|
|
||||||
# plugin name for your DNS provider
|
|
||||||
CADDY_DNS_PLUGIN=cloudflare
|
|
||||||
# API token from your DNS provider
|
|
||||||
CADDY_DNS_PLUGIN_TOKEN=abcd1234
|
|
||||||
# use the staging API until you're sure everything is working to avoid hitting the rate limit
|
|
||||||
CADDY_ACME_API=https://acme-staging-v02.api.letsencrypt.org/directory
|
|
||||||
|
|
||||||
#
|
|
||||||
## set these in .env for Route53
|
|
||||||
#
|
|
||||||
# AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID}
|
|
||||||
# AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY}
|
|
||||||
# AWS_REGION: ${AWS_REGION}
|
|
||||||
# AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN} # if temporary credential, e.g., from STS
|
|
||||||
|
|
||||||
#
|
|
||||||
## if not using Caddy for TLS, uncomment to publish the insecure ports to the internet
|
|
||||||
#
|
|
||||||
#ZROK_INSECURE_INTERFACE=0.0.0.0
|
|
||||||
|
|
||||||
# these insecure ports must be proxied with TLS for security
|
|
||||||
ZROK_CTRL_PORT=18080
|
ZROK_CTRL_PORT=18080
|
||||||
ZROK_FRONTEND_PORT=8080
|
ZROK_FRONTEND_PORT=8080
|
||||||
ZROK_OAUTH_PORT=8081
|
ZROK_OAUTH_PORT=8081
|
||||||
|
|
||||||
# these secure ziti ports must be published to the internet
|
|
||||||
ZITI_CTRL_ADVERTISED_PORT=80
|
ZITI_CTRL_ADVERTISED_PORT=80
|
||||||
ZITI_ROUTER_PORT=3022
|
ZITI_ROUTER_PORT=3022
|
||||||
|
|
||||||
# optionally configure oauth for public shares
|
|
||||||
#ZROK_OAUTH_HASH_KEY=oauthhashkeysecret
|
|
||||||
#ZROK_OAUTH_GITHUB_CLIENT_ID=abcd1234
|
|
||||||
#ZROK_OAUTH_GITHUB_CLIENT_SECRET=abcd1234
|
|
||||||
#ZROK_OAUTH_GOOGLE_CLIENT_ID=abcd1234
|
|
||||||
#ZROK_OAUTH_GOOGLE_CLIENT_SECRET=abcd1234
|
|
||||||
|
|
||||||
# zrok version, e.g., 1.0.0
|
|
||||||
ZROK_CLI_TAG=latest
|
|
||||||
# ziti version, e.g., 1.0.0
|
|
||||||
ZITI_CLI_TAG=latest
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Start the Docker Compose Project
|
#### Start the Docker Compose Project
|
||||||
|
|
||||||
1. Start the zrok instance.
|
Start the zrok instance:
|
||||||
|
|
||||||
The container images for zrok (including caddy) are built in this step. This provides a simple configuration to get started. You can modify the templates named like `*.envsubst` or mount a customized configuration file to mask the one that was built in.
|
```bash
|
||||||
|
docker compose up --build --detach
|
||||||
|
```
|
||||||
|
|
||||||
```bash
|
### Expanded Configuration with TLS (Caddy or Traefik)
|
||||||
docker compose up --build --detach
|
|
||||||
```
|
For production deployments, you should use TLS. You can choose between Caddy or Traefik for TLS termination and reverse proxy to the zrok services. The ziti services are always published directly, not proxied, and they bring their own TLS.
|
||||||
|
|
||||||
|
#### DNS Configuration for TLS
|
||||||
|
|
||||||
|
1. Ensure a wildcard record exists for the IP address where the zrok instance will run
|
||||||
|
(e.g., if your DNS zone is `share.example.com`, then your wildcard record is `*.share.example.com`).
|
||||||
|
|
||||||
|
2. Choose a DNS provider that supports automatic DNS challenge for obtaining wildcard certificates and for which a plugin is available in Caddy or Traefik.
|
||||||
|
|
||||||
|
#### Configure the Docker Compose File
|
||||||
|
|
||||||
|
Add this setting to your `.env` file to select which TLS provider to use:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Use one of the following:
|
||||||
|
COMPOSE_FILE=compose.yml:compose.caddy.yml # For Caddy
|
||||||
|
# OR
|
||||||
|
COMPOSE_FILE=compose.yml:compose.traefik.yml # For Traefik
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Caddy Configuration
|
||||||
|
|
||||||
|
If using Caddy, add these settings to your `.env` file:
|
||||||
|
|
||||||
|
```bash title=".env for Caddy"
|
||||||
|
# Caddy TLS configuration
|
||||||
|
CADDY_DNS_PLUGIN=cloudflare # Plugin name for your DNS provider (see github.com/caddy-dns)
|
||||||
|
CADDY_DNS_PLUGIN_TOKEN=abcd1234 # API token from your DNS provider
|
||||||
|
CADDY_ACME_API=https://acme-v02.api.letsencrypt.org/directory # ACME API endpoint
|
||||||
|
CADDY_HTTPS_PORT=443 # HTTPS port (optional, defaults to 443)
|
||||||
|
CADDY_INTERFACE=0.0.0.0 # Interface to bind to (optional, defaults to all interfaces)
|
||||||
|
|
||||||
|
# For AWS Route53, uncomment and set these instead of CADDY_DNS_PLUGIN_TOKEN:
|
||||||
|
# AWS_ACCESS_KEY_ID=your-access-key
|
||||||
|
# AWS_SECRET_ACCESS_KEY=your-secret-key
|
||||||
|
# AWS_REGION=your-region
|
||||||
|
# AWS_SESSION_TOKEN=your-session-token # Only if using temporary credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Traefik Configuration
|
||||||
|
|
||||||
|
If using Traefik, add these settings to your `.env` file:
|
||||||
|
|
||||||
|
```bash title=".env for Traefik"
|
||||||
|
# Traefik TLS configuration
|
||||||
|
TRAEFIK_DNS_PROVIDER=digitalocean # DNS provider for Traefik
|
||||||
|
TRAEFIK_DNS_PROVIDER_TOKEN=abcd1234 # API token from your DNS provider
|
||||||
|
TRAEFIK_ACME_API=https://acme-v02.api.letsencrypt.org/directory # ACME API endpoint
|
||||||
|
TRAEFIK_HTTPS_PORT=443 # HTTPS port (optional, defaults to 443)
|
||||||
|
TRAEFIK_INTERFACE=0.0.0.0 # Interface to bind to (optional, defaults to all interfaces)
|
||||||
|
|
||||||
|
# For AWS Route53, uncomment and set these instead of TRAEFIK_DNS_PROVIDER_TOKEN:
|
||||||
|
# AWS_ACCESS_KEY_ID=your-access-key
|
||||||
|
# AWS_SECRET_ACCESS_KEY=your-secret-key
|
||||||
|
# AWS_REGION=your-region
|
||||||
|
# AWS_SESSION_TOKEN=your-session-token # Only if using temporary credentials
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Start the Docker Compose Project
|
||||||
|
|
||||||
|
Start the zrok instance with TLS support:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker compose up --build --detach
|
||||||
|
```
|
||||||
|
|
||||||
### Set up a User Account
|
### Set up a User Account
|
||||||
|
|
||||||
This step creates a user account. You will log in to the zrok web console with the account password created in this step. The ZROK_USER_EMAIL and ZROK_USER_PWD variables are set in the `.env` file. You can create more user accounts the same way by substituting a different email and password.
|
This step creates a user account. You will log in to the zrok web console with the account password created in this step. The ZROK_USER_EMAIL and ZROK_USER_PWD variables are set in the `.env` file.
|
||||||
|
|
||||||
```bash title="Create the first user account"
|
```bash title="Create the first user account"
|
||||||
docker compose exec zrok-controller bash -xc 'zrok admin create account ${ZROK_USER_EMAIL} ${ZROK_USER_PWD}'
|
docker compose exec zrok-controller bash -xc 'zrok admin create account ${ZROK_USER_EMAIL} ${ZROK_USER_PWD}'
|
||||||
@ -148,19 +175,17 @@ You must enable each device environment with the account token obtained when the
|
|||||||
|
|
||||||
Follow [the getting started guide](/docs/getting-started#installing-the-zrok-command) to install the zrok CLI on some device and enable a zrok environment.
|
Follow [the getting started guide](/docs/getting-started#installing-the-zrok-command) to install the zrok CLI on some device and enable a zrok environment.
|
||||||
|
|
||||||
1. Configure the environment with the zrok API. Substitute the API endpoint with the one you're using, e.g. `https://zrok.${ZROK_DNS_ZONE}`.
|
1. Configure the environment with the zrok API endpoint:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# If using TLS (Caddy or Traefik)
|
||||||
zrok config set apiEndpoint https://zrok.share.example.com
|
zrok config set apiEndpoint https://zrok.share.example.com
|
||||||
|
|
||||||
|
# If using basic configuration (localhost, no TLS)
|
||||||
|
zrok config set apiEndpoint http://localhost:18080
|
||||||
```
|
```
|
||||||
|
|
||||||
or, if not using Caddy for TLS:
|
2. Enable an environment on this device with the account token from the previous step.
|
||||||
|
|
||||||
```bash
|
|
||||||
zrok config set apiEndpoint http://zrok.share.example.com:18080
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Enable an environment on this device with the account token from the previous step.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
zrok enable heMqncCyxZcx
|
zrok enable heMqncCyxZcx
|
||||||
@ -168,105 +193,56 @@ Follow [the getting started guide](/docs/getting-started#installing-the-zrok-com
|
|||||||
|
|
||||||
### Firewall Configuration
|
### Firewall Configuration
|
||||||
|
|
||||||
The `ziti-quickstart` and `caddy` containers publish ports to all devices that use zrok shares. The `zrok-controller` and `zrok-frontend` containers expose ports only to the `caddy` container and the Docker host's loopback interface.
|
- `443/tcp` - HTTPS for all services (Caddy or Traefik)
|
||||||
|
- `80/tcp` - ziti ctrl plane
|
||||||
|
- `3022/tcp` - ziti data plane
|
||||||
|
|
||||||
#### Required
|
### Additional Configuration Options
|
||||||
|
|
||||||
1. `443/tcp` - reverse proxy handles HTTPS requests for zrok API, OAuth, and public shares (published by container `caddy`)
|
You can add these additional settings to your `.env` file for more customization:
|
||||||
1. `80/tcp` - ziti ctrl plane (published by container `ziti-quickstart`)
|
|
||||||
1. `3022/tcp` - ziti data plane (published by container `ziti-quickstart`)
|
|
||||||
|
|
||||||
<!-- 1. 443/udp used by Caddy for HTTP/3 QUIC protocol (published by container `caddy`) -->
|
```bash
|
||||||
|
# OAuth configuration for public shares
|
||||||
See "My internet connection can only send traffic to common ports" below about changing the required ports.
|
ZROK_OAUTH_HASH_KEY=oauthhashkeysecret
|
||||||
|
ZROK_OAUTH_GITHUB_CLIENT_ID=abcd1234
|
||||||
|
ZROK_OAUTH_GITHUB_CLIENT_SECRET=abcd1234
|
||||||
|
ZROK_OAUTH_GOOGLE_CLIENT_ID=abcd1234
|
||||||
|
ZROK_OAUTH_GOOGLE_CLIENT_SECRET=abcd1234
|
||||||
|
```
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
1. Check the ziti and zrok logs.
|
1. Check the service logs:
|
||||||
|
|
||||||
You can substitute the service container name of each to check their logs individually: `ziti-quickstart`, `zrok-controller`, `zrok-frontend`.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# View logs for a specific service
|
||||||
docker compose logs zrok-controller
|
docker compose logs zrok-controller
|
||||||
```
|
docker compose logs zrok-frontend
|
||||||
|
docker compose logs ziti-quickstart
|
||||||
|
|
||||||
1. Check the Caddy logs.
|
# View logs for Caddy (if using)
|
||||||
|
|
||||||
It can take a few minutes for Caddy to obtain the wildcard certificate. You can check the logs to see if there were any errors completing the DNS challenge which involves using the Caddy DNS plugin to create a TXT record in your DNS zone. This leverages the API token you provided in the `.env` file, which must have permission to create DNS records in the zrok DNS zone.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose logs caddy
|
docker compose logs caddy
|
||||||
|
|
||||||
|
# View logs for Traefik (if using)
|
||||||
|
docker compose logs traefik
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Caddy keeps failing to obtain a wildcard certificate because it timed out waiting for DNS.
|
2. Validate TLS configuration:
|
||||||
|
|
||||||
Symptom: the Caddy log contains "timed out waiting for record to fully propagate." This means that Caddy added a DNS record with your DNS provider's API to prove to the CA it controls the zrok DNS zone, but it wasn't able to verify the record was created successfully with a DNS query.
|
|
||||||
|
|
||||||
Solutions:
|
|
||||||
|
|
||||||
- Add `propagation_delay` in your `Caddyfile` to delay the first DNS verification query. This avoids caching a verification query failure by waiting a few minutes for the record to become available so the verification query will succeed on the first attempt. Caddy will be unable to verify the DNS record if the failure remains in the cache too long.
|
|
||||||
- If the prior solution fails, you can override the default resolves/nameservers with `resolvers`, a space-separated list of DNS servers. This gives you more control over if and where the verification query result is cached.
|
|
||||||
|
|
||||||
```
|
|
||||||
tls {
|
|
||||||
dns {CADDY_DNS_PLUGIN} {CADDY_DNS_PLUGIN_TOKEN}
|
|
||||||
propagation_timeout 60m # default 2m
|
|
||||||
propagation_delay 5m # default 0m
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
1. `zrok enable` fails certificate verification: ensure you are not using the staging API for Let's Encrypt.
|
|
||||||
|
|
||||||
If you are using the staging API, you will see an error about the API certificate when you use the zrok CLI. You can switch to the production API by removing the overriding assignment of the `CADDY_ACME_API` variable.
|
|
||||||
|
|
||||||
```buttonless title="Example output"
|
|
||||||
there was a problem enabling your environment!
|
|
||||||
you are trying to use the zrok service at: https://zrok.share.example.com
|
|
||||||
you can change your zrok service endpoint using this command:
|
|
||||||
|
|
||||||
$ zrok config set apiEndpoint <newEndpoint>
|
|
||||||
|
|
||||||
(where newEndpoint is something like: https://some.zrok.io)
|
|
||||||
[ERROR]: error creating service client (error getting version from api endpoint 'https://zrok.share.example.com': Get "https://zrok.share.example.com/api/v1/version": tls: failed to verify certificate: x509: certificate signed by unknown authority: Get "https://zrok.share.example.com/api/v1/version": tls: failed to verify certificate: x509: certificate signed by unknown authority)
|
|
||||||
```
|
|
||||||
|
|
||||||
1. Validate the Caddyfile.
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
# For Caddy
|
||||||
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
|
docker compose exec caddy caddy validate --config /etc/caddy/Caddyfile
|
||||||
|
|
||||||
|
# For Traefik
|
||||||
|
docker compose exec traefik traefik healthcheck
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Verify the correct DNS provider module was built-in to Caddy.
|
3. Check certificate status:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
docker compose exec caddy caddy list-modules | grep dns.providers
|
# For Caddy
|
||||||
```
|
docker compose exec caddy curl -s "http://localhost:2019/certificates"
|
||||||
|
|
||||||
```buttonless title="Example output"
|
# For Traefik - view the ACME certificate file directly
|
||||||
dns.providers.cloudflare
|
docker compose exec traefik cat /etc/traefik/acme/acme.json | grep -A 5 "Certificates"
|
||||||
```
|
|
||||||
|
|
||||||
1. Use the Caddy admin API.
|
|
||||||
|
|
||||||
You can use the Caddy admin API to check the status of the Caddy instance. The admin API is available on port `2019/tcp` inside the Docker Compose project. You can modify `compose.override.yml` to publish the port if you want to access the admin API from the Docker host or elsewhere.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker compose exec caddy curl http://localhost:2019/config/ | jq
|
|
||||||
```
|
|
||||||
|
|
||||||
1. My DNS provider credential is composed of several values, not a single API token.
|
|
||||||
|
|
||||||
As long as your DNS provider is supported by Caddy then it will work. Here's a checklist for DNS providers like Route53 with credentials expressed as multiple values, e.g., `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`.
|
|
||||||
|
|
||||||
1. Define env vars in `.env` file.
|
|
||||||
1. Declare env vars in `compose.override.yml` file on `caddy`'s `environment`.
|
|
||||||
1. Modify `Caddyfile` according to the DNS plugin author's instructions ([link to Route53 README](https://github.com/caddy-dns/route53)). This means modifying the `Caddyfile` to reference the env vars. The provided file `route53.Caddyfile` serves as an example.
|
|
||||||
|
|
||||||
1. My internet connection can only send traffic to common ports like 80, 443, and 3389.
|
|
||||||
|
|
||||||
You can change the required ports in the `.env` file. Caddy will still use port 443 for zrok shares and API if you renamed `compose.caddy.yml` as `compose.override.yml` to enable Caddy.
|
|
||||||
|
|
||||||
```bash title=".env"
|
|
||||||
ZITI_CTRL_ADVERTISED_PORT=80
|
|
||||||
ZITI_ROUTER_PORT=3389
|
|
||||||
```
|
```
|
||||||
|
@ -87,7 +87,7 @@ until [[ -n "${ZITI_PUBLIC_ID}" ]]; do
|
|||||||
done
|
done
|
||||||
echo "DEBUG: 'public' ZITI_PUBLIC_ID=$ZITI_PUBLIC_ID"
|
echo "DEBUG: 'public' ZITI_PUBLIC_ID=$ZITI_PUBLIC_ID"
|
||||||
|
|
||||||
until curl -sSf "${ZROK_API_ENDPOINT}/api/v1/version"; do
|
until curl -sSf "${ZROK_API_ENDPOINT}" &>/dev/null; do
|
||||||
echo "DEBUG: waiting for zrok controller API version endpoint to respond"
|
echo "DEBUG: waiting for zrok controller API version endpoint to respond"
|
||||||
sleep 3
|
sleep 3
|
||||||
done
|
done
|
||||||
|
@ -8,6 +8,7 @@ services:
|
|||||||
CADDY_DNS_PLUGIN: ${CADDY_DNS_PLUGIN} # e.g., "digitalocean" (see github.com/caddy-dns)
|
CADDY_DNS_PLUGIN: ${CADDY_DNS_PLUGIN} # e.g., "digitalocean" (see github.com/caddy-dns)
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
|
CADDY_HTTPS_PORT: ${CADDY_HTTPS_PORT:-443}
|
||||||
#
|
#
|
||||||
## set these in .env for providers other than Route53
|
## set these in .env for providers other than Route53
|
||||||
#
|
#
|
||||||
@ -31,12 +32,12 @@ services:
|
|||||||
ZROK_OAUTH_PORT: ${ZROK_OAUTH_PORT:-8081}
|
ZROK_OAUTH_PORT: ${ZROK_OAUTH_PORT:-8081}
|
||||||
expose:
|
expose:
|
||||||
# - 80/tcp
|
# - 80/tcp
|
||||||
- 443/tcp
|
- ${CADDY_HTTPS_PORT:-443}/tcp
|
||||||
- 443/udp # Caddy's HTTP/3 (QUIC) (not published)
|
- ${CADDY_HTTPS_PORT:-443}/udp # Caddy's HTTP/3 (QUIC) (not published)
|
||||||
- 2019/tcp # Caddy's admin API (not published)
|
- 2019/tcp # Caddy's admin API (not published)
|
||||||
ports:
|
ports:
|
||||||
# - ${CADDY_INTERFACE:-0.0.0.0}:80:80
|
# - ${CADDY_INTERFACE:-0.0.0.0}:80:80 # port occupied by ziti
|
||||||
- ${CADDY_INTERFACE:-0.0.0.0}:443:443
|
- ${CADDY_INTERFACE:-0.0.0.0}:${CADDY_HTTPS_PORT:-443}:${CADDY_HTTPS_PORT:-443}
|
||||||
# - ${CADDY_INTERFACE:-0.0.0.0}:443:443/udp" # future: HTTP/3 (QUIC)
|
# - ${CADDY_INTERFACE:-0.0.0.0}:443:443/udp" # future: HTTP/3 (QUIC)
|
||||||
volumes:
|
volumes:
|
||||||
- caddy_data:/data
|
- caddy_data:/data
|
||||||
@ -47,7 +48,7 @@ services:
|
|||||||
zrok-frontend:
|
zrok-frontend:
|
||||||
environment:
|
environment:
|
||||||
ZROK_FRONTEND_SCHEME: https
|
ZROK_FRONTEND_SCHEME: https
|
||||||
ZROK_FRONTEND_PORT: 443
|
ZROK_FRONTEND_PORT: ${CADDY_HTTPS_PORT:-443}
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
caddy_data:
|
caddy_data:
|
||||||
|
80
docker/compose/zrok-instance/compose.traefik.yml
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
# delete this file from your compose project if you do not want to use Traefik for TLS termination
|
||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
build:
|
||||||
|
context: .
|
||||||
|
dockerfile: ./traefik.Dockerfile
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
# DNS provider configuration
|
||||||
|
TRAEFIK_CERTIFICATESRESOLVERS_default_ACME_EMAIL: ${ZROK_USER_EMAIL}
|
||||||
|
TRAEFIK_CERTIFICATESRESOLVERS_default_ACME_CASERVER: ${TRAEFIK_ACME_API:-https://acme-v02.api.letsencrypt.org/directory}
|
||||||
|
TRAEFIK_CERTIFICATESRESOLVERS_default_ACME_DNSCHALLENGE: "true"
|
||||||
|
TRAEFIK_CERTIFICATESRESOLVERS_default_ACME_DNSCHALLENGE_PROVIDER: ${TRAEFIK_DNS_PROVIDER}
|
||||||
|
TRAEFIK_CERTIFICATESRESOLVERS_default_ACME_DNSCHALLENGE_RESOLVERS: "1.1.1.1:53,8.8.8.8:53"
|
||||||
|
TRAEFIK_CERTIFICATESRESOLVERS_default_ACME_DNSCHALLENGE_DELAYBEFORECHECK: "60"
|
||||||
|
TRAEFIK_CERTIFICATESRESOLVERS_default_ACME_STORAGE: /etc/traefik/acme/acme.json
|
||||||
|
|
||||||
|
# Entrypoints configuration
|
||||||
|
TRAEFIK_ENTRYPOINTS_websecure_ADDRESS: ":${TRAEFIK_HTTPS_PORT:-443}"
|
||||||
|
|
||||||
|
# DNS provider credentials - these will be mapped to environment variables expected by the provider
|
||||||
|
# See: https://doc.traefik.io/traefik/https/acme/#providers
|
||||||
|
TRAEFIK_DNS_PROVIDER: ${TRAEFIK_DNS_PROVIDER} # e.g., "digitalocean"
|
||||||
|
|
||||||
|
# Provider-specific credentials - uncomment and set in .env as needed
|
||||||
|
# Digital Ocean
|
||||||
|
# DO_AUTH_TOKEN: ${TRAEFIK_DNS_PROVIDER_TOKEN:-}
|
||||||
|
|
||||||
|
# Cloudflare - Option 1: Using Email and API Key
|
||||||
|
# CLOUDFLARE_EMAIL: ${CLOUDFLARE_EMAIL:-}
|
||||||
|
# CLOUDFLARE_API_KEY: ${CLOUDFLARE_API_KEY:-}
|
||||||
|
|
||||||
|
# Cloudflare - Option 2: Using API Tokens (recommended)
|
||||||
|
CLOUDFLARE_DNS_API_TOKEN: ${TRAEFIK_DNS_PROVIDER_TOKEN:-}
|
||||||
|
# CLOUDFLARE_ZONE_API_TOKEN: ${TRAEFIK_DNS_PROVIDER_TOKEN:-}
|
||||||
|
|
||||||
|
# AWS Route53 - uncomment if using Route53
|
||||||
|
# AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
|
||||||
|
# AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
|
||||||
|
# AWS_REGION: ${AWS_REGION:-}
|
||||||
|
# AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN:-} # if temporary credential, e.g., from STS
|
||||||
|
|
||||||
|
# General configuration
|
||||||
|
ZROK_DNS_ZONE: ${ZROK_DNS_ZONE} # e.g., "example.com" or "127.0.0.1.sslip.io"
|
||||||
|
ZROK_CTRL_PORT: ${ZROK_CTRL_PORT:-18080}
|
||||||
|
ZROK_FRONTEND_PORT: ${ZROK_FRONTEND_PORT:-8080}
|
||||||
|
ZROK_OAUTH_PORT: ${ZROK_OAUTH_PORT:-8081}
|
||||||
|
ZITI_CTRL_ADVERTISED_PORT: ${ZITI_CTRL_ADVERTISED_PORT:-80}
|
||||||
|
|
||||||
|
# Traefik specific configurations
|
||||||
|
TRAEFIK_API_DASHBOARD: "true"
|
||||||
|
TRAEFIK_API_INSECURE: "false"
|
||||||
|
TRAEFIK_PROVIDERS_DOCKER: "false" # Disable Docker provider since we're not mounting the socket
|
||||||
|
TRAEFIK_PROVIDERS_DOCKER_EXPOSEDBYDEFAULT: "false"
|
||||||
|
TRAEFIK_PROVIDERS_FILE_DIRECTORY: "/etc/traefik/dynamic"
|
||||||
|
TRAEFIK_PROVIDERS_FILE_WATCH: "true"
|
||||||
|
TRAEFIK_LOG_LEVEL: "DEBUG"
|
||||||
|
TRAEFIK_ACCESSLOG: "true"
|
||||||
|
TRAEFIK_ACCESSLOG_FORMAT: "common"
|
||||||
|
|
||||||
|
expose:
|
||||||
|
- ${TRAEFIK_HTTPS_PORT:-443}/tcp
|
||||||
|
- ${TRAEFIK_HTTPS_PORT:-443}/udp # For HTTP/3 (QUIC) (not published yet)
|
||||||
|
- 8080/tcp # Traefik's admin API (not published)
|
||||||
|
ports:
|
||||||
|
- ${TRAEFIK_INTERFACE:-0.0.0.0}:${TRAEFIK_HTTPS_PORT:-443}:${TRAEFIK_HTTPS_PORT:-443}
|
||||||
|
# - ${TRAEFIK_INTERFACE:-0.0.0.0}:${TRAEFIK_HTTPS_PORT:-443}:${TRAEFIK_HTTPS_PORT:-443}/udp # future: HTTP/3 (QUIC)
|
||||||
|
volumes:
|
||||||
|
- traefik_data:/etc/traefik/acme
|
||||||
|
# - /var/run/docker.sock:/var/run/docker.sock:ro # Docker provider for detecting new routes by label
|
||||||
|
networks:
|
||||||
|
zrok-instance:
|
||||||
|
|
||||||
|
zrok-frontend:
|
||||||
|
environment:
|
||||||
|
ZROK_FRONTEND_SCHEME: https
|
||||||
|
ZROK_FRONTEND_PORT: ${TRAEFIK_HTTPS_PORT:-443}
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
traefik_data:
|
@ -8,7 +8,7 @@
|
|||||||
# redir https://{host}{uri} permanent
|
# redir https://{host}{uri} permanent
|
||||||
# }
|
# }
|
||||||
|
|
||||||
*.{$ZROK_DNS_ZONE} {
|
*.{$ZROK_DNS_ZONE}:{$CADDY_HTTPS_PORT} {
|
||||||
tls {
|
tls {
|
||||||
dns route53 {
|
dns route53 {
|
||||||
access_key_id {$AWS_ACCESS_KEY_ID}
|
access_key_id {$AWS_ACCESS_KEY_ID}
|
||||||
|
17
docker/compose/zrok-instance/traefik.Dockerfile
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
# Use the official Traefik image
|
||||||
|
FROM traefik:v2.10
|
||||||
|
|
||||||
|
# Install curl for healthcheck
|
||||||
|
RUN apk add --no-cache curl
|
||||||
|
|
||||||
|
# Create necessary directories
|
||||||
|
RUN mkdir -p /etc/traefik/dynamic /etc/traefik/acme
|
||||||
|
|
||||||
|
# Add configuration file
|
||||||
|
COPY ./traefik.dynamic.toml /etc/traefik/dynamic/traefik.toml
|
||||||
|
|
||||||
|
# Create and set permissions for the ACME certificates storage
|
||||||
|
RUN touch /etc/traefik/acme/acme.json && chmod 600 /etc/traefik/acme/acme.json
|
||||||
|
|
||||||
|
HEALTHCHECK --interval=5s --timeout=3s --retries=3 \
|
||||||
|
CMD traefik healthcheck
|
95
docker/compose/zrok-instance/traefik.dynamic.toml
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
# Dynamic configuration for Traefik
|
||||||
|
|
||||||
|
# Entrypoints configuration is handled in the main Traefik configuration through environment variables
|
||||||
|
# We don't define entryPoints here as they will be configured by the environment variables in compose.traefik.yml:
|
||||||
|
# TRAEFIK_ENTRYPOINTS_websecure_ADDRESS: ":${TRAEFIK_HTTPS_PORT:-443}"
|
||||||
|
|
||||||
|
# TLS wildcard certificate configuration
|
||||||
|
[tls]
|
||||||
|
[tls.options]
|
||||||
|
[tls.options.default]
|
||||||
|
minVersion = "VersionTLS12"
|
||||||
|
sniStrict = true
|
||||||
|
|
||||||
|
# HTTP to HTTPS redirect middleware
|
||||||
|
[http.middlewares]
|
||||||
|
[http.middlewares.https-redirect.redirectScheme]
|
||||||
|
scheme = "https"
|
||||||
|
permanent = true
|
||||||
|
|
||||||
|
# Note: We can't use template syntax here as it's not supported in static config
|
||||||
|
# Instead, we'll use passHostHeader in the loadBalancer configs
|
||||||
|
|
||||||
|
# Define servers transports
|
||||||
|
[http.serversTransports]
|
||||||
|
[http.serversTransports.ziti-transport]
|
||||||
|
insecureSkipVerify = true
|
||||||
|
|
||||||
|
# Routing configuration
|
||||||
|
[http.routers]
|
||||||
|
# Ziti router
|
||||||
|
[http.routers.ziti]
|
||||||
|
rule = "Host(`ziti.{{ env "ZROK_DNS_ZONE" }}`)"
|
||||||
|
service = "ziti"
|
||||||
|
entrypoints = ["websecure"]
|
||||||
|
[http.routers.ziti.tls]
|
||||||
|
certResolver = "default"
|
||||||
|
[[http.routers.ziti.tls.domains]]
|
||||||
|
main = "*.{{ env "ZROK_DNS_ZONE" }}"
|
||||||
|
|
||||||
|
# OAuth router
|
||||||
|
[http.routers.oauth]
|
||||||
|
rule = "Host(`oauth.{{ env "ZROK_DNS_ZONE" }}`)"
|
||||||
|
service = "oauth"
|
||||||
|
entrypoints = ["websecure"]
|
||||||
|
[http.routers.oauth.tls]
|
||||||
|
certResolver = "default"
|
||||||
|
[[http.routers.oauth.tls.domains]]
|
||||||
|
main = "*.{{ env "ZROK_DNS_ZONE" }}"
|
||||||
|
|
||||||
|
# Controller router
|
||||||
|
[http.routers.ctrl]
|
||||||
|
rule = "Host(`zrok.{{ env "ZROK_DNS_ZONE" }}`)"
|
||||||
|
service = "ctrl"
|
||||||
|
entrypoints = ["websecure"]
|
||||||
|
[http.routers.ctrl.tls]
|
||||||
|
certResolver = "default"
|
||||||
|
[[http.routers.ctrl.tls.domains]]
|
||||||
|
main = "*.{{ env "ZROK_DNS_ZONE" }}"
|
||||||
|
|
||||||
|
# Frontend router (default route)
|
||||||
|
[http.routers.frontend]
|
||||||
|
rule = "HostRegexp(`{subdomain:[a-zA-Z0-9-]+}.{{ env "ZROK_DNS_ZONE" }}`) && !Host(`ziti.{{ env "ZROK_DNS_ZONE" }}`) && !Host(`oauth.{{ env "ZROK_DNS_ZONE" }}`) && !Host(`zrok.{{ env "ZROK_DNS_ZONE" }}`)"
|
||||||
|
service = "frontend"
|
||||||
|
entrypoints = ["websecure"]
|
||||||
|
[http.routers.frontend.tls]
|
||||||
|
certResolver = "default"
|
||||||
|
[[http.routers.frontend.tls.domains]]
|
||||||
|
main = "*.{{ env "ZROK_DNS_ZONE" }}"
|
||||||
|
|
||||||
|
# Service configuration
|
||||||
|
[http.services]
|
||||||
|
# Ziti service
|
||||||
|
[http.services.ziti.loadBalancer]
|
||||||
|
passHostHeader = true
|
||||||
|
serversTransport = "ziti-transport"
|
||||||
|
[[http.services.ziti.loadBalancer.servers]]
|
||||||
|
url = "http://ziti-quickstart:{{ env "ZITI_CTRL_ADVERTISED_PORT" | default 80 }}"
|
||||||
|
|
||||||
|
# OAuth service
|
||||||
|
[http.services.oauth.loadBalancer]
|
||||||
|
passHostHeader = true
|
||||||
|
[[http.services.oauth.loadBalancer.servers]]
|
||||||
|
url = "http://zrok-frontend:{{ env "ZROK_OAUTH_PORT" | default 8081 }}"
|
||||||
|
|
||||||
|
# Controller service
|
||||||
|
[http.services.ctrl.loadBalancer]
|
||||||
|
passHostHeader = true
|
||||||
|
[[http.services.ctrl.loadBalancer.servers]]
|
||||||
|
url = "http://zrok-controller:{{ env "ZROK_CTRL_PORT" | default 18080 }}"
|
||||||
|
|
||||||
|
# Frontend service
|
||||||
|
[http.services.frontend.loadBalancer]
|
||||||
|
passHostHeader = true
|
||||||
|
[[http.services.frontend.loadBalancer.servers]]
|
||||||
|
url = "http://zrok-frontend:{{ env "ZROK_FRONTEND_PORT" | default 8080 }}"
|
@ -36,7 +36,7 @@ services:
|
|||||||
HOME: /mnt # zrok homedir in container
|
HOME: /mnt # zrok homedir in container
|
||||||
|
|
||||||
# most relevant options
|
# most relevant options
|
||||||
ZROK_UNIQUE_NAME: # name is used to construct frontend domain name, e.g. "myapp" in "myapp.share.zrok.io"
|
ZROK_UNIQUE_NAME: # name is used to construct frontend domain name, e.g. "toaster" in "toaster.share.zrok.io"; lowercase alphanumeric, between 4 and 32 characters in length
|
||||||
ZROK_BACKEND_MODE: # web, caddy, drive, proxy
|
ZROK_BACKEND_MODE: # web, caddy, drive, proxy
|
||||||
ZROK_TARGET: # backend target, is a path in container filesystem unless proxy mode
|
ZROK_TARGET: # backend target, is a path in container filesystem unless proxy mode
|
||||||
ZROK_INSECURE: # "--insecure" if proxy target has unverifiable TLS server certificate
|
ZROK_INSECURE: # "--insecure" if proxy target has unverifiable TLS server certificate
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
services:
|
|
||||||
my-other-zrok-share:
|
|
||||||
image: ${ZROK_CONTAINER_IMAGE:-docker.io/openziti/zrok}
|
|
||||||
restart: unless-stopped
|
|
||||||
entrypoint: zrok-share.bash
|
|
||||||
depends_on:
|
|
||||||
zrok-enable:
|
|
||||||
condition: service_completed_successfully
|
|
||||||
volumes:
|
|
||||||
- zrok_env:/mnt
|
|
||||||
- ./Caddyfile:/Caddyfile
|
|
||||||
environment:
|
|
||||||
# most relevant options
|
|
||||||
ZROK_UNIQUE_NAME: "my-other-zrok-share"
|
|
||||||
ZROK_BACKEND_MODE: caddy
|
|
||||||
ZROK_TARGET: /Caddyfile
|
|
||||||
# internal configuration
|
|
||||||
HOME: /mnt # zrok homedir in container
|
|
@ -38,7 +38,7 @@ services:
|
|||||||
HOME: /mnt # zrok homedir in container
|
HOME: /mnt # zrok homedir in container
|
||||||
|
|
||||||
# most relevant options
|
# most relevant options
|
||||||
ZROK_UNIQUE_NAME: # name is used to construct frontend domain name, e.g. "myapp" in "myapp.share.zrok.io"
|
ZROK_UNIQUE_NAME: # name is used to construct frontend domain name, e.g. "toaster" in "toaster.share.zrok.io"; lowercase alphanumeric, between 4 and 32 characters in length
|
||||||
ZROK_BACKEND_MODE: # web, caddy, drive, proxy
|
ZROK_BACKEND_MODE: # web, caddy, drive, proxy
|
||||||
ZROK_TARGET: # backend target, is a path in container filesystem unless proxy mode
|
ZROK_TARGET: # backend target, is a path in container filesystem unless proxy mode
|
||||||
ZROK_INSECURE: # "--insecure" if proxy target has unverifiable TLS server certificate
|
ZROK_INSECURE: # "--insecure" if proxy target has unverifiable TLS server certificate
|
||||||
|
@ -48,6 +48,6 @@ The following illustration shows the possibilities available.
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
The `*.in.zrok.io` frontend is a "public" frontend, available to all `zrok` users. Most `zrok` installations will want to have at least one public, global frontend for all public, internet-facing ingress traffic for private backend instances. In the underlying data store, the public frontend will have a `name` set to `public` (or some other representative name), allowing users to reference that `frontend` using a friendly label.
|
The `*.share.zrok.io` frontend is a "public" frontend, available to all `zrok` users. Most `zrok` installations will want to have at least one public, global frontend for all public, internet-facing ingress traffic for private backend instances. In the underlying data store, the public frontend will have a `name` set to `public` (or some other representative name), allowing users to reference that `frontend` using a friendly label.
|
||||||
|
|
||||||
The other two "private" frontends are configured with no `name` label (the lack of a `name` label signifies that these are "private" frontends). The ephemeral environment is allocated when a `zrok` frontend request is made without an account on behalf of a private share.
|
The other two "private" frontends are configured with no `name` label (the lack of a `name` label signifies that these are "private" frontends). The ephemeral environment is allocated when a `zrok` frontend request is made without an account on behalf of a private share.
|
||||||
|
@ -16,16 +16,16 @@ To delete your `reserved` share use the `zrok release` command or click the dele
|
|||||||
|
|
||||||
## Unique Names
|
## Unique Names
|
||||||
|
|
||||||
The default is to generate a random _share token_ and you may specify a _unique name_.
|
The default is to generate a random _share token_ and you may instead specify a _unique name_. The unique name must be lowercase alphanumeric, between 4 and 32 characters in length.
|
||||||
|
|
||||||
This reserves public share token "myshare."
|
This reserves public share token "toaster".
|
||||||
|
|
||||||
```bash title="Reserve with the Command Line"
|
```bash title="Reserve with the Command Line"
|
||||||
zrok reserve public 80 --unique-name "myshare"
|
zrok reserve public 80 --unique-name "toaster"
|
||||||
```
|
```
|
||||||
|
|
||||||
This shares `127.0.0.1:80` as `https://myshare.zrok.example.com` where `https://{token}.zrok.example.com` is the frontend's template.
|
This shares `127.0.0.1:80` as `https://toaster.zrok.example.com` where `https://{token}.zrok.example.com` is the frontend's template.
|
||||||
|
|
||||||
```bash title="Share a Reserved Token"
|
```bash title="Share a Reserved Token"
|
||||||
zrok share reserved "myshare"
|
zrok share reserved "toaster"
|
||||||
```
|
```
|
||||||
|
@ -9,11 +9,32 @@ import DownloadCard from '@site/src/components/download-card';
|
|||||||
import DownloadCardStyles from '@site/src/css/download-card.module.css';
|
import DownloadCardStyles from '@site/src/css/download-card.module.css';
|
||||||
import InstallCards from '/../docs/guides/install/_install_cards.mdx';
|
import InstallCards from '/../docs/guides/install/_install_cards.mdx';
|
||||||
|
|
||||||
|
:::note
|
||||||
|
If you've upgraded to `v1.0.0` from a previous version and you receive an error message like this:
|
||||||
|
|
||||||
|
```
|
||||||
|
[ERROR]: unable to create share (error getting zrok client: client version error accessing api endpoint 'https://api.zrok.io': [POST /clientVersionCheck] clientVersionCheck (status 404): {}: [POST /clientVersionCheck] clientVersionCheck (status 404): {})
|
||||||
|
```
|
||||||
|
|
||||||
|
Use the command `zrok rebase apiEndpoint https://api-v1.zrok.io/` to update your environment to the latest zrok endpoint.
|
||||||
|
:::
|
||||||
|
|
||||||
|
|
||||||
## Your Secure Internet Sharing Perimeter
|
## Your Secure Internet Sharing Perimeter
|
||||||
|
|
||||||
`zrok` (*/ziːɹɒk/ ZEE-rock*) is a secure, open-source, self-hostable sharing platform that simplifies shielding and sharing network services or files.
|
`zrok` (*/ziːɹɒk/ ZEE-rock*) is a secure, open-source, self-hostable sharing platform that simplifies shielding and sharing network services or files.
|
||||||
There's a hardened zrok-as-a-service offering available at [myzrok.io](https://myzrok.io) with a generous free tier.
|
There's a hardened zrok-as-a-service offering available at [myzrok.io](https://myzrok.io) with a generous free tier.
|
||||||
|
|
||||||
|
## What's it for?
|
||||||
|
|
||||||
|
Use `zrok` to share a running service, like a web server or a network socket, or to share a directory of static files. `zrok` goes beyond simple tunneling to provide sharing solutions for a variety of network and storage use cases.
|
||||||
|
|
||||||
|
When using `zrok` to [share publicly](./concepts/sharing-public.mdx), you can reserve a public hostname, enable authentication options, or both. Public shares proxy HTTPS to your service or files.
|
||||||
|
|
||||||
|
If [sharing privately](./concepts/sharing-private.mdx), only users with the share token (and the appropriate permission grants) can access your share. In addition to what you can share publicly, private shares can include TCP and UDP services.
|
||||||
|
|
||||||
|
Here's a quick overview of what's involved in getting started with `zrok`:
|
||||||
|
|
||||||
### Your First Share
|
### Your First Share
|
||||||
|
|
||||||
1. Get an account token
|
1. Get an account token
|
||||||
@ -51,7 +72,7 @@ There's a hardened zrok-as-a-service offering available at [myzrok.io](https://m
|
|||||||
</Columns>
|
</Columns>
|
||||||
|
|
||||||
2. [Download the zrok binary](#installing-the-zrok-command)
|
2. [Download the zrok binary](#installing-the-zrok-command)
|
||||||
3. Enable zrok for your [user environment](#enabling-your-zrok-environment)
|
3. Enable zrok for your [environment](#enabling-your-zrok-environment)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
zrok enable <your_account_token>
|
zrok enable <your_account_token>
|
||||||
@ -65,41 +86,11 @@ There's a hardened zrok-as-a-service offering available at [myzrok.io](https://m
|
|||||||
|
|
||||||
5. Visit the public URL displayed in your terminal
|
5. Visit the public URL displayed in your terminal
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
## Share Backend Modes
|
# A Deeper Look at Getting Started
|
||||||
|
|
||||||
zrok shares can be public or private, with different options for backend modes, including:
|
Here's a deeper, more thorough look at getting started with `zrok`:
|
||||||
|
|
||||||
* [Public shares](./concepts/sharing-public.mdx) for [web services](./concepts/http.md) or [files](./concepts/files.md)
|
|
||||||
* [Private shares for web services or files](./concepts/sharing-private.mdx)
|
|
||||||
* [TCP Tunnels](./concepts/tunnels.md)
|
|
||||||
* [UDP Tunnels](./concepts/tunnels.md)
|
|
||||||
* [File Drives](./guides/drives.mdx)
|
|
||||||
* [VPN](./guides/vpn/vpn.md)
|
|
||||||
|
|
||||||
|
|
||||||
## Open Source
|
|
||||||
|
|
||||||
`zrok` is licensed under Apache 2.0.
|
|
||||||
|
|
||||||
Check [the roadmap](https://github.com/orgs/openziti/projects/16) if you're thinking about the future. We would love to hear your ideas for `zrok`!
|
|
||||||
|
|
||||||
The best ways to engage are [Discourse](https://openziti.discourse.group/) for questions and [GitHub Issues](https://github.com/openziti/zrok/issues) for documenting problems.
|
|
||||||
|
|
||||||
[Read more about zrok open source](/concepts/opensource.md).
|
|
||||||
|
|
||||||
### Ziti native
|
|
||||||
|
|
||||||
`zrok` is a _Ziti Native Application_, built on the [OpenZiti](https://openziti.io) platform, and supported by the OpenZiti community and NetFoundry team.
|
|
||||||
|
|
||||||
## What's it for?
|
|
||||||
|
|
||||||
Use `zrok` to share a running service, like a web server or a network socket, or to share a directory of static files.
|
|
||||||
|
|
||||||
If [sharing publicly](./concepts/sharing-public.mdx), you can reserve a subdomain, enable authentication options, or both. Public shares proxy HTTPS to your service or files.
|
|
||||||
|
|
||||||
If [sharing privately](./concepts/sharing-private.mdx), only users with the share token can access your share. In addition to what you can share publicly, private shares can include TCP and UDP services.
|
|
||||||
|
|
||||||
## Installing the zrok Command
|
## Installing the zrok Command
|
||||||
|
|
||||||
@ -111,56 +102,64 @@ After you have [an account](#your-first-share), you can enable your `zrok` envir
|
|||||||
|
|
||||||
A zrok environment usually refers to an enabled device where shares and accesses can be created, .e.g., `~/.zrok` on a Unix machine. It can be a specific user's environment or a system-wide agent's environment owned by the administrator.
|
A zrok environment usually refers to an enabled device where shares and accesses can be created, .e.g., `~/.zrok` on a Unix machine. It can be a specific user's environment or a system-wide agent's environment owned by the administrator.
|
||||||
|
|
||||||
When your `zrok` account was created, the service generated a _secret token_ that identifies and authenticates in a single step. Protect your secret token as if it were a password, or an important account number; it's a _secret_, protect it.
|
When your `zrok` account was created, the service generated an _account token_ that identifies and authenticates in a single step. Protect your account token as if it were a password, or an important account number; it's a _secret_, protect it.
|
||||||
|
|
||||||
When we left off you had downloaded, extracted, and configured your `zrok` environment. In order to use that environment with your account, you'll need to `enable` it. Enabling an environment generates a secure identity and the necessary underlying security policies with the OpenZiti network hosting the `zrok` service.
|
When we left off you had downloaded, extracted, and configured your `zrok` software. In order to use that environment with your account, you'll need to `enable` an _environment_ on your system. Enabling an environment generates a secure identity and the necessary underlying security policies with the OpenZiti network hosting the `zrok` service so that you can begin sharing.
|
||||||
|
|
||||||
From the web console, click on your email address in the upper right corner of the header. That drop down menu contains an `Enable Your Environment` link. Click that link and a modal dialog will be shown like this:
|
Log into the API console at:
|
||||||
|
|
||||||

|
[https://api-v1.zrok.io/](https://api-v1.zrok.io/)
|
||||||
|
|
||||||
This dialog box shows you the `zrok enable` command that you can use to enable any shell to work with your `zrok` account with a single command.
|
When you first log into your account on the API console, your interface will look like this:
|
||||||
|
|
||||||
Let's copy that command and paste it into your shell:
|

|
||||||
|
|
||||||
```buttonless title="Example"
|
In the toolbar, there is a big green button that says "CLICK HERE TO GET STARTED!". If you click that button, you'll see the getting started wizard, which looks like this:
|
||||||
$ zrok enable klFEoIi0QAg7
|
|
||||||
|

|
||||||
|
|
||||||
|
This wizard is broken into multiple steps. The first step we've already covered, which gets the zrok software installed onto your system.
|
||||||
|
|
||||||
|
Below "step 2" is a command: `zrok enable 7g3K6gVKikWb` (your account will have a different account token, other than `7g3K6gVKikWb`). You'll want to copy this command into your shell and execute it:
|
||||||
|
|
||||||
|
```txt
|
||||||
|
$ zrok enable 7g3K6gVKikWb
|
||||||
⣻ contacting the zrok service...
|
⣻ contacting the zrok service...
|
||||||
```
|
```
|
||||||
|
|
||||||
After a few seconds, the message will change and indicate that the enable operation succeeded:
|
After a few seconds, the message will change and indicate that the enable operation succeeded:
|
||||||
|
|
||||||
```buttonless title="Example"
|
```txt
|
||||||
$ zrok enable klFEoIi0QAg7
|
$ zrok enable 7g3K6gVKikWb
|
||||||
⣻ the zrok environment was successfully enabled...
|
⣻ the zrok environment was successfully enabled...
|
||||||
```
|
```
|
||||||
|
|
||||||
Now, if we run a `zrok status` command, you will see the details of your environment:
|
Now, if we run a `zrok status` command, you will see the details of your environment:
|
||||||
|
|
||||||
```txt
|
```txt
|
||||||
zrok status
|
$ zrok status
|
||||||
```
|
|
||||||
|
|
||||||
```buttonless title="Output"
|
|
||||||
Config:
|
Config:
|
||||||
|
|
||||||
CONFIG VALUE SOURCE
|
CONFIG VALUE SOURCE
|
||||||
apiEndpoint https://api.staging.zrok.io env
|
apiEndpoint https://api-v1.zrok.io env
|
||||||
|
defaultFrontend public binary
|
||||||
|
headless false binary
|
||||||
|
|
||||||
Environment:
|
Environment:
|
||||||
|
|
||||||
PROPERTY VALUE
|
PROPERTY VALUE
|
||||||
Secret Token <<SET>>
|
Account Token <<SET>>
|
||||||
Ziti Identity <<SET>>
|
Ziti Identity <<SET>>
|
||||||
```
|
```
|
||||||
|
|
||||||
Excellent... our environment is now fully enabled.
|
Excellent... our environment is now fully enabled.
|
||||||
|
|
||||||
If we return to the _web console_, we'll now see the new environment reflected in the explorer view:
|
If we return to the _API console_, we'll now see the new environment reflected in the API console visualizer:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
In my case, the environment is named `michael@ziti-lx`, which is the username of my shell and the hostname of the system the shell is running on.
|
In my case, the environment is named `michael@testing`, which is the username of my shell and the hostname of the system the shell is running on.
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
Should you want to use a non-default name for your environment, you can pass the `-d` option to the `zrok enable` command. See `zrok enable --help` for details.
|
Should you want to use a non-default name for your environment, you can pass the `-d` option to the `zrok enable` command. See `zrok enable --help` for details.
|
||||||
@ -168,16 +167,12 @@ Should you want to use a non-default name for your environment, you can pass the
|
|||||||
|
|
||||||
If you click on the environment node in the explorer in the _web console_, the details panel shown at the bottom of the page will change:
|
If you click on the environment node in the explorer in the _web console_, the details panel shown at the bottom of the page will change:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
The explorer supports clicking, dragging, mouse wheel zooming, and selecting the nodes in the graph for more information (and available actions) for the selected node. If you ever get lost in the explorer, click the  _zoom to fit_ icon in the lower right corner of the explorer.
|
The visualizer supports clicking, dragging, mouse wheel zooming, and selecting the nodes in the graph for more information (and available actions) for the selected node. If you ever get lost in the visualizer, click the  _zoom to fit_ icon in the lower right corner of the explorer.
|
||||||
|
|
||||||
If we click on the `Detail` tab for our environment, we'll see something like:
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
:::note
|
:::note
|
||||||
With your `zrok` account you can `zrok enable` multiple environments. This will allow you to run `zrok share` in one environment, and `zrok access` in other environments.
|
With your `zrok` account you can `zrok enable` multiple environments. This will allow you to share (and access your shares) from multiple environments simultaneously.
|
||||||
:::
|
:::
|
||||||
|
|
||||||
Your environment is fully ready to go. Now we can move on to the fun stuff...
|
Your environment is fully ready to go. Now we can move on to the fun stuff...
|
||||||
@ -196,63 +191,30 @@ Resources that are shared _publicly_ are exposed to any users on the internet wh
|
|||||||
|
|
||||||
A frontend is an HTTPS listener exposed to the internet, that lets any user with your ephemeral share token access your publicly shared resources.
|
A frontend is an HTTPS listener exposed to the internet, that lets any user with your ephemeral share token access your publicly shared resources.
|
||||||
|
|
||||||
For example, I might create a public share using the `zrok share public` command, which results in my `zrok` instance exposing a URL like `https://2ptgbr8tlfvk.share.zrok.io` to access my resources.
|
For example, I might create a public share using the `zrok share public` command, which results in my `zrok` instance exposing a URL like `https://xxr2b7tzfx64.share.zrok.io` to access my resources.
|
||||||
|
|
||||||
In this case, my share was given the "share token" of `2ptgbr8tlfvk`. That URL can be given to any user, allowing them to immediately access the shared resources directly from my local environment, all without exposing any access to my private, secure environment. The physical network location of my environment is not exposed to anonymous consumers of my resources.
|
```
|
||||||
|
$ zrok share public --backend-mode web .
|
||||||
:::note
|
|
||||||
Here is the `--help` output from `zrok share public`:
|
|
||||||
|
|
||||||
```text
|
|
||||||
zrok share public
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```buttonless title="Output"
|
In this case, my share was given the "share token" of `xxr2b7tzfx64`. That URL can be given to any user, allowing them to immediately access the shared resources directly from my local environment, all without exposing any access to my private, secure environment. The physical network location of my environment is not exposed to anonymous consumers of my resources.
|
||||||
Error: accepts 1 arg(s), received 0
|
|
||||||
Usage:
|
|
||||||
zrok share public <target> [flags]
|
|
||||||
|
|
||||||
Flags:
|
|
||||||
--backend-mode string The backend mode {proxy, web, caddy, drive} (default "proxy")
|
|
||||||
--basic-auth stringArray Basic authentication users (<username:password>,...)
|
|
||||||
--frontends stringArray Selected frontends to use for the share (default [public])
|
|
||||||
--headless Disable TUI and run headless
|
|
||||||
-h, --help help for public
|
|
||||||
--insecure Enable insecure TLS certificate validation for <target>
|
|
||||||
|
|
||||||
Global Flags:
|
|
||||||
-p, --panic Panic instead of showing pretty errors
|
|
||||||
-v, --verbose Enable verbose logging
|
|
||||||
|
|
||||||
[ERROR]: an error occurred (accepts 1 arg(s), received 0)
|
|
||||||
```
|
|
||||||
|
|
||||||
`<target>` defines the path to the local resource that you intend to share. The form of `<target>` depends on the `--backend-mode` that you're using.
|
|
||||||
|
|
||||||
In the case of `--backend-mode proxy`, `<target>` should be a URL to an HTTP endpoint.
|
|
||||||
|
|
||||||
In the case of `--backend-mode web`, `<target>` is the path to a file on disk that serves as the "root" of the file tree to be shared.
|
|
||||||
:::
|
|
||||||
|
|
||||||
If we return to the web console, we see our share in the explorer:
|
If we return to the web console, we see our share in the explorer:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
If we click on our new share in the explorer, we can see the share details:
|
|
||||||

|
|
||||||
|
|
||||||
If we click on the _frontend endpoint_ a new browser tab opens and we see the content of our share:
|
If we click on the _frontend endpoint_ a new browser tab opens and we see the content of our share:
|
||||||

|

|
||||||
|
|
||||||
If we click on the environment in the explorer, we're shown all of the shares for that environment (including our new share), along with a spark line that shows the activity:
|
When we start accessing our share, notice the _sparkline_ graphs showing the activity:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
And as soon as I terminate the `zrok share` client, the resources are removed from the `zrok` environment.
|
And as soon as I terminate the `zrok share` client, the resources are removed from the `zrok` environment.
|
||||||
|
|
||||||
If we try to reload the frontend endpoint in our web browser, we'll see:
|
If we try to reload the frontend endpoint in our web browser, we'll see:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
[More about public shares](/concepts/sharing-public.mdx)
|
[More about public shares](/concepts/sharing-public.mdx)
|
||||||
|
|
||||||
@ -260,7 +222,7 @@ If we try to reload the frontend endpoint in our web browser, we'll see:
|
|||||||
|
|
||||||
`zrok` also provides a powerful _private_ sharing model. If I execute the following command:
|
`zrok` also provides a powerful _private_ sharing model. If I execute the following command:
|
||||||
|
|
||||||
```buttonless title="Example"
|
```buttonless
|
||||||
$ zrok share private http://localhost:8080
|
$ zrok share private http://localhost:8080
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -294,7 +256,7 @@ A reserved share can be re-used multiple times; it will survive termination of t
|
|||||||
|
|
||||||
The first step is to create the reserved share:
|
The first step is to create the reserved share:
|
||||||
|
|
||||||
```txt title="Example"
|
```txt
|
||||||
$ zrok reserve public --backend-mode web v0.3_getting_started
|
$ zrok reserve public --backend-mode web v0.3_getting_started
|
||||||
[ 0.275] INFO main.(*reserveCommand).run: your reserved share token is 'mltwsinym1s2'
|
[ 0.275] INFO main.(*reserveCommand).run: your reserved share token is 'mltwsinym1s2'
|
||||||
[ 0.275] INFO main.(*reserveCommand).run: reserved frontend endpoint: https://mltwsinym1s2.share.zrok.io
|
[ 0.275] INFO main.(*reserveCommand).run: reserved frontend endpoint: https://mltwsinym1s2.share.zrok.io
|
||||||
@ -306,13 +268,13 @@ You'll want to remember the share token (`mltwsinym1s2` in this case), and the f
|
|||||||
|
|
||||||
If we do nothing else, and then point a web browser at the frontend endpoint, we get:
|
If we do nothing else, and then point a web browser at the frontend endpoint, we get:
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
This is the `404` error message returned by the `zrok` frontend. We're getting this because we haven't yet started up a `zrok share` for the service. Let's do that:
|
This is the `404` error message returned by the `zrok` frontend. We're getting this because we haven't yet started up a `zrok share` for the service. Let's do that:
|
||||||
|
|
||||||
This command:
|
This command:
|
||||||
|
|
||||||
```txt title="Example"
|
```txt
|
||||||
$ zrok share reserved mltwsinym1s2
|
$ zrok share reserved mltwsinym1s2
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -328,7 +290,7 @@ With the reserved share, we're free to stop and restart the `zrok share reserved
|
|||||||
|
|
||||||
When we're done with the reserved share, we can _release_ it using this command:
|
When we're done with the reserved share, we can _release_ it using this command:
|
||||||
|
|
||||||
```txt title="Example"
|
```txt
|
||||||
$ zrok release mltwsinym1s2
|
$ zrok release mltwsinym1s2
|
||||||
[ 0.230] INFO main.(*releaseCommand).run: reserved share 'mltwsinym1s2' released
|
[ 0.230] INFO main.(*releaseCommand).run: reserved share 'mltwsinym1s2' released
|
||||||
```
|
```
|
||||||
|
@ -59,10 +59,10 @@ ZROK_ENABLE_TOKEN="14cbfca9772f"
|
|||||||
|
|
||||||
## Name your Share
|
## Name your Share
|
||||||
|
|
||||||
This unique name becomes part of the domain name of the share, e.g. `https://my-prod-app.in.zrok.io`. A random name is generated if you don't specify one.
|
This unique name becomes part of the domain name of the share, e.g. `https://toaster.share.zrok.io`. A random name is generated if you don't specify one. The name must be lowercase alphanumeric, between 4 and 32 characters in length.
|
||||||
|
|
||||||
```bash title="/opt/openziti/etc/zrok/zrok-share.env"
|
```bash title="/opt/openziti/etc/zrok/zrok-share.env"
|
||||||
ZROK_UNIQUE_NAME="my-prod-app"
|
ZROK_UNIQUE_NAME="toaster"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use Cases
|
## Use Cases
|
||||||
|
17
docs/guides/_linux-agent-install.mdx
Normal file
@ -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-agent
|
||||||
|
```
|
||||||
|
|
||||||
|
1. If you set up the repository by following the guide, then also install the `zrok-agent` package. This package provides the systemd service.
|
||||||
|
|
||||||
|
```bash title="Ubuntu, Debian"
|
||||||
|
sudo apt install zrok-agent
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash title="Fedora, Rocky"
|
||||||
|
sudo dnf install zrok-agent
|
||||||
|
```
|
8
docs/guides/agent/_category_.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"label": "The zrok Agent",
|
||||||
|
"position": 15,
|
||||||
|
"link": {
|
||||||
|
"type": "doc",
|
||||||
|
"id": "guides/agent/index"
|
||||||
|
}
|
||||||
|
}
|
109
docs/guides/agent/index.mdx
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
---
|
||||||
|
title: Agent
|
||||||
|
sidebar_position: 20
|
||||||
|
---
|
||||||
|
|
||||||
|
The zrok Agent centralizes management of your zrok shares and accesses. It provides both web-based and command-line interfaces, and changes how the `zrok share` and `zrok access` commands behave.
|
||||||
|
|
||||||
|
## Tutorial
|
||||||
|
|
||||||
|
Run the Agent in the foreground.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zrok agent
|
||||||
|
```
|
||||||
|
|
||||||
|
In another terminal, open the console.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zrok agent console
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see the Agent UI in your default web browser.
|
||||||
|
|
||||||
|
Start sharing a public share with the Agent.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zrok share public 8080
|
||||||
|
```
|
||||||
|
|
||||||
|
```buttonless title="Output"
|
||||||
|
token:"zje5x8p0k9pi" frontendEndpoints:"https://zje5x8p0k9pi.share.zrok.io"
|
||||||
|
```
|
||||||
|
|
||||||
|
You will see the new public share in the Agent UI and you can access it at the public share URL.
|
||||||
|
|
||||||
|
Reserve a private share for the Agent to share.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zrok reserve private 8080 --closed --unique-name "myshare"
|
||||||
|
```
|
||||||
|
|
||||||
|
```buttonless title="Output"
|
||||||
|
[ 1.883] INFO main.(*reserveCommand).run: your reserved share token is 'myshare'
|
||||||
|
```
|
||||||
|
|
||||||
|
Start sharing the reserved share with the Agent.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zrok share reserved "myshare"
|
||||||
|
```
|
||||||
|
|
||||||
|
```buttonless title="Output"
|
||||||
|
[ 0.001] INFO main.(*shareReservedCommand).shareAgent: starting
|
||||||
|
token:"myshare" backendMode:"proxy" shareMode:"private" target:"http://127.0.0.1:8080"
|
||||||
|
```
|
||||||
|
|
||||||
|
You will see the new reserved share in the Agent UI and you can access it by running `zrok access "myshare"` on another device where you have enabled the same zrok account, since the share was reserved with closed permission mode.
|
||||||
|
|
||||||
|
Check the status of the Agent's shares and accesses.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zrok agent status
|
||||||
|
```
|
||||||
|
|
||||||
|
```buttonless title="Output"
|
||||||
|
FRONTEND TOKEN TOKEN BIND ADDRESS
|
||||||
|
0 accesses in agent
|
||||||
|
|
||||||
|
TOKEN RESERVED SHARE MODE BACKEND MODE TARGET
|
||||||
|
myshare true private tcpTunnel 127.0.0.1:8080
|
||||||
|
1 share in agent
|
||||||
|
```
|
||||||
|
|
||||||
|
### Running the Agent in the background
|
||||||
|
|
||||||
|
You can keep the Agent running reliably in the background by installing the Agent service in Windows or Linux.
|
||||||
|
|
||||||
|
- Windows - [set up the Windows system service](/guides/agent/windows-service/index.mdx)
|
||||||
|
- Linux - [install the Linux package `zrok-agent`](/guides/agent/linux-service.mdx)
|
||||||
|
|
||||||
|
## How the Agent Works
|
||||||
|
|
||||||
|
### Centralized Management
|
||||||
|
|
||||||
|
Without the Agent running, each time you execute a `zrok share` or `zrok access` command, a separate process is created to handle that specific share or access.
|
||||||
|
|
||||||
|
When the Agent is running:
|
||||||
|
|
||||||
|
- All shares and accesses are managed by a single Agent process.
|
||||||
|
- The Agent provides a web UI for monitoring and managing your shares and accesses.
|
||||||
|
- The `zrok share` and `zrok access` commands delegate their operations to the running Agent.
|
||||||
|
- You can stop and restart individual shares/accesses without terminating the Agent.
|
||||||
|
- The Agent will remember and automatically restart your shares started with `share reserved`, and any accesses started with `access private`.
|
||||||
|
- The Agent will not restart regular, ephemeral shares started with `share private` or `share public`.
|
||||||
|
|
||||||
|
### Agent Console
|
||||||
|
|
||||||
|
The Agent provides a web-based console interface that can be accessed with:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zrok agent console
|
||||||
|
```
|
||||||
|
|
||||||
|
This command opens your default web browser to the Agent UI, where you can:
|
||||||
|
|
||||||
|
- View the status of all your active shares and accesses
|
||||||
|
- Create new shares and accesses using simple UI widgets
|
||||||
|
- Stop or restart existing shares and accesses
|
||||||
|
- Monitor traffic and connection statistics
|
42
docs/guides/agent/linux-service.mdx
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
---
|
||||||
|
title: Linux Agent Service
|
||||||
|
sidebar_position: 40
|
||||||
|
---
|
||||||
|
|
||||||
|
import LinuxAgentInstall from '/../docs/guides/_linux-agent-install.mdx'
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Run the zrok agent as a `systemd --user` service under your Linux user account.
|
||||||
|
|
||||||
|
## Install the Package
|
||||||
|
|
||||||
|
The package provides the `zrok` executable and the `zrok-agent.service` unit.
|
||||||
|
|
||||||
|
<LinuxAgentInstall />
|
||||||
|
|
||||||
|
## Enable your Account
|
||||||
|
|
||||||
|
This creates a `~/.zrok` directory enabled for your zrok account.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
zrok enable <your_account_token>
|
||||||
|
```
|
||||||
|
|
||||||
|
## Start the Service
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user enable --now zrok-agent.service
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use the agent
|
||||||
|
|
||||||
|
Learn more about using the zrok agent in the [agent guide](/guides/agent/index.mdx).
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Check the User Service Log
|
||||||
|
|
||||||
|
```bash
|
||||||
|
journalctl --user -lfu zrok-agent.service
|
||||||
|
```
|
BIN
docs/guides/agent/windows-service/images/empty-command-prompt.png
Executable file
After Width: | Height: | Size: 12 KiB |
BIN
docs/guides/agent/windows-service/images/enable.png
Executable file
After Width: | Height: | Size: 39 KiB |
BIN
docs/guides/agent/windows-service/images/nssm-cli-installation.png
Executable file
After Width: | Height: | Size: 43 KiB |
BIN
docs/guides/agent/windows-service/images/nssm-file-rotation.png
Executable file
After Width: | Height: | Size: 9.0 KiB |
BIN
docs/guides/agent/windows-service/images/nssm-install.png
Executable file
After Width: | Height: | Size: 8.6 KiB |
BIN
docs/guides/agent/windows-service/images/nssm-io.png
Executable file
After Width: | Height: | Size: 8.9 KiB |
BIN
docs/guides/agent/windows-service/images/program-files.png
Executable file
After Width: | Height: | Size: 31 KiB |
BIN
docs/guides/agent/windows-service/images/services-running.png
Executable file
After Width: | Height: | Size: 108 KiB |
BIN
docs/guides/agent/windows-service/images/services.png
Executable file
After Width: | Height: | Size: 106 KiB |
BIN
docs/guides/agent/windows-service/images/zrok-agent-console.png
Executable file
After Width: | Height: | Size: 34 KiB |
BIN
docs/guides/agent/windows-service/images/zrokdir.png
Executable file
After Width: | Height: | Size: 40 KiB |
144
docs/guides/agent/windows-service/index.mdx
Normal file
@ -0,0 +1,144 @@
|
|||||||
|
---
|
||||||
|
title: Configuring a Windows Service
|
||||||
|
sidebar_label: Windows Agent Service
|
||||||
|
---
|
||||||
|
|
||||||
|
In Windows environments, it can be useful to run the zrok Agent as a service, allowing it to automatically restart with your system.
|
||||||
|
|
||||||
|
Support for running the zrok Agent as a Windows service is handled through a third party utility, `nssm`, which is available here:
|
||||||
|
|
||||||
|
[https://nssm.cc/download](https://nssm.cc/download)
|
||||||
|
|
||||||
|
Give the `nssm` documentation a quick review, here:
|
||||||
|
|
||||||
|
[https://nssm.cc/usage](https://nssm.cc/usage)
|
||||||
|
|
||||||
|
You'll want to install both the `zrok.exe` and `nssm.exe` in a convenient, protected location. In this example, we'll put them in `C:\Program Files\zrok`, as `C:\Program Files\zrok\zrok.exe` and `C:\Program Files\zrok\nssm.exe`.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
We're going to use a command prompt to install and configure our Agent service. Open a new command prompt and `cd "\Program Files\zrok"`:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Enabling the Service Environment
|
||||||
|
|
||||||
|
On Windows, the `USERPROFILE` environment variable controls the "home" where zrok will look for the enabled environment. When running as a service on Windows, the process will be running as the `Local System` user, which uses the directory `C:\Windows\System32\config\systemprofile` directory as the user's "home" directory.
|
||||||
|
|
||||||
|
We're going to want to enable a zrok environment for our new Agent service in this directory. First, we'll run:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>set USERPROFILE=c:\Windows\System32\config\systemprofile
|
||||||
|
```
|
||||||
|
|
||||||
|
And with that environment variable set, we'll use the `zrok enable` command to enable an environment for our new Agent service:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>zrok enable <accountToken>
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Now we've got a new, enabled environment to use with our Agent running as a Windows service.
|
||||||
|
|
||||||
|
## Installing the Service
|
||||||
|
|
||||||
|
In the `C:\Program Files\zrok` directory, execute this command to invoke `nssm` to create the new Windows service for our zrok Agent:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>nssm install zrokAgent
|
||||||
|
```
|
||||||
|
|
||||||
|
Windows might ask you for elevated Administrator privileges and will then show the main `nssm` installation dialog, which we'll use to configure the new Windows service:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
We'll set "Path" to `C:\Program Files\zrok\zrok.exe` (the zrok executable). We'll set the "Startup directory" to `C:\Windows\System32\config\systemprofile` (the "home" directory for the `Local System` user). The "Arguments" are the command-line parameters that will get added to the command-line when starting the service, in this case, we want the service to start with the command `zrok agent start`.
|
||||||
|
|
||||||
|
We'll set the "Service name" to `zrokAgent`.
|
||||||
|
|
||||||
|
Next, scroll over to the "I/O" tab in the `nssm` installer:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Set the "Output (stdout)" to log the standard output from the `zrok agent start` process to the file `C:\Windows\System32\config\systemprofile\.zrok\agent-stdout.log`.
|
||||||
|
|
||||||
|
Set the "Error (stderr)" to log the standard error to the file `C:\Windows\System32\config\systemprofile\.zrok\agent-stderr.log`.
|
||||||
|
|
||||||
|
Setting the I/O redirection in this way will produce logs from the `zrok agent start` process that could be useful for future troubleshooting.
|
||||||
|
|
||||||
|
`nssm` also provides options for automatically rotating these log files:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
We'll leave these unset in this example, but if your configuration needs this kind of log file rotation `nssm` makes it available.
|
||||||
|
|
||||||
|
Finally, click the `Install service` button to create the service.
|
||||||
|
|
||||||
|
If we open the "Services" utility in Windows, we can see our new `zrokAgent` service:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If we click the start button in the toolbar, or right-click on the service and select "start", our new zrok Agent service will start:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
If we open a Windows Explorer in the folder `C:\Windows\System32\config\systemprofile\.zrok` we can see our logs, and the `agent.socket` which is used by the zrok command-line to interact with our Agent service:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
This zrok Agent service is now available for use. Whenever a Windows Command Prompt wants to interact with this environment, it is important that the `USERPROFILE` environment variable is properly set to `C:\Windows\System32\config\systemprofile`, otherwise the zrok commands will attempt to interact with the environment that would be created in the user's default profile directory.
|
||||||
|
|
||||||
|
We can access the Agent console using this command:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>zrok agent console
|
||||||
|
```
|
||||||
|
|
||||||
|
And this will open a web interface which allows the Agent to be managed:
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
The Agent console can be used to directly create shares and accesses, and the zrok command-line can also be used.
|
||||||
|
|
||||||
|
## Non-interactive Service Installation
|
||||||
|
|
||||||
|
`nssm` provides a command-line that can do this configuration without interacting with the `nssm` GUI. We would create our service like this:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>nssm install zrokAgent "C:\Program Files\zrok\zrok.exe" agent start
|
||||||
|
```
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>nssm set zrokAgent AppDirectory C:\Windows\System32\config\systemprofile
|
||||||
|
```
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>nssm set zrokAgent AppStdout C:\Windows\System32\config\systemprofile\.zrok\agent-stdout.log
|
||||||
|
```
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>nssm set zrokAgent AppStderr C:\Windows\System32\config\systemprofile\.zrok\agent-stderr.log
|
||||||
|
```
|
||||||
|
|
||||||
|
And we can start our new service using the standard Windows service control utility, `sc`:
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>sc start zrokAgent
|
||||||
|
```
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
## Removing the zrok Agent Service
|
||||||
|
|
||||||
|
The following commands (in a Command Prompt running as Administrator) can be used to remove the service from your system:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
C:\>sc stop zrokAgent
|
||||||
|
```
|
||||||
|
```cmd
|
||||||
|
C:\>sc delete zrokAgent
|
||||||
|
```
|
||||||
|
|
||||||
|
And if you have your `USERPROFILE` environment variable properly set to `C:\Windows\System32\config\systemprofile`, you can use this command to remove the environment from your system and from the zrok service:
|
||||||
|
|
||||||
|
```cmd
|
||||||
|
C:\Program Files\zrok>zrok disable
|
||||||
|
```
|
||||||
|
|
@ -41,10 +41,10 @@ When the project runs it will:
|
|||||||
|
|
||||||
1. Name the Share
|
1. Name the Share
|
||||||
|
|
||||||
This unique name becomes part of the domain name of the share, e.g. `https://my-prod-app.in.zrok.io`. A random name is generated if you don't specify one.
|
This unique name becomes part of the domain name of the share, e.g. `https://toaster.share.zrok.io`. A random name is generated if you don't specify one.
|
||||||
|
|
||||||
```bash title=".env"
|
```bash title=".env"
|
||||||
ZROK_UNIQUE_NAME="my-prod-app"
|
ZROK_UNIQUE_NAME="toaster"
|
||||||
```
|
```
|
||||||
|
|
||||||
1. Run the Compose project to start sharing the built-in demo web server. Be sure to `--detach` so the project runs in the background if you want it to auto-restart when your computer reboots.
|
1. Run the Compose project to start sharing the built-in demo web server. Be sure to `--detach` so the project runs in the background if you want it to auto-restart when your computer reboots.
|
||||||
@ -60,7 +60,7 @@ When the project runs it will:
|
|||||||
```
|
```
|
||||||
|
|
||||||
```buttonless title="Output"
|
```buttonless title="Output"
|
||||||
zrok-public-share-1 | https://w6r1vesearkj.in.zrok.io/
|
zrok-public-share-1 | https://w6r1vesearkj.share.zrok.io/
|
||||||
```
|
```
|
||||||
|
|
||||||
This concludes the minimum steps to begin sharing the demo web server. Read on to learn how to pivot to sharing any website or web service by leveraging additional zrok backend modes.
|
This concludes the minimum steps to begin sharing the demo web server. Read on to learn how to pivot to sharing any website or web service by leveraging additional zrok backend modes.
|
||||||
@ -149,5 +149,5 @@ With Caddy, you can balance the workload for websites or web services or share s
|
|||||||
```
|
```
|
||||||
|
|
||||||
```buttonless title="Output"
|
```buttonless title="Output"
|
||||||
INFO: zrok public URL: https://88s803f2qvao.in.zrok.io/
|
INFO: zrok public URL: https://88s803f2qvao.share.zrok.io/
|
||||||
```
|
```
|
||||||
|
@ -14,9 +14,9 @@ This page provides `docker` and `docker compose` examples of mounting the host's
|
|||||||
|
|
||||||
## Permanent Public Share
|
## Permanent Public Share
|
||||||
|
|
||||||
Let's say you have a `compose.yml` file that defines a web app known within the project's bridge network as `https://myapp:8080` and you want to publish it as a reliable, public site.
|
Let's say you have a `compose.yml` file that defines a web app known within the project's bridge network as `https://toaster:8080` and you want to publish it as a reliable, public site.
|
||||||
|
|
||||||
1. Reserve a subdomain by running `zrok reserve public --unique-name "myapp" https://myapp:8080` on the Docker host.
|
1. Reserve a subdomain by running `zrok reserve public --unique-name "toaster" https:toasterpp:8080` on the Docker host.
|
||||||
1. Merge this YAML with `compose.yml` or save it in the same directory as `compose.override.yml` to let `docker compose up` merge it for you.
|
1. Merge this YAML with `compose.yml` or save it in the same directory as `compose.override.yml` to let `docker compose up` merge it for you.
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@ -29,10 +29,10 @@ Let's say you have a `compose.yml` file that defines a web app known within the
|
|||||||
- ${HOME}/.zrok:/home/ziggy/.zrok
|
- ${HOME}/.zrok:/home/ziggy/.zrok
|
||||||
environment:
|
environment:
|
||||||
PFXLOG_NO_JSON: "true"
|
PFXLOG_NO_JSON: "true"
|
||||||
command: share reserved "myapp" --headless
|
command: share reserved "toaster" --headless
|
||||||
```
|
```
|
||||||
|
|
||||||
The reserved share will be available at `https://myapp.share.zrok.io` each time the `zrok` container starts up.
|
The reserved share will be available at `https://toaster.share.zrok.io` each time the `zrok` container starts up.
|
||||||
|
|
||||||
## Temporary Public Share
|
## Temporary Public Share
|
||||||
|
|
||||||
|
@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"label": "Linux User Share",
|
|
||||||
"position": 40,
|
|
||||||
"link": {
|
|
||||||
"type": "doc",
|
|
||||||
"id": "guides/linux-user-share/index"
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
---
|
|
||||||
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.
|
|
||||||
|
|
||||||
<LinuxShareInstall />
|
|
||||||
|
|
||||||
## 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.
|
|
BIN
docs/images/zrok-getting-started-button.png
Normal file
After Width: | Height: | Size: 223 KiB |
BIN
docs/images/zrok-getting-started-modal.png
Normal file
After Width: | Height: | Size: 328 KiB |
BIN
docs/images/zrok-not-found.png
Executable file
After Width: | Height: | Size: 256 KiB |
BIN
docs/images/zrok-reserved-not-found.png
Executable file
After Width: | Height: | Size: 260 KiB |
BIN
docs/images/zrok-share-public.png
Executable file
After Width: | Height: | Size: 109 KiB |
BIN
docs/images/zrok-visualizer-enabled.png
Normal file
After Width: | Height: | Size: 251 KiB |
BIN
docs/images/zrok-visualizer-environment.png
Normal file
After Width: | Height: | Size: 232 KiB |
BIN
docs/images/zrok-visualizer-public-share.png
Normal file
After Width: | Height: | Size: 307 KiB |
BIN
docs/images/zrok-visualizer-sparklines.png
Normal file
After Width: | Height: | Size: 308 KiB |
BIN
docs/images/zrok-zoom-to-fit.png
Normal file
After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 8.8 KiB After Width: | Height: | Size: 12 KiB |
BIN
docs/images/zrok_web_console.png
Executable file → Normal file
Before Width: | Height: | Size: 163 KiB After Width: | Height: | Size: 310 KiB |
Before Width: | Height: | Size: 2.0 KiB |
@ -6,7 +6,7 @@ title: Custom Domains
|
|||||||
[myzrok.io](https://myzrok.io) is a hosted zrok-as-a-service offering that provides a way for you bring a custom DNS name for zrok shares.
|
[myzrok.io](https://myzrok.io) is a hosted zrok-as-a-service offering that provides a way for you bring a custom DNS name for zrok shares.
|
||||||
For example, let's say you own the domain `foo.example.io`, you can leverage zrok custom domains to
|
For example, let's say you own the domain `foo.example.io`, you can leverage zrok custom domains to
|
||||||
create ephemeral shares such as: `https://vw8jbg4ijz5g.foo.example.io`
|
create ephemeral shares such as: `https://vw8jbg4ijz5g.foo.example.io`
|
||||||
or [reserved shares](/concepts/sharing-reserved.md) such as `https://myshare.foo.example.io`.
|
or [reserved shares](/concepts/sharing-reserved.md) such as `https://toaster.foo.example.io`.
|
||||||
|
|
||||||
Custom domains require a Pro subscription with [myzrok.io](https://myzrok.io).
|
Custom domains require a Pro subscription with [myzrok.io](https://myzrok.io).
|
||||||
If you don't already have an account, you can sign up for one [here](https://myzrok.io).
|
If you don't already have an account, you can sign up for one [here](https://myzrok.io).
|
||||||
|
@ -29,6 +29,7 @@ type Root interface {
|
|||||||
DeleteZitiIdentityNamed(name string) error
|
DeleteZitiIdentityNamed(name string) error
|
||||||
|
|
||||||
AgentSocket() (string, error)
|
AgentSocket() (string, error)
|
||||||
|
AgentRegistry() (string, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Environment struct {
|
type Environment struct {
|
||||||
|
@ -194,7 +194,11 @@ func (r *Root) DeleteZitiIdentityNamed(name string) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (r *Root) AgentSocket() (string, error) {
|
func (r *Root) AgentSocket() (string, error) {
|
||||||
return "", errors.Errorf("this environment version does not support agent sockets; please 'zrok update' this environment")
|
return "", errors.Errorf("this environment version does not support the zrok Agent; please 'zrok update' this environment")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *Root) AgentRegistry() (string, error) {
|
||||||
|
return "", errors.Errorf("this environment version does not support the zrok Agent; please 'zrok update' this environment")
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Root) Obliterate() error {
|
func (r *Root) Obliterate() error {
|
||||||
|
@ -196,6 +196,10 @@ func (r *Root) AgentSocket() (string, error) {
|
|||||||
return agentSocket()
|
return agentSocket()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Root) AgentRegistry() (string, error) {
|
||||||
|
return agentRegistry()
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Root) Obliterate() error {
|
func (r *Root) Obliterate() error {
|
||||||
zrd, err := rootDir()
|
zrd, err := rootDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -61,3 +61,11 @@ func agentSocket() (string, error) {
|
|||||||
}
|
}
|
||||||
return filepath.Join(zrd, "agent.socket"), nil
|
return filepath.Join(zrd, "agent.socket"), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func agentRegistry() (string, error) {
|
||||||
|
zrd, err := rootDir()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return filepath.Join(zrd, "agent-registry.json"), nil
|
||||||
|
}
|
||||||
|
16
nfpm/zrok-agent.service
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
# /usr/lib/systemd/user/zrok-agent.service
|
||||||
|
|
||||||
|
[Unit]
|
||||||
|
Description=zrok agent user service unit
|
||||||
|
After=network-online.target
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
UMask=0007
|
||||||
|
ExecStartPre=/usr/bin/env rm --force %h/.zrok/agent.socket
|
||||||
|
ExecStart=/opt/openziti/bin/zrok agent start
|
||||||
|
Restart=always
|
||||||
|
RestartSec=3
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=default.target
|
@ -84,7 +84,7 @@ ZROK_TARGET="" # e.g., http://127.0.0.1:3000
|
|||||||
#
|
#
|
||||||
|
|
||||||
# you MAY customize the share token that is used to construct the reserved subdomain; if not set a random
|
# you MAY customize the share token that is used to construct the reserved subdomain; if not set a random
|
||||||
# subdomain is reserved
|
# subdomain is reserved; lowercase alphanumeric, between 4 and 32 characters in length
|
||||||
# WARNING: changes take effect the next time the frontend URL is reserved
|
# WARNING: changes take effect the next time the frontend URL is reserved
|
||||||
#ZROK_UNIQUE_NAME=""
|
#ZROK_UNIQUE_NAME=""
|
||||||
|
|
||||||
|
@ -14,4 +14,4 @@ Restart=always
|
|||||||
RestartSec=3
|
RestartSec=3
|
||||||
|
|
||||||
[Install]
|
[Install]
|
||||||
WantedBy=multi-user.target
|
WantedBy=default.target
|
||||||
|
48
ui/package-lock.json
generated
@ -210,26 +210,26 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/helpers": {
|
"node_modules/@babel/helpers": {
|
||||||
"version": "7.26.0",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.0.tgz",
|
||||||
"integrity": "sha512-tbhNuIxNcVb21pInl3ZSjksLCvgdZy9KwJ8brv993QtIVKJBBkYXz4q4ZbAv31GdnC+R90np23L5FbEBlthAEw==",
|
"integrity": "sha512-U5eyP/CTFPuNE3qk+WZMxFkp/4zUzdceQlfzf7DdGdhp+Fezd7HD+i8Y24ZuTMKX3wQBld449jijbGq6OdGNQg==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/template": "^7.25.9",
|
"@babel/template": "^7.27.0",
|
||||||
"@babel/types": "^7.26.0"
|
"@babel/types": "^7.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/parser": {
|
"node_modules/@babel/parser": {
|
||||||
"version": "7.26.2",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.2.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.27.0.tgz",
|
||||||
"integrity": "sha512-DWMCZH9WA4Maitz2q21SRKHo9QXZxkDsbNZoVD62gusNtNBBqDg9i7uOhASfTfIGNzW+O+r7+jAlM8dwphcJKQ==",
|
"integrity": "sha512-iaepho73/2Pz7w2eMS0Q5f83+0RKI7i4xmiYeBmDzfRVbQtTOG7Ts0S4HzJVsTMGI9keU8rNfuZr8DKfSt7Yyg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/types": "^7.26.0"
|
"@babel/types": "^7.27.0"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"parser": "bin/babel-parser.js"
|
"parser": "bin/babel-parser.js"
|
||||||
@ -271,9 +271,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/runtime": {
|
"node_modules/@babel/runtime": {
|
||||||
"version": "7.26.0",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.0.tgz",
|
||||||
"integrity": "sha512-FDSOghenHTiToteC/QRlv2q3DhPZ/oOXTBoirfWNx1Cx3TMVcGWQtMMmQcSvb/JjpNeGzx8Pq/b4fKEJuWm1sw==",
|
"integrity": "sha512-VtPOkrdPHZsKc/clNqyi9WUA8TINkZ4cGk63UUE3u4pmB2k+ZMQRDuIOagv8UVd6j7k0T3+RRIb7beKTebNbcw==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"regenerator-runtime": "^0.14.0"
|
"regenerator-runtime": "^0.14.0"
|
||||||
@ -283,14 +283,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/template": {
|
"node_modules/@babel/template": {
|
||||||
"version": "7.25.9",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.25.9.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.0.tgz",
|
||||||
"integrity": "sha512-9DGttpmPvIxBb/2uwpVo3dqJ+O6RooAFOS+lB+xDqoE2PVCE8nfoHMdZLpfCQRLwvohzXISPZcgxt80xLfsuwg==",
|
"integrity": "sha512-2ncevenBqXI6qRMukPlXwHKHchC7RyMuu4xv5JBXRfOGVcTy1mXCD12qrp7Jsoxll1EV3+9sE4GugBVRjT2jFA==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/code-frame": "^7.25.9",
|
"@babel/code-frame": "^7.26.2",
|
||||||
"@babel/parser": "^7.25.9",
|
"@babel/parser": "^7.27.0",
|
||||||
"@babel/types": "^7.25.9"
|
"@babel/types": "^7.27.0"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=6.9.0"
|
"node": ">=6.9.0"
|
||||||
@ -324,9 +324,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@babel/types": {
|
"node_modules/@babel/types": {
|
||||||
"version": "7.26.0",
|
"version": "7.27.0",
|
||||||
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.0.tgz",
|
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.27.0.tgz",
|
||||||
"integrity": "sha512-Z/yiTPj+lDVnF7lWeKCIJzaIkI0vYO87dMpZ4bg4TDrFe4XXLFWL1TbXU27gBP3QccxV9mZICCrnjnYlJjXHOA==",
|
"integrity": "sha512-H45s8fVLYjbhFH62dIJ3WtmJ6RSPt/3DRO0ZcT2SUiYiQyz3BLVb9ADEnLl91m74aQPS3AzzeajZHYOalWe3bg==",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@babel/helper-string-parser": "^7.25.9",
|
"@babel/helper-string-parser": "^7.25.9",
|
||||||
@ -4739,9 +4739,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/vite": {
|
"node_modules/vite": {
|
||||||
"version": "6.2.0",
|
"version": "6.2.4",
|
||||||
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/vite/-/vite-6.2.4.tgz",
|
||||||
"integrity": "sha512-7dPxoo+WsT/64rDcwoOjk76XHj+TqNTIvHKcuMQ1k4/SeHDaQt5GFAeLYzrimZrMpn/O6DtdI03WUjdxuPM0oQ==",
|
"integrity": "sha512-veHMSew8CcRzhL5o8ONjy8gkfmFJAd5Ac16oxBUjlwgX3Gq2Wqr+qNC3TjPIpy7TPV/KporLga5GT9HqdrCizw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|