From 9625a6f9f61b9bf178ef8b8745784c70e17125de Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Fri, 22 Mar 2024 11:08:47 -0400 Subject: [PATCH 1/6] add Linux package openziti-router; add bootstrapping to Docker container ziti-router --- .github/workflows/publish-docker-images.yml | 7 +- dist/cloudfront/get.openziti.io/routes.yml | 4 + .../linux/nfpm-openziti-router.yaml | 40 ++++ .../linux/openziti-router/bootstrap.bash | 77 +++++++ .../linux/openziti-router/entrypoint.bash | 33 +++ dist/dist-packages/linux/openziti-router/env | 43 ++++ .../linux/openziti-router/postinstall.bash | 215 ++++++++++++++++++ .../linux/openziti-router/preremove.bash | 15 ++ .../linux/openziti-router/ziti-router.service | 67 ++++++ dist/docker-images/ziti-cli/Dockerfile | 7 +- dist/docker-images/ziti-cli/entrypoint.sh | 5 - dist/docker-images/ziti-router/Dockerfile | 31 ++- dist/docker-images/ziti-router/README.md | 92 ++++++++ dist/docker-images/ziti-router/compose.yml | 66 ++++++ dist/docker-images/ziti-router/entrypoint.sh | 5 - ziti/cmd/create/config_templates/router.yml | 8 +- ziti/cmd/create/create_config.go | 6 +- ziti/cmd/create/create_config_environment.go | 6 +- ziti/cmd/create/create_config_router_edge.go | 1 + .../create/create_config_router_edge_test.go | 2 +- .../create_config_router_fabric_test.go | 2 +- ziti/cmd/create/create_config_test.go | 2 + ziti/cmd/helpers/env_helpers.go | 4 + ziti/constants/constants.go | 6 + 24 files changed, 709 insertions(+), 35 deletions(-) create mode 100644 dist/dist-packages/linux/nfpm-openziti-router.yaml create mode 100755 dist/dist-packages/linux/openziti-router/bootstrap.bash create mode 100755 dist/dist-packages/linux/openziti-router/entrypoint.bash create mode 100644 dist/dist-packages/linux/openziti-router/env create mode 100755 dist/dist-packages/linux/openziti-router/postinstall.bash create mode 100755 dist/dist-packages/linux/openziti-router/preremove.bash create mode 100644 dist/dist-packages/linux/openziti-router/ziti-router.service delete mode 100644 dist/docker-images/ziti-cli/entrypoint.sh create mode 100644 dist/docker-images/ziti-router/README.md create mode 100644 dist/docker-images/ziti-router/compose.yml delete mode 100644 dist/docker-images/ziti-router/entrypoint.sh diff --git a/.github/workflows/publish-docker-images.yml b/.github/workflows/publish-docker-images.yml index d2d29ac27..28688ce0f 100644 --- a/.github/workflows/publish-docker-images.yml +++ b/.github/workflows/publish-docker-images.yml @@ -116,19 +116,20 @@ jobs: if [[ "${{ github.ref }}" == "refs/heads/main" ]]; then DOCKER_TAGS+=",${IMAGE_REPO}:latest" fi - echo "DEBUG: DOCKER_TAGS=${DOCKER_TAGS}" - echo DOCKER_TAGS="${DOCKER_TAGS}" >> $GITHUB_OUTPUT + echo DOCKER_TAGS="${DOCKER_TAGS}" | tee -a $GITHUB_OUTPUT - name: Build & Push Multi-Platform Router Container Image to Hub uses: docker/build-push-action@v3 with: builder: ${{ steps.buildx.outputs.name }} - context: ${{ github.workspace }}/dist/docker-images/ziti-router/ + context: ${{ github.workspace }}/ + file: ${{ github.workspace }}/dist/docker-images/ziti-router/Dockerfile platforms: linux/amd64,linux/arm64 tags: ${{ steps.tagprep_router.outputs.DOCKER_TAGS }} build-args: | ZITI_CLI_TAG=${{ env.ZITI_CLI_TAG }} ZITI_CLI_IMAGE=${{ env.ZITI_CLI_IMAGE }} + DOCKER_BUILD_DIR=./dist/docker-images/ziti-router push: true - name: Set Up Container Image Tags for Go Tunneler Container diff --git a/dist/cloudfront/get.openziti.io/routes.yml b/dist/cloudfront/get.openziti.io/routes.yml index b6e5a1d75..e893bb4e5 100644 --- a/dist/cloudfront/get.openziti.io/routes.yml +++ b/dist/cloudfront/get.openziti.io/routes.yml @@ -38,3 +38,7 @@ - get: /zdew/ raw: /openziti/desktop-edge-win/main/release-streams/ file: latest.json + +- get: /dist/ + raw: /openziti/ziti/{{GITHUB_SHA}}/dist/ + file: /docker-images/ziti-router/compose.yml diff --git a/dist/dist-packages/linux/nfpm-openziti-router.yaml b/dist/dist-packages/linux/nfpm-openziti-router.yaml new file mode 100644 index 000000000..bce61b2db --- /dev/null +++ b/dist/dist-packages/linux/nfpm-openziti-router.yaml @@ -0,0 +1,40 @@ +# nfpm configuration file +# +# check https://nfpm.goreleaser.com/configuration for detailed usage +# +name: openziti-router +arch: ${GOARCH} +platform: linux +version: ${ZITI_VERSION} +maintainer: ${ZITI_MAINTAINER} +description: > + Provides a system service for running an OpenZiti Router +vendor: ${ZITI_VENDOR} +homepage: ${ZITI_HOMEPAGE} +license: Apache-2.0 +# Contents to add to the package. +contents: + - dst: /lib/systemd/system/ + src: ./dist/dist-packages/linux/openziti-router/ziti-router.service + + - dst: /opt/openziti/etc/router + type: dir + file_info: + mode: 0755 + + - dst: /opt/openziti/etc/router/ + src: ./dist/dist-packages/linux/openziti-router/env + type: config|noreplace + + - dst: /opt/openziti/etc/router/ + src: ./dist/dist-packages/linux/openziti-router/bootstrap.bash + + - dst: /opt/openziti/etc/router/ + src: ./dist/dist-packages/linux/openziti-router/entrypoint.bash + +scripts: + postinstall: ./dist/dist-packages/linux/openziti-router/postinstall.bash + preremove: ./dist/dist-packages/linux/openziti-router/preremove.bash + +depends: + - openziti # ziti CLI diff --git a/dist/dist-packages/linux/openziti-router/bootstrap.bash b/dist/dist-packages/linux/openziti-router/bootstrap.bash new file mode 100755 index 000000000..2aa51bed9 --- /dev/null +++ b/dist/dist-packages/linux/openziti-router/bootstrap.bash @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# +# bootstrap the OpenZiti Router with a config file and identity +# + +function makeConfig() { + # + # create config file + # + + if [[ ! -s "${ZITI_ROUTER_CONFIG_FILE}" || "${1:-}" == --force ]]; then + ziti create config router "${ZITI_ROUTER_TYPE}" \ + --tunnelerMode "${ZITI_ROUTER_MODE}" \ + --routerName "${ZITI_ROUTER_NAME}" \ + --output "${ZITI_ROUTER_CONFIG_FILE}" + fi + +} + +function enroll() { + + # shellcheck disable=SC1090 # find the identity file path + source <(ziti create config environment | grep ZITI_ROUTER) + + if [[ ! -s "${ZITI_ROUTER_IDENTITY_CERT}" || "${1:-}" == --force ]]; then + if [ -n "${ZITI_ENROLL_TOKEN:-}" ]; then + # shellcheck disable=SC2188 + ziti router enroll "${ZITI_ROUTER_CONFIG_FILE}" \ + --jwt <(echo "${ZITI_ENROLL_TOKEN}") + elif [ -s "/run/credentials/${UNIT_NAME:=ziti-router.service}/ZITI_ENROLL_TOKEN" ]; then + ziti router enroll "${ZITI_ROUTER_CONFIG_FILE}" \ + --jwt "/run/credentials/${UNIT_NAME}/ZITI_ENROLL_TOKEN" + else + echo "ERROR: use SetCredential or LoadCredential in"\ + " /lib/systemd/system/ziti-router.service or set env var ZITI_ENROLL_TOKEN" >&2 + fi + fi + +} + +function bootstrap() { + + if [ -n "${1:-}" ]; then + ZITI_ROUTER_CONFIG_FILE="${1}" + else + echo "ERROR: no config file path provided" >&2 + return 1 + fi + + # make config file unless it exists if true, set force to overwrite + if [ "${ZITI_BOOTSTRAP_CONFIG}" == true ]; then + makeConfig + elif [ "${ZITI_BOOTSTRAP_CONFIG}" == force ]; then + makeConfig --force + fi + + # enroll unless certificate exists, set "force" to overwrite key and cert (requires new enrollment token) + if [ "${ZITI_BOOTSTRAP_ENROLLMENT}" == true ]; then + enroll + elif [ "${ZITI_BOOTSTRAP_ENROLLMENT}" == force ]; then + enroll --force + fi +} + +# +# defaults +# + +# used by "ziti create config router" and "ziti create config environment" +: "${ZITI_ROUTER_ADVERTISED_ADDRESS:=${HOSTNAME:=$(hostname -f)}}" +: "${ZITI_ROUTER_NAME:=${HOSTNAME%%.*}}" +: "${ZITI_CTRL_ADVERTISED_PORT:=1280}" +export ZITI_ROUTER_NAME \ + ZITI_ROUTER_ADVERTISED_ADDRESS \ + ZITI_CTRL_ADVERTISED_PORT \ + ZITI_ROUTER_PORT \ + ZITI_ROUTER_LISTENER_BIND_PORT="${ZITI_ROUTER_PORT}" diff --git a/dist/dist-packages/linux/openziti-router/entrypoint.bash b/dist/dist-packages/linux/openziti-router/entrypoint.bash new file mode 100755 index 000000000..11dac254e --- /dev/null +++ b/dist/dist-packages/linux/openziti-router/entrypoint.bash @@ -0,0 +1,33 @@ +#!/usr/bin/env bash +# +# this thin wrapper script for the OpenZiti Router uses variable assignments from the systemd env file +# + +set -o errexit +set -o nounset +set -o pipefail + +if ! (( $# )); then + # if no args, run the router with the default config file + set -- run config.yml +elif [[ "${1}" == run && -z "${2:-}" ]]; then + # if first arg is "run" and second arg is empty, run the router with the default config file + set -- run config.yml +fi + +# shellcheck disable=SC1090 # default path is assigned in env file +source "${ZITI_ROUTER_BOOTSTRAP_BASH:-/opt/openziti/etc/router/bootstrap.bash}" + +# if first arg is "run", bootstrap the router with the config file +if [ "${1}" == run ]; then + bootstrap "${2}" +fi + +# optionally renew certs at startup +if [ "${ZITI_AUTO_RENEW_CERTS:-}" == true ]; then + # shellcheck disable=SC2068 + set -- ${@} --extend +fi + +# shellcheck disable=SC2068 +exec ziti router ${@} diff --git a/dist/dist-packages/linux/openziti-router/env b/dist/dist-packages/linux/openziti-router/env new file mode 100644 index 000000000..bbeb4b59d --- /dev/null +++ b/dist/dist-packages/linux/openziti-router/env @@ -0,0 +1,43 @@ +# +# this is a systemd env file allowing simple assignments for ziti-controller.service environment and serves as an answer +# file for first run prompts and unattended installations; only variables that are consumed by the ziti binary are +# expected here to preserve the separation between the service unit and the binary +# + +# +# for "ziti create config router edge" commands in bootstrap.bash +# + +# address of the controller (required) +ZITI_CTRL_ADVERTISED_ADDRESS= +# tcp port of the controller (default: 1280) +ZITI_CTRL_ADVERTISED_PORT= + +# for better security, leave this assignment empty and create a file readable only by root containing the +# token and set "LoadCredential=ZITI_ENROLL_TOKEN:/opt/openziti/etc/router/.token" in +# /lib/systemd/system/ziti-router.service +ZITI_ENROLL_TOKEN= + +# the router's address must be resolvable by other routers and edge identities (default: qualified hostname) +ZITI_ROUTER_ADVERTISED_ADDRESS= +# the advertised and listening port of the router, if <= 1024, then grant the NET_BIND_SERVICE ambient capability in +# /lib/systemd/system/ziti-router.service (default: 3022) +ZITI_ROUTER_PORT= + +# the mode of the router; the router must be administratively created with the --tunneler-enabled flag; if "tproxy" mode +# then grant ambient capbility NET_ADMIN in /lib/systemd/system/ziti-router.service and set the host's DNS resolvers to +# have this router's nameserver as the primary in additional to a secondary, recursive resolver (host, tproxy, proxy; +# default: host) +ZITI_ROUTER_MODE= +# where to listen for DNS requests in tproxy mode (default: udp://127.0.0.1:53) +ZITI_ROUTER_TPROXY_RESOLVER= + +# the interface address on which to listen (default: 0.0.0.0) +ZITI_ROUTER_BIND_ADDRESS= + +# set identity filenames (default: unqualified hostname) +# ZITI_ROUTER_NAME= + +# type of router (default: edge, options: edge, fabric) +ZITI_ROUTER_TYPE=edge + diff --git a/dist/dist-packages/linux/openziti-router/postinstall.bash b/dist/dist-packages/linux/openziti-router/postinstall.bash new file mode 100755 index 000000000..42005bacb --- /dev/null +++ b/dist/dist-packages/linux/openziti-router/postinstall.bash @@ -0,0 +1,215 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail +# set -o xtrace + +install() { + checkSystemdVersion $MINIMUM_SYSTEMD_VERSION + commonActions + +} + +upgrade() { + # Step 2(upgrade), do what you need + commonActions + +} + +commonActions() { + makeTokenFile + loadEnv + promptCtrlAdvertisedAddress + promptRouterAdvertisedAddress + promptEnrollToken + promptRouterMode + promptRouterPort +} + +checkSystemdVersion() { + # Step 2 (clean install), enable the service in the proper way for this platform + if ! command -V systemctl &>/dev/null; then + echo "ERROR: required command 'systemctl' is missing" >&2 + return 1 + else + systemd_version=$(systemctl --version | awk '/^systemd/ {print $2}') + fi + + if [ "${systemd_version}" -lt "$1" ]; then + printf "\033[31m systemd version %s is less then 232, aborting \033[0m\n" "${systemd_version}" + return 1 + fi +} + +makeTokenFile() { + # unless it exists, create an empty enrollment token file with restrictive permissions so the service can start with + # LoadCredential enabled + if ! [ -s "${ZITI_ENROLL_TOKEN_FILE}" ]; then + umask 0177 + touch "${ZITI_ENROLL_TOKEN_FILE}" + fi +} + +prompt() { + # return true if interactive and response is not empty + if [[ "${DEBIAN_FRONTEND:-}" != "noninteractive" && -t 0 ]]; then + read -r -p "$1" response + if [ -n "${response:-}" ]; then + echo "${response}" + else + return 1 + fi + else + echo "WARN: non-interactive, unable to prompt for answer: '$1'" >&2 + return 1 + fi +} + +loadEnv() { + # shellcheck disable=SC1091 + source /opt/openziti/etc/router/env +} + +promptCtrlAdvertisedAddress() { + if [ -z "${ZITI_CTRL_ADVERTISED_ADDRESS:-}" ]; then + if ZITI_CTRL_ADVERTISED_ADDRESS="$(prompt 'Enter the advertised address for the controller: ')"; then + if [ -n "${ZITI_CTRL_ADVERTISED_ADDRESS:-}" ]; then + sed -Ei "s/^(ZITI_CTRL_ADVERTISED_ADDRESS)=.*/\1=${ZITI_CTRL_ADVERTISED_ADDRESS}/" /opt/openziti/etc/router/env + fi + else + echo "WARN: missing ZITI_CTRL_ADVERTISED_ADDRESS in /opt/openziti/etc/router/env" >&2 + fi + fi +} + +promptRouterAdvertisedAddress() { + if [ -z "${ZITI_ROUTER_ADVERTISED_ADDRESS:-}" ]; then + DEFAULT_ADDR="${HOSTNAME:=$(hostname -f)}}" + if ZITI_ROUTER_ADVERTISED_ADDRESS="$(prompt "Enter the advertised address for this router [$DEFAULT_ADDR]: " || echo "$DEFAULT_ADDR")"; then + sed -Ei "s/^(ZITI_ROUTER_ADVERTISED_ADDRESS)=.*/\1=${ZITI_ROUTER_ADVERTISED_ADDRESS}/" /opt/openziti/etc/router/env + fi + fi +} + +promptEnrollToken() { + # make ziti vars available in "ziti create config environment" + exportZitiVars + # shellcheck disable=SC1090 # compute the path to the identity file + source <(ZITI_HOME=/var/lib/ziti-router ziti create config environment) + # do nothing if identity file has stuff in it + if [ -s "${ZITI_ROUTER_IDENTITY_CERT}" ]; then + echo "INFO: enrolled identity exists in ${ZITI_ROUTER_IDENTITY_CERT}" + # prompt for enrollment token if interactive, unless already answered + else + ZITI_BOOTSTRAP_ENROLLMENT=$(awk -F= '/^Environment=ZITI_BOOTSTRAP_ENROLLMENT=/ {print $3}' /lib/systemd/system/ziti-router.service) + if ! [[ "${ZITI_BOOTSTRAP_ENROLLMENT:-}" == true ]]; then + echo "INFO: ZITI_BOOTSTRAP_ENROLLMENT is not true in /lib/systemd/system/ziti-router.service" >&2 + # do nothing if enrollment token is already defined in env file + elif [[ -n "${ZITI_ENROLL_TOKEN:-}" ]]; then + echo "INFO: ZITI_ENROLL_TOKEN is defined in /opt/openziti/etc/router/env and will be used to enroll during"\ + "next startup" + elif grep -qE "^LoadCredential=ZITI_ENROLL_TOKEN=${ZITI_ENROLL_TOKEN_FILE}" \ + /lib/systemd/system/ziti-router.service \ + && [[ -s "${ZITI_ENROLL_TOKEN_FILE}" ]]; then + echo "INFO: ZITI_ENROLL_TOKEN is defined in ${ZITI_ENROLL_TOKEN_FILE} and will be used to"\ + "enroll during next startup " + elif grep -qE '^SetCredential=ZITI_ENROLL_TOKEN:.+' /lib/systemd/system/ziti-router.service; then + echo "INFO: ZITI_ENROLL_TOKEN is defined in /lib/systemd/system/ziti-router.service and will be used to"\ + "enroll during next startup" + else + if ZITI_ENROLL_TOKEN=$(prompt "Enter the enrollment token: "); then + if [ -n "${ZITI_ENROLL_TOKEN:-}" ]; then + echo "$ZITI_ENROLL_TOKEN" >| /opt/openziti/etc/router/.token + fi + else + echo "WARN: missing ZITI_ENROLL_TOKEN; use LoadCredential or SetCredential in"\ + "/lib/systemd/system/ziti-router.service or set in /opt/openziti/etc/router/env" >&2 + fi + fi + fi +} + +promptRouterMode() { + # if undefined or default value in env file, prompt for router mode, preserving default if no answer + if [[ -z "${ZITI_ROUTER_MODE:-}" ]]; then + if ZITI_ROUTER_MODE="$(prompt 'Enter the router mode (eg. host, tproxy, proxy) [host]: ' || echo 'host')"; then + sed -Ei "s/^(ZITI_ROUTER_MODE)=.*/\1=${ZITI_ROUTER_MODE}/" /opt/openziti/etc/router/env + fi + fi + if [[ "${ZITI_ROUTER_MODE}" == tproxy ]]; then + grantNetAdmin + fi +} + +grantNetAdmin() { + # grant ambient capabilities to the router process if not already granted + if ! grep -qE '^AmbientCapabilities=CAP_NET_ADMIN' /lib/systemd/system/ziti-router.service; then + # uncomment the line + sed -Ei 's/.*AmbientCapabilities=CAP_NET_ADMIN/AmbientCapabilities=CAP_NET_ADMIN/' /lib/systemd/system/ziti-router.service + fi + systemctl daemon-reload +} + +promptRouterPort() { + # if undefined or default value in env file, prompt for router port, preserving default if no answer + if [[ -z "${ZITI_ROUTER_PORT:-}" ]]; then + if ZITI_ROUTER_PORT="$(prompt 'Enter the router port [3022]: ' || echo '3022')"; then + sed -Ei "s/^(ZITI_ROUTER_PORT)=.*/\1=${ZITI_ROUTER_PORT}/" /opt/openziti/etc/router/env + fi + fi + if [[ "${ZITI_ROUTER_PORT}" -le 1024 ]]; then + grantNetBindService + fi +} + +grantNetBindService() { + # grant binding privileged low ports unless already granted + if ! grep -qE '^AmbientCapabilities=CAP_NET_BIND_SERVICE' /lib/systemd/system/ziti-router.service; then + # uncomment the line + sed -Ei 's/.*AmbientCapabilities=CAP_NET_BIND_SERVICE/AmbientCapabilities=CAP_NET_BIND_SERVICE/' /lib/systemd/system/ziti-router.service + fi + systemctl daemon-reload +} + +exportZitiVars() { + # make ziti vars available in forks like "ziti create config environment" + for line in $(set | grep -e "^ZITI_" | sort); do + # shellcheck disable=SC2013 + for var in $(awk -F= '{print $1}' <<< "$line"); do + # shellcheck disable=SC2163 + export "$var" + done + done +} + +MINIMUM_SYSTEMD_VERSION=232 +ZITI_ENROLL_TOKEN_FILE=/opt/openziti/etc/router/.token + +# Step 1, check if this is a clean install or an upgrade +if (( $# )); then + if [[ $1 == 1 || ($1 == configure && -z ${2:-}) ]]; then + # deb passes $1=configure, rpm passes $1=1 + action=install + elif [[ $1 == 2 || ($1 == configure && -n ${2:-}) ]]; then + # deb passes $1=configure $2=, rpm passes $1=2 + action=upgrade + else + echo "ERROR: unexpected action '$1'" >&2 + exit 1 + fi +else + echo "ERROR: missing action" >&2 + exit 1 +fi + +case "$action" in + "install") + printf "\033[32m Post Install of an clean install\033[0m\n" + install + ;; + "upgrade") + printf "\033[32m Post Install of an upgrade\033[0m\n" + upgrade + ;; +esac diff --git a/dist/dist-packages/linux/openziti-router/preremove.bash b/dist/dist-packages/linux/openziti-router/preremove.bash new file mode 100755 index 000000000..e7d272675 --- /dev/null +++ b/dist/dist-packages/linux/openziti-router/preremove.bash @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +set -o errexit +set -o nounset +set -o pipefail +# set -o xtrace + +# if it exists and is still empty, clean up the enrollment token file that was created by postinstall.bash, allowing the +# package manager to remove the empty directory +ZITI_ENROLL_TOKEN_FILE=/opt/openziti/etc/router/.token +if [ -e "${ZITI_ENROLL_TOKEN_FILE}" ]; then + if ! [ -s "${ZITI_ENROLL_TOKEN_FILE}" ]; then + rm -f "${ZITI_ENROLL_TOKEN_FILE}" + fi +fi diff --git a/dist/dist-packages/linux/openziti-router/ziti-router.service b/dist/dist-packages/linux/openziti-router/ziti-router.service new file mode 100644 index 000000000..df9d943b4 --- /dev/null +++ b/dist/dist-packages/linux/openziti-router/ziti-router.service @@ -0,0 +1,67 @@ +[Unit] +Description=OpenZiti Router +After=network-online.target + +[Service] + +# +## Required Configuration +# + +# you must provide an enrollment token to enroll the router at first startup in the .token file or by temporarily setting env var +# ZITI_ENROLL_TOKEN +# load enrollment token from a file readable only by root for better security; null the file after bootstrapping +LoadCredential=ZITI_ENROLL_TOKEN:/opt/openziti/etc/router/.token + +# or, temporarily set one-time enrollment token as literal string +# SetCredential=ZITI_ENROLL_TOKEN: + + +# +## extra permissions +# + +# allow binding low ports, e.g., 443/tcp +# AmbientCapabilities=CAP_NET_BIND_SERVICE +# allow adding IP routes and iptables rules; required when ZITI_ROUTER_MODE=tproxy +# AmbientCapabilities=CAP_NET_ADMIN + +# +## options +# + +# additional environment variables used by ziti commands in bootstrap.bash +EnvironmentFile=/opt/openziti/etc/router/env +# used by bootstrap.bash to look up /run/credentials/$UNIT_NAME/$CREDENTIAL_NAME +Environment=UNIT_NAME=ziti-router.service +# disable JSON logging +Environment=PFXLOG_NO_JSON=true +# create a config file unless it exists if "true", set "force" to overwrite +Environment=ZITI_BOOTSTRAP_CONFIG=true +# enroll unless already enrolled if "true", set "force" to overwrite key and cert (requires new enrollment token) +Environment=ZITI_BOOTSTRAP_ENROLLMENT=true +# set the bootstrap function definitions for entrypoint.bash to source +Environment=ZITI_ROUTER_BOOTSTRAP_BASH=/opt/openziti/etc/router/bootstrap.bash +# renew server and client certificates every startup +Environment=ZITI_AUTO_RENEW_CERTS=true + +# +## misc +# + +# manage the user and permissions for the service automatically +DynamicUser=yes +# relative to /var/lib +StateDirectory=ziti-router +# absolute path where service will be run +WorkingDirectory=/var/lib/ziti-router +# "ziti router run" is the main process managed by this service and replaces entrypoint.bash +Type=simple +UMask=0007 +Restart=always +RestartSec=3 +LimitNOFILE=65535 +ExecStart=/opt/openziti/etc/router/entrypoint.bash run config.yml + +[Install] +WantedBy=multi-user.target diff --git a/dist/docker-images/ziti-cli/Dockerfile b/dist/docker-images/ziti-cli/Dockerfile index c6fbbb3f7..85ec12fa1 100644 --- a/dist/docker-images/ziti-cli/Dockerfile +++ b/dist/docker-images/ziti-cli/Dockerfile @@ -31,7 +31,7 @@ LABEL name="openziti/ziti-cli" \ USER root ### install packages -RUN INSTALL_PKGS="python3.11 python3.11-pip tar bash-completion vim-minimal less shadow-utils jq findutils" && \ +RUN INSTALL_PKGS="python3.11 python3.11-pip tar bash-completion vim-minimal less shadow-utils jq findutils hostname" && \ microdnf -y update --setopt=install_weak_deps=0 --setopt=tsflags=nodocs && \ microdnf -y install --setopt=install_weak_deps=0 --setopt=tsflags=nodocs ${INSTALL_PKGS} && \ microdnf clean all @@ -58,9 +58,6 @@ RUN chmod 0755 /usr/local/bin/ziti RUN /usr/local/bin/ziti completion bash > /etc/bash_completion.d/ziti_cli -COPY ${DOCKER_BUILD_DIR}/entrypoint.sh / -RUN chmod +x /entrypoint.sh USER ziggy COPY ${DOCKER_BUILD_DIR}/bashrc /home/ziggy/.bashrc -ENTRYPOINT [ "/entrypoint.sh" ] -CMD [ "ziti" ] +ENTRYPOINT [ "ziti" ] diff --git a/dist/docker-images/ziti-cli/entrypoint.sh b/dist/docker-images/ziti-cli/entrypoint.sh deleted file mode 100644 index e6d33f04c..000000000 --- a/dist/docker-images/ziti-cli/entrypoint.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -exec ziti "${@}" diff --git a/dist/docker-images/ziti-router/Dockerfile b/dist/docker-images/ziti-router/Dockerfile index 1a3a82134..702f2cfa8 100644 --- a/dist/docker-images/ziti-router/Dockerfile +++ b/dist/docker-images/ziti-router/Dockerfile @@ -3,19 +3,34 @@ ARG ZITI_CLI_IMAGE="docker.io/openziti/ziti-cli" # this builds docker.io/openziti/ziti-router FROM ${ZITI_CLI_IMAGE}:${ZITI_CLI_TAG} -# This build stage grabs artifacts that are copied into the final image. -# It uses the same base as the final image to maximize docker cache hits. - -### Required OpenShift Labels +### Required OpenShift Labels LABEL name="openziti/ziti-router" \ maintainer="developers@openziti.org" \ vendor="NetFoundry" \ summary="Run the OpenZiti Router" \ description="Run the OpenZiti Router" +# set up image as root, then drop privs to ziggy USER root -COPY ./entrypoint.sh / -RUN chmod +x /entrypoint.sh + +RUN INSTALL_PKGS="util-linux iptables" && \ + microdnf -y update --setopt=install_weak_deps=0 --setopt=tsflags=nodocs && \ + microdnf -y install --setopt=install_weak_deps=0 --setopt=tsflags=nodocs ${INSTALL_PKGS} + +# set a var for entrypoint.bash to find this script +ENV ZITI_ROUTER_BOOTSTRAP_BASH=/bootstrap.bash +ENV ZITI_ROUTER_TYPE=edge +ENV ZITI_ROUTER_PORT=3022 +ENV ZITI_BOOTSTRAP_CONFIG=true +ENV ZITI_BOOTSTRAP_ENROLLMENT=true +ENV PFXLOG_NO_JSON=true +COPY ./dist/dist-packages/linux/openziti-router/bootstrap.bash ${ZITI_ROUTER_BOOTSTRAP_BASH} +COPY ./dist/dist-packages/linux/openziti-router/entrypoint.bash / + +WORKDIR /ziti-router +RUN chown -R ziggy:ziggy /ziti-router + +# run as ziggy (2171:2171) by default, override run-as user with root when DOCKER_ROUTER_MODE=tproxy USER ziggy -ENTRYPOINT [ "/entrypoint.sh" ] -CMD [ "run" ] + +ENTRYPOINT [ "/entrypoint.bash" ] diff --git a/dist/docker-images/ziti-router/README.md b/dist/docker-images/ziti-router/README.md new file mode 100644 index 000000000..834c25942 --- /dev/null +++ b/dist/docker-images/ziti-router/README.md @@ -0,0 +1,92 @@ + +# Run Ziti Router in Docker + +You can use this container image to run Ziti Router in a Docker container. + +## Container Image + +The `openziti/ziti-router` image is thin and is based on the `openziti/ziti-cli` image, which only provides the `ziti` +CLI. The `ziti-router` image simply adds the `ziti router` subcommand to prefix the args you supply. + +## Docker Compose + +The included `compose.yml` demonstrates how to bootstrap a router and assumes you have the enrollment token and know the +address of the controller, i.e., the `ctrl.endpoint` of the control plane listener provided by the OpenZiti controller. + +### TPROXY Example + +This demonstrates how to use the `openziti/ziti-router` image to run a Ziti Router in a Docker container to configure +the network namespace of another container to use the Ziti network. + +```bash +# fetch the compose file for the ziti-router image +wget -O ./compose.router.yml https://get.openziti.io/dist/docker-images/ziti-router/compose.yml + +# run the quickstart network in the background to provide the ctrl.endpoint at quickstart:1280 +wget -O ./compose.quickstart.yml https://get.openziti.io/dock/all-in-one/compose.yml + +# patch the Compose project to use the quickstart network and provide a web server to test the hello service +cat <./compose.tproxy.yml +services: + # add a hello web server to use for a Ziti service target + hello: + image: openziti/hello-world + expose: + - 8000 + networks: + - quickstart + + # add a web client that waits for a healthy tproxy router + tproxy-demo-client: + image: busybox + network_mode: service:ziti-router + depends_on: + ziti-router: + condition: service_healthy + command: wget --output-document=- http://hello.internal/ + + # link the router to the quickstart network so it can reach the Ziti controller + ziti-router: + networks: + - quickstart +EOF +export COMPOSE_FILE=compose.router.yml:compose.quickstart.yml:compose.tproxy.yml + +# run the Ziti controller in the background with the all-in-one quickstart container +docker compose up quickstart-check + +# start the hello web server listening on 8000 +docker compose up hello --detach + +# log in to the Ziti controller +ziti edge login 127.0.0.1:1280 -y -u admin -p admin + +# create a Ziti service for the hello web server +ziti edge secure hello tcp:hello:8000 \ + --interceptAddress=hello.internal + +# grant the quickstart router permission to bind (provide) the hello service +ziti edge update identity quickstart-router \ + --role-attributes=hello.servers + +# create a second Ziti router to use as a tproxy client +ziti edge create edge-router "tproxy-router" \ + --jwt-output-file=./tproxy-router.jwt \ + --tunneler-enabled + +# grant the tproxy client permission to dial (consume) the hello service +ziti edge update identity tproxy-router \ + --role-attributes=hello.clients + +# simulate policies to check for authorization problems +ziti edge policy-advisor services -q + +# run the demo client which triggers the run of the tproxy router because it is a dependency +ZITI_ROUTER_JWT="$(<./tproxyRouter.jwt)" \ +ZITI_ROUTER_MODE=tproxy \ +ZITI_CTRL_ADVERTISED_ADDRESS=quickstart \ +ZITI_CTRL_ADVERTISED_PORT=1280 \ +ZITI_ROUTER_PORT=3023 \ +ZITI_ROUTER_ADVERTISED_ADDRESS=ziti-router \ + docker compose up tproxy-demo-client +``` diff --git a/dist/docker-images/ziti-router/compose.yml b/dist/docker-images/ziti-router/compose.yml new file mode 100644 index 000000000..af6ec096c --- /dev/null +++ b/dist/docker-images/ziti-router/compose.yml @@ -0,0 +1,66 @@ + +volumes: + ziti-router: + driver: local + +services: + chown-volume: + image: busybox + command: chown -R ${ZIGGY_UID:-2171} /mnt + volumes: + - ziti-router:/mnt + + ziti-router: + image: ${ZITI_ROUTER_IMAGE:-openziti/ziti-router} + depends_on: + chown-volume: + condition: service_completed_successfully + volumes: + - ziti-router:/mnt + working_dir: /mnt + # these declared vars pass through to container and should be assigned in an .env file or exported from parent env + # to ensure consistency throughout the compose project + environment: + # *** these are the important vars to set *** + ZITI_CTRL_ADVERTISED_ADDRESS: # domain name of the controller (required) + ZITI_CTRL_ADVERTISED_PORT: ${ZITI_CTRL_ADVERTISED_PORT:-1280} # exposed port of the controller + ZITI_ENROLL_TOKEN: # enrollment token for this router (required) + ZITI_ROUTER_ADVERTISED_ADDRESS: # domain name for this router (default: the container ID [hostname -f]) + ZITI_ROUTER_PORT: ${ZITI_ROUTER_PORT-3022} # exposed port for this router + ZITI_ROUTER_MODE: ${ZITI_ROUTER_MODE:-host} # host, tproxy, tproxy (tproxy requires additional config below) + # *** less relevant vars below *** + ZITI_ROUTER_TYPE: edge # edge, fabric + ZITI_ROUTER_NAME: ziti-router # ensure the computed filenames based on this var are consistent, not based on + # ephemeral container id + ZITI_BOOTSTRAP_CONFIG: true # make config file from env vars and defaults if "true," overwrite if "force" + ZITI_BOOTSTRAP_ENROLLMENT: true # enroll with controller if "true," overwrite if "force" + PFXLOG_NO_JSON: true + ZITI_TIME_FORMAT: utc + command: run config.yml + ports: + # ensure this port matches the value of ZITI_ROUTER_PORT in the container + - ${ZITI_INTERFACE:-0.0.0.0}:${ZITI_ROUTER_PORT:-3022}:${ZITI_ROUTER_PORT:-3022} + expose: + - ${ZITI_ROUTER_PORT:-3022} + restart: unless-stopped + healthcheck: + test: + - CMD + - ziti + - agent + - stats + interval: 3s + timeout: 3s + retries: 5 + start_period: 15s + + # Additional config for other containers using this router as a transparent intercepting proxy sidecar and default + # nameserver - dns, user, cap_add are required when ZITI_ROUTER_MODE=tproxy (see adjacent README.md for TPROXY + # example) + # + # dns: + # - 127.0.0.1 # this router's Ziti resolver + # - 1.1.1.1 # any recursive resolver + # user: root # required to create TPROXY routes in a container? + # cap_add: + # - NET_ADMIN # required to create TPROXY rules diff --git a/dist/docker-images/ziti-router/entrypoint.sh b/dist/docker-images/ziti-router/entrypoint.sh deleted file mode 100644 index 8bb695e7a..000000000 --- a/dist/docker-images/ziti-router/entrypoint.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -euo pipefail - -exec ziti router "${@}" diff --git a/ziti/cmd/create/config_templates/router.yml b/ziti/cmd/create/config_templates/router.yml index c383e1c4f..1bcf7944a 100644 --- a/ziti/cmd/create/config_templates/router.yml +++ b/ziti/cmd/create/config_templates/router.yml @@ -40,8 +40,8 @@ link: {{ if or .Router.IsFabric (eq .Router.TunnelerMode "none") }}#{{ end }} - binding: tunnel {{ if or .Router.IsFabric (eq .Router.TunnelerMode "none") }}#{{ end }} options: {{ if or .Router.IsFabric (eq .Router.TunnelerMode "none") }}# mode: host #tproxy|host{{ else }} mode: {{ .Router.TunnelerMode }} #tproxy|host{{ end }} -{{ if and (not .Router.IsFabric) (eq .Router.TunnelerMode "tproxy") }} resolver: udp://{{ .Router.Edge.AdvertisedHost }}:53{{ end }} -{{ if and (not .Router.IsFabric) (eq .Router.TunnelerMode "tproxy") }} lanIf: {{ .Router.Edge.LanInterface }}{{ end }} +{{ if and (not .Router.IsFabric) (eq .Router.TunnelerMode "tproxy") }} resolver: {{ .Router.Edge.Resolver }}{{ end }} +{{ if and (not .Router.IsFabric) (eq .Router.TunnelerMode "tproxy") (.Router.Edge.LanInterface) }} lanIf: {{ .Router.Edge.LanInterface }}{{ end }} {{ if .Router.IsFabric -}} csr: country: US @@ -53,7 +53,7 @@ csr: dns: - localhost {{ if .Router.Edge.CsrSans }} - {{ .Router.Edge.CsrSans }}{{ end }} -{{ if ne .Router.Edge.CsrSans .Hostname }} - {{ .Hostname }}{{ end }} +{{ if ne .Router.Edge.CsrSans .HostnameOrNetworkName }} - {{ .HostnameOrNetworkName }}{{ end }} ip: - "127.0.0.1" {{ if .Router.Edge.IPOverride }} - "{{ .Router.Edge.IPOverride }}"{{ end }} @@ -69,7 +69,7 @@ edge: dns: - localhost {{ if .Router.Edge.CsrSans }} - {{ .Router.Edge.CsrSans }}{{ end }} -{{ if ne .Router.Edge.CsrSans .Hostname }} - {{ .Hostname }}{{ end }} +{{ if ne .Router.Edge.CsrSans .HostnameOrNetworkName }} - {{ .HostnameOrNetworkName }}{{ end }} ip: - "127.0.0.1" {{ if .Router.Edge.IPOverride }} - "{{ .Router.Edge.IPOverride }}"{{ end }} diff --git a/ziti/cmd/create/create_config.go b/ziti/cmd/create/create_config.go index ba4da861a..7b93d0d1d 100644 --- a/ziti/cmd/create/create_config.go +++ b/ziti/cmd/create/create_config.go @@ -52,7 +52,7 @@ type CreateConfigOptions struct { type ConfigTemplateValues struct { ZitiHome string - Hostname string + HostnameOrNetworkName string Controller ControllerTemplateValues Router RouterTemplateValues @@ -167,6 +167,7 @@ type EdgeRouterTemplateValues struct { IPOverride string AdvertisedHost string LanInterface string + Resolver string ListenerBindPort string CsrC string CsrST string @@ -230,7 +231,7 @@ func (options *CreateConfigOptions) addCreateFlags(cmd *cobra.Command) { func (data *ConfigTemplateValues) PopulateConfigValues() { // Get and add hostname to the params - data.Hostname = cmdHelper.HostnameOrNetworkName() + data.HostnameOrNetworkName = cmdHelper.HostnameOrNetworkName() // Get and add ziti home to the params zitiHome := cmdHelper.GetZitiHome() @@ -282,6 +283,7 @@ func (data *ConfigTemplateValues) PopulateConfigValues() { // ************* Router Values ************ data.Router.Edge.Port = cmdHelper.GetZitiEdgeRouterPort() data.Router.Edge.ListenerBindPort = cmdHelper.GetZitiEdgeRouterListenerBindPort() + data.Router.Edge.Resolver = cmdHelper.GetZitiEdgeRouterResolver() data.Router.Edge.CsrC = cmdHelper.GetZitiEdgeRouterC() data.Router.Edge.CsrST = cmdHelper.GetZitiEdgeRouterST() data.Router.Edge.CsrL = cmdHelper.GetZitiEdgeRouterL() diff --git a/ziti/cmd/create/create_config_environment.go b/ziti/cmd/create/create_config_environment.go index 74275d1e8..e36125590 100644 --- a/ziti/cmd/create/create_config_environment.go +++ b/ziti/cmd/create/create_config_environment.go @@ -92,7 +92,7 @@ func NewCmdCreateConfigEnvironment() *cobra.Command { PreRun: func(cmd *cobra.Command, args []string) { data.PopulateConfigValues() // Set router identities - SetZitiRouterIdentity(&data.Router, validateRouterName("")) + SetZitiRouterIdentity(&data.Router, validateRouterName(os.Getenv(constants.ZitiEdgeRouterNameVarName))) // Set up other identity info SetControllerIdentity(&data.Controller) SetEdgeConfig(&data.Controller) @@ -100,6 +100,7 @@ func NewCmdCreateConfigEnvironment() *cobra.Command { environmentOptions.EnvVars = []EnvVar{ {constants.ZitiHomeVarName, constants.ZitiHomeVarDescription, data.ZitiHome}, + {constants.ZitiNetworkNameVarName, constants.ZitiNetworkNameVarDescription, data.HostnameOrNetworkName}, {constants.PkiCtrlCertVarName, constants.PkiCtrlCertVarDescription, data.Controller.Identity.Cert}, {constants.PkiCtrlServerCertVarName, constants.PkiCtrlServerCertVarDescription, data.Controller.Identity.ServerCert}, {constants.PkiCtrlKeyVarName, constants.PkiCtrlKeyVarDescription, data.Controller.Identity.Key}, @@ -133,6 +134,7 @@ func NewCmdCreateConfigEnvironment() *cobra.Command { {constants.ZitiRouterIdentityCAVarName, constants.ZitiRouterIdentityCAVarDescription, data.Router.IdentityCA}, {constants.ZitiEdgeRouterIPOverrideVarName, constants.ZitiEdgeRouterIPOverrideVarDescription, data.Router.Edge.IPOverride}, {constants.ZitiEdgeRouterAdvertisedAddressVarName, constants.ZitiEdgeRouterAdvertisedAddressVarDescription, data.Router.Edge.AdvertisedHost}, + {constants.ZitiEdgeRouterResolverVarName, constants.ZitiEdgeRouterResolverVarDescription, data.Router.Edge.Resolver}, {constants.ZitiEdgeRouterCsrCVarName, constants.ZitiEdgeRouterCsrCVarDescription, data.Router.Edge.CsrC}, {constants.ZitiEdgeRouterCsrSTVarName, constants.ZitiEdgeRouterCsrSTVarDescription, data.Router.Edge.CsrST}, {constants.ZitiEdgeRouterCsrLVarName, constants.ZitiEdgeRouterCsrLVarDescription, data.Router.Edge.CsrL}, @@ -185,6 +187,7 @@ func NewCmdCreateConfigEnvironment() *cobra.Command { "the config output.\n\nThe following environment variables can be set to override config values " + "(current value is displayed):\n") sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiHomeVarName, constants.ZitiHomeVarDescription, data.ZitiHome)) + sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiNetworkNameVarName, constants.ZitiNetworkNameVarDescription, data.HostnameOrNetworkName)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.PkiCtrlCertVarName, constants.PkiCtrlCertVarDescription, data.Controller.Identity.Cert)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.PkiCtrlServerCertVarName, constants.PkiCtrlServerCertVarDescription, data.Controller.Identity.ServerCert)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.PkiCtrlKeyVarName, constants.PkiCtrlKeyVarDescription, data.Controller.Identity.Key)) @@ -216,6 +219,7 @@ func NewCmdCreateConfigEnvironment() *cobra.Command { sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiRouterIdentityCAVarName, constants.ZitiRouterIdentityCAVarDescription, data.Router.IdentityCA)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterIPOverrideVarName, constants.ZitiEdgeRouterIPOverrideVarDescription, data.Router.Edge.IPOverride)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterAdvertisedAddressVarName, constants.ZitiEdgeRouterAdvertisedAddressVarDescription, data.Router.Edge.AdvertisedHost)) + sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterResolverVarName, constants.ZitiEdgeRouterResolverVarDescription, data.Router.Edge.Resolver)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterCsrCVarName, constants.ZitiEdgeRouterCsrCVarDescription, data.Router.Edge.CsrC)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterCsrSTVarName, constants.ZitiEdgeRouterCsrSTVarDescription, data.Router.Edge.CsrST)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterCsrLVarName, constants.ZitiEdgeRouterCsrLVarDescription, data.Router.Edge.CsrL)) diff --git a/ziti/cmd/create/create_config_router_edge.go b/ziti/cmd/create/create_config_router_edge.go index 06ad8a4d5..01cd4f84d 100644 --- a/ziti/cmd/create/create_config_router_edge.go +++ b/ziti/cmd/create/create_config_router_edge.go @@ -76,6 +76,7 @@ func NewCmdCreateConfigRouterEdge(routerOptions *CreateConfigRouterOptions, data data.Router.IsPrivate = routerOptions.IsPrivate data.Router.TunnelerMode = routerOptions.TunnelerMode data.Router.Edge.LanInterface = routerOptions.LanInterface + data.Router.Edge.Resolver = cmdhelper.GetZitiEdgeRouterResolver() }, Run: func(cmd *cobra.Command, args []string) { routerOptions.Cmd = cmd diff --git a/ziti/cmd/create/create_config_router_edge_test.go b/ziti/cmd/create/create_config_router_edge_test.go index ae642101c..68c5f05f1 100644 --- a/ziti/cmd/create/create_config_router_edge_test.go +++ b/ziti/cmd/create/create_config_router_edge_test.go @@ -238,7 +238,7 @@ func TestExecuteCreateConfigRouterEdgeHasNonBlankTemplateValues(t *testing.T) { _, data := createRouterConfig([]string{"edge", "--routerName", routerName}, routerOptions, nil) expectedNonEmptyStringFields := []string{".Router.Edge.ListenerBindPort", ".ZitiHome", ".Hostname", ".Router.Name", ".Router.IdentityCert", ".Router.IdentityServerCert", ".Router.IdentityKey", ".Router.IdentityCA", ".Router.Edge.Port"} - expectedNonEmptyStringValues := []*string{&data.Router.Edge.ListenerBindPort, &data.ZitiHome, &data.Hostname, &data.Router.Name, &data.Router.IdentityCert, &data.Router.IdentityServerCert, &data.Router.IdentityKey, &data.Router.IdentityCA, &data.Router.Edge.Port} + expectedNonEmptyStringValues := []*string{&data.Router.Edge.ListenerBindPort, &data.ZitiHome, &data.HostnameOrNetworkName, &data.Router.Name, &data.Router.IdentityCert, &data.Router.IdentityServerCert, &data.Router.IdentityKey, &data.Router.IdentityCA, &data.Router.Edge.Port} expectedNonEmptyIntFields := []string{".Router.Listener.OutQueueSize", ".Router.Wss.ReadBufferSize", ".Router.Wss.WriteBufferSize", ".Router.Forwarder.XgressDialQueueLength", ".Router.Forwarder.XgressDialWorkerCount", ".Router.Forwarder.LinkDialQueueLength", ".Router.Forwarder.LinkDialWorkerCount"} expectedNonEmptyIntValues := []*int{&data.Router.Listener.OutQueueSize, &data.Router.Wss.ReadBufferSize, &data.Router.Wss.WriteBufferSize, &data.Router.Forwarder.XgressDialQueueLength, &data.Router.Forwarder.XgressDialWorkerCount, &data.Router.Forwarder.LinkDialQueueLength, &data.Router.Forwarder.LinkDialWorkerCount} expectedNonEmptyTimeFields := []string{".Router.Listener.ConnectTimeout", "Router.Listener.GetSessionTimeout", ".Router.Wss.WriteTimeout", ".Router.Wss.ReadTimeout", ".Router.Wss.IdleTimeout", ".Router.Wss.PongTimeout", ".Router.Wss.PingInterval", ".Router.Wss.HandshakeTimeout"} diff --git a/ziti/cmd/create/create_config_router_fabric_test.go b/ziti/cmd/create/create_config_router_fabric_test.go index 3269d58b1..eaeddbff1 100644 --- a/ziti/cmd/create/create_config_router_fabric_test.go +++ b/ziti/cmd/create/create_config_router_fabric_test.go @@ -20,7 +20,7 @@ func TestExecuteCreateConfigRouterFabricHasNonBlankTemplateValues(t *testing.T) _, data := createRouterConfig([]string{"fabric", "--routerName", routerName}, routerOptions, nil) expectedNonEmptyStringFields := []string{".Router.Listener.BindPort", ".ZitiHome", ".Hostname", ".Router.Name", ".Router.IdentityCert", ".Router.IdentityServerCert", ".Router.IdentityKey", ".Router.IdentityCA", ".Router.Edge.Port"} - expectedNonEmptyStringValues := []*string{&data.Router.Edge.ListenerBindPort, &data.ZitiHome, &data.Hostname, &data.Router.Name, &data.Router.IdentityCert, &data.Router.IdentityServerCert, &data.Router.IdentityKey, &data.Router.IdentityCA, &data.Router.Edge.Port} + expectedNonEmptyStringValues := []*string{&data.Router.Edge.ListenerBindPort, &data.ZitiHome, &data.HostnameOrNetworkName, &data.Router.Name, &data.Router.IdentityCert, &data.Router.IdentityServerCert, &data.Router.IdentityKey, &data.Router.IdentityCA, &data.Router.Edge.Port} expectedNonEmptyIntFields := []string{".Router.Listener.OutQueueSize", ".Router.Wss.ReadBufferSize", ".Router.Wss.WriteBufferSize", ".Router.Forwarder.XgressDialQueueLength", ".Router.Forwarder.XgressDialWorkerCount", ".Router.Forwarder.LinkDialQueueLength", ".Router.Forwarder.LinkDialWorkerCount"} expectedNonEmptyIntValues := []*int{&data.Router.Listener.OutQueueSize, &data.Router.Wss.ReadBufferSize, &data.Router.Wss.WriteBufferSize, &data.Router.Forwarder.XgressDialQueueLength, &data.Router.Forwarder.XgressDialWorkerCount, &data.Router.Forwarder.LinkDialQueueLength, &data.Router.Forwarder.LinkDialWorkerCount} expectedNonEmptyTimeFields := []string{".Router.Listener.ConnectTimeout", "Router.Listener.GetSessionTimeout", ".Router.Wss.WriteTimeout", ".Router.Wss.ReadTimeout", ".Router.Wss.IdleTimeout", ".Router.Wss.PongTimeout", ".Router.Wss.PingInterval", ".Router.Wss.HandshakeTimeout"} diff --git a/ziti/cmd/create/create_config_test.go b/ziti/cmd/create/create_config_test.go index 41baa7298..da51e7719 100644 --- a/ziti/cmd/create/create_config_test.go +++ b/ziti/cmd/create/create_config_test.go @@ -58,6 +58,8 @@ func getZitiEnvironmentVariables() []string { "ZITI_ROUTER_IDENTITY_KEY", "ZITI_ROUTER_IDENTITY_CA", "ZITI_ROUTER_IP_OVERRIDE", + "ZITI_ROUTER_TPROXY_RESOLVER", + "ZITI_NETWORK_NAME", "ZITI_EDGE_IDENTITY_ENROLLMENT_DURATION", "ZITI_ROUTER_ENROLLMENT_DURATION", "ZITI_ROUTER_ADVERTISED_ADDRESS", diff --git a/ziti/cmd/helpers/env_helpers.go b/ziti/cmd/helpers/env_helpers.go index 424d48553..97078e25e 100644 --- a/ziti/cmd/helpers/env_helpers.go +++ b/ziti/cmd/helpers/env_helpers.go @@ -17,6 +17,7 @@ package helpers import ( + "github.com/openziti/ziti/router/xgress_edge_tunnel" edge "github.com/openziti/ziti/controller/config" "github.com/openziti/ziti/ziti/constants" "github.com/pkg/errors" @@ -202,6 +203,9 @@ func NormalizePath(input string) string { func GetRouterAdvertisedAddress() string { return getFromEnv(constants.ZitiEdgeRouterAdvertisedAddressVarName, HostnameOrNetworkName) } +func GetZitiEdgeRouterResolver() string { + return getFromEnv(constants.ZitiEdgeRouterResolverVarName, defaultValue(xgress_edge_tunnel.DefaultDnsResolver)) +} func GetRouterSans() string { return getFromEnv(constants.ZitiRouterCsrSansDnsVarName, GetRouterAdvertisedAddress) } diff --git a/ziti/constants/constants.go b/ziti/constants/constants.go index cb73c181b..c15cd882d 100644 --- a/ziti/constants/constants.go +++ b/ziti/constants/constants.go @@ -61,6 +61,10 @@ const ( const ( ZitiHomeVarName = "ZITI_HOME" ZitiHomeVarDescription = "base dirname used to construct paths" + + ZitiNetworkNameVarName = "ZITI_NETWORK_NAME" + ZitiNetworkNameVarDescription = "base filename used to construct paths" + PkiCtrlCertVarName = "ZITI_PKI_CTRL_CERT" PkiCtrlCertVarDescription = "Path to the controller's default identity client cert" PkiCtrlServerCertVarName = "ZITI_PKI_CTRL_SERVER_CERT" @@ -123,6 +127,8 @@ const ( ZitiEdgeRouterAdvertisedAddressVarDescription = "The router's advertised address and DNS SAN" ZitiEdgeRouterListenerBindPortVarName = "ZITI_ROUTER_LISTENER_BIND_PORT" ZitiEdgeRouterListenerBindPortVarDescription = "TCP port where the router will listen for and advertise links to other routers" + ZitiEdgeRouterResolverVarName = "ZITI_ROUTER_TPROXY_RESOLVER" + ZitiEdgeRouterResolverVarDescription = "The bind URI to listen for DNS requests in tproxy mode" ZitiEdgeRouterCsrCVarName = "ZITI_ROUTER_CSR_C" ZitiEdgeRouterCsrCVarDescription = "The country (C) to use for router CSRs" ZitiEdgeRouterCsrSTVarName = "ZITI_ROUTER_CSR_ST" From 6b401c232ed7afad6114b5252b97aa4fcb5b99d5 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Fri, 22 Mar 2024 12:31:30 -0400 Subject: [PATCH 2/6] add option to set router tproxy DNS IP range in generated config and env --- dist/dist-packages/linux/openziti-router/env | 2 ++ .../linux/openziti-router/ziti-router.service | 2 +- ziti/cmd/create/config_templates/router.yml | 7 ++++++- ziti/cmd/create/create_config.go | 2 ++ ziti/cmd/create/create_config_environment.go | 2 ++ ziti/cmd/create/create_config_router_edge.go | 1 + ziti/cmd/create/create_config_test.go | 1 + ziti/cmd/helpers/env_helpers.go | 3 +++ ziti/constants/constants.go | 2 ++ 9 files changed, 20 insertions(+), 2 deletions(-) diff --git a/dist/dist-packages/linux/openziti-router/env b/dist/dist-packages/linux/openziti-router/env index bbeb4b59d..42d702a45 100644 --- a/dist/dist-packages/linux/openziti-router/env +++ b/dist/dist-packages/linux/openziti-router/env @@ -31,6 +31,8 @@ ZITI_ROUTER_PORT= ZITI_ROUTER_MODE= # where to listen for DNS requests in tproxy mode (default: udp://127.0.0.1:53) ZITI_ROUTER_TPROXY_RESOLVER= +# CIDR range of IP addresses to assign to DNS clients in tproxy mode (default: 100.64.0.1/10) +ZITI_ROUTER_DNS_IP_RANGE= # the interface address on which to listen (default: 0.0.0.0) ZITI_ROUTER_BIND_ADDRESS= diff --git a/dist/dist-packages/linux/openziti-router/ziti-router.service b/dist/dist-packages/linux/openziti-router/ziti-router.service index df9d943b4..51d689c16 100644 --- a/dist/dist-packages/linux/openziti-router/ziti-router.service +++ b/dist/dist-packages/linux/openziti-router/ziti-router.service @@ -21,7 +21,7 @@ LoadCredential=ZITI_ENROLL_TOKEN:/opt/openziti/etc/router/.token ## extra permissions # -# allow binding low ports, e.g., 443/tcp +# allow binding low ports, e.g., 443/tcp; required when ZITI_ROUTER_MODE=tproxy or ZITI_ROUTER_PORT <= 1024 # AmbientCapabilities=CAP_NET_BIND_SERVICE # allow adding IP routes and iptables rules; required when ZITI_ROUTER_MODE=tproxy # AmbientCapabilities=CAP_NET_ADMIN diff --git a/ziti/cmd/create/config_templates/router.yml b/ziti/cmd/create/config_templates/router.yml index 1bcf7944a..2a71d482c 100644 --- a/ziti/cmd/create/config_templates/router.yml +++ b/ziti/cmd/create/config_templates/router.yml @@ -41,7 +41,12 @@ link: {{ if or .Router.IsFabric (eq .Router.TunnelerMode "none") }}#{{ end }} options: {{ if or .Router.IsFabric (eq .Router.TunnelerMode "none") }}# mode: host #tproxy|host{{ else }} mode: {{ .Router.TunnelerMode }} #tproxy|host{{ end }} {{ if and (not .Router.IsFabric) (eq .Router.TunnelerMode "tproxy") }} resolver: {{ .Router.Edge.Resolver }}{{ end }} -{{ if and (not .Router.IsFabric) (eq .Router.TunnelerMode "tproxy") (.Router.Edge.LanInterface) }} lanIf: {{ .Router.Edge.LanInterface }}{{ end }} +{{- if and (not .Router.IsFabric) (eq .Router.TunnelerMode "tproxy") (.Router.Edge.LanInterface) }} + lanIf: {{ .Router.Edge.LanInterface }} +{{- end }} +{{- if and (not .Router.IsFabric) (eq .Router.TunnelerMode "tproxy") (.Router.Edge.DnsSvcIpRange ) }} + dnsSvcIpRange: {{ .Router.Edge.DnsSvcIpRange }} +{{- end }} {{ if .Router.IsFabric -}} csr: country: US diff --git a/ziti/cmd/create/create_config.go b/ziti/cmd/create/create_config.go index 7b93d0d1d..0a688a24a 100644 --- a/ziti/cmd/create/create_config.go +++ b/ziti/cmd/create/create_config.go @@ -168,6 +168,7 @@ type EdgeRouterTemplateValues struct { AdvertisedHost string LanInterface string Resolver string + DnsSvcIpRange string ListenerBindPort string CsrC string CsrST string @@ -284,6 +285,7 @@ func (data *ConfigTemplateValues) PopulateConfigValues() { data.Router.Edge.Port = cmdHelper.GetZitiEdgeRouterPort() data.Router.Edge.ListenerBindPort = cmdHelper.GetZitiEdgeRouterListenerBindPort() data.Router.Edge.Resolver = cmdHelper.GetZitiEdgeRouterResolver() + data.Router.Edge.DnsSvcIpRange = cmdHelper.GetZitiEdgeRouterDnsSvcIpRange() data.Router.Edge.CsrC = cmdHelper.GetZitiEdgeRouterC() data.Router.Edge.CsrST = cmdHelper.GetZitiEdgeRouterST() data.Router.Edge.CsrL = cmdHelper.GetZitiEdgeRouterL() diff --git a/ziti/cmd/create/create_config_environment.go b/ziti/cmd/create/create_config_environment.go index e36125590..86213583b 100644 --- a/ziti/cmd/create/create_config_environment.go +++ b/ziti/cmd/create/create_config_environment.go @@ -135,6 +135,7 @@ func NewCmdCreateConfigEnvironment() *cobra.Command { {constants.ZitiEdgeRouterIPOverrideVarName, constants.ZitiEdgeRouterIPOverrideVarDescription, data.Router.Edge.IPOverride}, {constants.ZitiEdgeRouterAdvertisedAddressVarName, constants.ZitiEdgeRouterAdvertisedAddressVarDescription, data.Router.Edge.AdvertisedHost}, {constants.ZitiEdgeRouterResolverVarName, constants.ZitiEdgeRouterResolverVarDescription, data.Router.Edge.Resolver}, + {constants.ZitiEdgeRouterDnsSvcIpRangeVarName, constants.ZitiEdgeRouterDnsSvcIpRangeVarDescription, data.Router.Edge.DnsSvcIpRange}, {constants.ZitiEdgeRouterCsrCVarName, constants.ZitiEdgeRouterCsrCVarDescription, data.Router.Edge.CsrC}, {constants.ZitiEdgeRouterCsrSTVarName, constants.ZitiEdgeRouterCsrSTVarDescription, data.Router.Edge.CsrST}, {constants.ZitiEdgeRouterCsrLVarName, constants.ZitiEdgeRouterCsrLVarDescription, data.Router.Edge.CsrL}, @@ -220,6 +221,7 @@ func NewCmdCreateConfigEnvironment() *cobra.Command { sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterIPOverrideVarName, constants.ZitiEdgeRouterIPOverrideVarDescription, data.Router.Edge.IPOverride)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterAdvertisedAddressVarName, constants.ZitiEdgeRouterAdvertisedAddressVarDescription, data.Router.Edge.AdvertisedHost)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterResolverVarName, constants.ZitiEdgeRouterResolverVarDescription, data.Router.Edge.Resolver)) + sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterDnsSvcIpRangeVarName, constants.ZitiEdgeRouterDnsSvcIpRangeVarDescription, data.Router.Edge.DnsSvcIpRange)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterCsrCVarName, constants.ZitiEdgeRouterCsrCVarDescription, data.Router.Edge.CsrC)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterCsrSTVarName, constants.ZitiEdgeRouterCsrSTVarDescription, data.Router.Edge.CsrST)) sb.WriteString(fmt.Sprintf("%-40s %-50s %s\n", constants.ZitiEdgeRouterCsrLVarName, constants.ZitiEdgeRouterCsrLVarDescription, data.Router.Edge.CsrL)) diff --git a/ziti/cmd/create/create_config_router_edge.go b/ziti/cmd/create/create_config_router_edge.go index 01cd4f84d..cc54709c8 100644 --- a/ziti/cmd/create/create_config_router_edge.go +++ b/ziti/cmd/create/create_config_router_edge.go @@ -77,6 +77,7 @@ func NewCmdCreateConfigRouterEdge(routerOptions *CreateConfigRouterOptions, data data.Router.TunnelerMode = routerOptions.TunnelerMode data.Router.Edge.LanInterface = routerOptions.LanInterface data.Router.Edge.Resolver = cmdhelper.GetZitiEdgeRouterResolver() + data.Router.Edge.DnsSvcIpRange = cmdhelper.GetZitiEdgeRouterDnsSvcIpRange() }, Run: func(cmd *cobra.Command, args []string) { routerOptions.Cmd = cmd diff --git a/ziti/cmd/create/create_config_test.go b/ziti/cmd/create/create_config_test.go index da51e7719..c1ef4ae74 100644 --- a/ziti/cmd/create/create_config_test.go +++ b/ziti/cmd/create/create_config_test.go @@ -59,6 +59,7 @@ func getZitiEnvironmentVariables() []string { "ZITI_ROUTER_IDENTITY_CA", "ZITI_ROUTER_IP_OVERRIDE", "ZITI_ROUTER_TPROXY_RESOLVER", + "ZITI_ROUTER_DNS_IP_RANGE", "ZITI_NETWORK_NAME", "ZITI_EDGE_IDENTITY_ENROLLMENT_DURATION", "ZITI_ROUTER_ENROLLMENT_DURATION", diff --git a/ziti/cmd/helpers/env_helpers.go b/ziti/cmd/helpers/env_helpers.go index 97078e25e..40ae01110 100644 --- a/ziti/cmd/helpers/env_helpers.go +++ b/ziti/cmd/helpers/env_helpers.go @@ -206,6 +206,9 @@ func GetRouterAdvertisedAddress() string { func GetZitiEdgeRouterResolver() string { return getFromEnv(constants.ZitiEdgeRouterResolverVarName, defaultValue(xgress_edge_tunnel.DefaultDnsResolver)) } +func GetZitiEdgeRouterDnsSvcIpRange() string { + return getFromEnv(constants.ZitiEdgeRouterDnsSvcIpRangeVarName, defaultValue(xgress_edge_tunnel.DefaultDnsServiceIpRange)) +} func GetRouterSans() string { return getFromEnv(constants.ZitiRouterCsrSansDnsVarName, GetRouterAdvertisedAddress) } diff --git a/ziti/constants/constants.go b/ziti/constants/constants.go index c15cd882d..ca8ee2aae 100644 --- a/ziti/constants/constants.go +++ b/ziti/constants/constants.go @@ -129,6 +129,8 @@ const ( ZitiEdgeRouterListenerBindPortVarDescription = "TCP port where the router will listen for and advertise links to other routers" ZitiEdgeRouterResolverVarName = "ZITI_ROUTER_TPROXY_RESOLVER" ZitiEdgeRouterResolverVarDescription = "The bind URI to listen for DNS requests in tproxy mode" + ZitiEdgeRouterDnsSvcIpRangeVarName = "ZITI_ROUTER_DNS_IP_RANGE" + ZitiEdgeRouterDnsSvcIpRangeVarDescription = "The CIDR range to use for Ziti DNS in tproxy mode" ZitiEdgeRouterCsrCVarName = "ZITI_ROUTER_CSR_C" ZitiEdgeRouterCsrCVarDescription = "The country (C) to use for router CSRs" ZitiEdgeRouterCsrSTVarName = "ZITI_ROUTER_CSR_ST" From 3f260af879ccda4e2cb7d02865355ad1dd3c429a Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Fri, 22 Mar 2024 13:32:17 -0400 Subject: [PATCH 3/6] grant net bind kernel capability if tproxy mode and privileged port --- .../linux/openziti-router/postinstall.bash | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dist/dist-packages/linux/openziti-router/postinstall.bash b/dist/dist-packages/linux/openziti-router/postinstall.bash index 42005bacb..8a62ba803 100755 --- a/dist/dist-packages/linux/openziti-router/postinstall.bash +++ b/dist/dist-packages/linux/openziti-router/postinstall.bash @@ -85,7 +85,7 @@ promptCtrlAdvertisedAddress() { promptRouterAdvertisedAddress() { if [ -z "${ZITI_ROUTER_ADVERTISED_ADDRESS:-}" ]; then - DEFAULT_ADDR="${HOSTNAME:=$(hostname -f)}}" + DEFAULT_ADDR="${HOSTNAME:=$(hostname -f)}" if ZITI_ROUTER_ADVERTISED_ADDRESS="$(prompt "Enter the advertised address for this router [$DEFAULT_ADDR]: " || echo "$DEFAULT_ADDR")"; then sed -Ei "s/^(ZITI_ROUTER_ADVERTISED_ADDRESS)=.*/\1=${ZITI_ROUTER_ADVERTISED_ADDRESS}/" /opt/openziti/etc/router/env fi @@ -109,7 +109,7 @@ promptEnrollToken() { elif [[ -n "${ZITI_ENROLL_TOKEN:-}" ]]; then echo "INFO: ZITI_ENROLL_TOKEN is defined in /opt/openziti/etc/router/env and will be used to enroll during"\ "next startup" - elif grep -qE "^LoadCredential=ZITI_ENROLL_TOKEN=${ZITI_ENROLL_TOKEN_FILE}" \ + elif grep -qE "^LoadCredential=ZITI_ENROLL_TOKEN:${ZITI_ENROLL_TOKEN_FILE}" \ /lib/systemd/system/ziti-router.service \ && [[ -s "${ZITI_ENROLL_TOKEN_FILE}" ]]; then echo "INFO: ZITI_ENROLL_TOKEN is defined in ${ZITI_ENROLL_TOKEN_FILE} and will be used to"\ @@ -137,8 +137,14 @@ promptRouterMode() { sed -Ei "s/^(ZITI_ROUTER_MODE)=.*/\1=${ZITI_ROUTER_MODE}/" /opt/openziti/etc/router/env fi fi + # grant kernel capability NET_ADMIN if tproxy mode if [[ "${ZITI_ROUTER_MODE}" == tproxy ]]; then grantNetAdmin + # also grant NET_BIND_SERVICE if resolver port is default 53 or defined <= 1024 + RESOLVER_PORT="${ZITI_ROUTER_TPROXY_RESOLVER##*:}" + if [[ -z "${RESOLVER_PORT}" || "${RESOLVER_PORT}" -le 1024 ]]; then + grantNetBindService + fi fi } From 60fe2079a8c970c5c371daa844bbb3b61a4fd1a1 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Fri, 22 Mar 2024 16:00:44 -0400 Subject: [PATCH 4/6] disable router tunnel binding by default --- dist/dist-packages/linux/openziti-router/bootstrap.bash | 1 + dist/dist-packages/linux/openziti-router/postinstall.bash | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/dist/dist-packages/linux/openziti-router/bootstrap.bash b/dist/dist-packages/linux/openziti-router/bootstrap.bash index 2aa51bed9..30720e5c9 100755 --- a/dist/dist-packages/linux/openziti-router/bootstrap.bash +++ b/dist/dist-packages/linux/openziti-router/bootstrap.bash @@ -70,6 +70,7 @@ function bootstrap() { : "${ZITI_ROUTER_ADVERTISED_ADDRESS:=${HOSTNAME:=$(hostname -f)}}" : "${ZITI_ROUTER_NAME:=${HOSTNAME%%.*}}" : "${ZITI_CTRL_ADVERTISED_PORT:=1280}" +: "${ZITI_ROUTER_MODE:=none}" export ZITI_ROUTER_NAME \ ZITI_ROUTER_ADVERTISED_ADDRESS \ ZITI_CTRL_ADVERTISED_PORT \ diff --git a/dist/dist-packages/linux/openziti-router/postinstall.bash b/dist/dist-packages/linux/openziti-router/postinstall.bash index 8a62ba803..f4c14657c 100755 --- a/dist/dist-packages/linux/openziti-router/postinstall.bash +++ b/dist/dist-packages/linux/openziti-router/postinstall.bash @@ -133,7 +133,7 @@ promptEnrollToken() { promptRouterMode() { # if undefined or default value in env file, prompt for router mode, preserving default if no answer if [[ -z "${ZITI_ROUTER_MODE:-}" ]]; then - if ZITI_ROUTER_MODE="$(prompt 'Enter the router mode (eg. host, tproxy, proxy) [host]: ' || echo 'host')"; then + if ZITI_ROUTER_MODE="$(prompt 'Enter the router mode (eg. host, tproxy, proxy) [none]: ' || echo 'none')"; then sed -Ei "s/^(ZITI_ROUTER_MODE)=.*/\1=${ZITI_ROUTER_MODE}/" /opt/openziti/etc/router/env fi fi From d4c063c81f14aa3e966154051358175d1ed419d5 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Tue, 26 Mar 2024 14:46:56 -0400 Subject: [PATCH 5/6] set optional prerelease revision tag on packages and containers when testing locally --- dist/dist-packages/linux/nfpm-openziti-controller.yaml | 1 + dist/dist-packages/linux/nfpm-openziti-router.yaml | 1 + dist/dist-packages/linux/nfpm-openziti.yaml | 1 + dist/docker-images/ziti-controller/Dockerfile | 4 +++- dist/docker-images/ziti-router/Dockerfile | 7 +++++-- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/dist/dist-packages/linux/nfpm-openziti-controller.yaml b/dist/dist-packages/linux/nfpm-openziti-controller.yaml index 4e887a3fd..ec552bb10 100644 --- a/dist/dist-packages/linux/nfpm-openziti-controller.yaml +++ b/dist/dist-packages/linux/nfpm-openziti-controller.yaml @@ -6,6 +6,7 @@ name: openziti-controller arch: ${GOARCH} platform: linux version: ${ZITI_VERSION} +prerelease: ${ZITI_REV} maintainer: ${ZITI_MAINTAINER} description: > Provides a system service for running an OpenZiti Controller diff --git a/dist/dist-packages/linux/nfpm-openziti-router.yaml b/dist/dist-packages/linux/nfpm-openziti-router.yaml index bce61b2db..877dac65b 100644 --- a/dist/dist-packages/linux/nfpm-openziti-router.yaml +++ b/dist/dist-packages/linux/nfpm-openziti-router.yaml @@ -6,6 +6,7 @@ name: openziti-router arch: ${GOARCH} platform: linux version: ${ZITI_VERSION} +prerelease: ${ZITI_REV} maintainer: ${ZITI_MAINTAINER} description: > Provides a system service for running an OpenZiti Router diff --git a/dist/dist-packages/linux/nfpm-openziti.yaml b/dist/dist-packages/linux/nfpm-openziti.yaml index 20be50d4b..49d11a761 100644 --- a/dist/dist-packages/linux/nfpm-openziti.yaml +++ b/dist/dist-packages/linux/nfpm-openziti.yaml @@ -6,6 +6,7 @@ name: openziti arch: ${GOARCH} platform: linux version: ${ZITI_VERSION} +prerelease: ${ZITI_REV} maintainer: ${ZITI_MAINTAINER} description: > The openziti package provides the ziti executable binary as a command line diff --git a/dist/docker-images/ziti-controller/Dockerfile b/dist/docker-images/ziti-controller/Dockerfile index 25688bcaf..433b2e3d2 100644 --- a/dist/docker-images/ziti-controller/Dockerfile +++ b/dist/docker-images/ziti-controller/Dockerfile @@ -3,6 +3,8 @@ ARG ZITI_CLI_IMAGE="docker.io/openziti/ziti-cli" # this builds docker.io/openziti/ziti-controller FROM ${ZITI_CLI_IMAGE}:${ZITI_CLI_TAG} +ARG DOCKER_BUILD_DIR=. + # This build stage grabs artifacts that are copied into the final image. # It uses the same base as the final image to maximize docker cache hits. @@ -14,7 +16,7 @@ LABEL name="openziti/ziti-controller" \ description="Run the OpenZiti Controller" USER root -COPY ./entrypoint.sh / +COPY ${DOCKER_BUILD_DIR}/entrypoint.sh / RUN chmod +x /entrypoint.sh USER ziggy ENTRYPOINT [ "/entrypoint.sh" ] diff --git a/dist/docker-images/ziti-router/Dockerfile b/dist/docker-images/ziti-router/Dockerfile index 702f2cfa8..b42ce7ad5 100644 --- a/dist/docker-images/ziti-router/Dockerfile +++ b/dist/docker-images/ziti-router/Dockerfile @@ -3,6 +3,9 @@ ARG ZITI_CLI_IMAGE="docker.io/openziti/ziti-cli" # this builds docker.io/openziti/ziti-router FROM ${ZITI_CLI_IMAGE}:${ZITI_CLI_TAG} +ARG ROUTER_PACKAGE=./dist/dist-packages/linux/openziti-router +ARG DOCKER_BUILD_DIR=. + ### Required OpenShift Labels LABEL name="openziti/ziti-router" \ maintainer="developers@openziti.org" \ @@ -24,8 +27,8 @@ ENV ZITI_ROUTER_PORT=3022 ENV ZITI_BOOTSTRAP_CONFIG=true ENV ZITI_BOOTSTRAP_ENROLLMENT=true ENV PFXLOG_NO_JSON=true -COPY ./dist/dist-packages/linux/openziti-router/bootstrap.bash ${ZITI_ROUTER_BOOTSTRAP_BASH} -COPY ./dist/dist-packages/linux/openziti-router/entrypoint.bash / +COPY ${ROUTER_PACKAGE}/bootstrap.bash ${ZITI_ROUTER_BOOTSTRAP_BASH} +COPY ${ROUTER_PACKAGE}/entrypoint.bash / WORKDIR /ziti-router RUN chown -R ziggy:ziggy /ziti-router From 3ff331ff21749b7c85184b938a4f9b5616b6ad76 Mon Sep 17 00:00:00 2001 From: Kenneth Bingham Date: Tue, 26 Mar 2024 16:11:34 -0400 Subject: [PATCH 6/6] add controller container project file and example --- dist/docker-images/ziti-controller/Dockerfile | 40 +++++++++++++-- dist/docker-images/ziti-controller/README.md | 41 +++++++++++++++ .../docker-images/ziti-controller/compose.yml | 50 +++++++++++++++++++ 3 files changed, 127 insertions(+), 4 deletions(-) create mode 100644 dist/docker-images/ziti-controller/README.md create mode 100644 dist/docker-images/ziti-controller/compose.yml diff --git a/dist/docker-images/ziti-controller/Dockerfile b/dist/docker-images/ziti-controller/Dockerfile index 433b2e3d2..3718f9116 100644 --- a/dist/docker-images/ziti-controller/Dockerfile +++ b/dist/docker-images/ziti-controller/Dockerfile @@ -3,6 +3,7 @@ ARG ZITI_CLI_IMAGE="docker.io/openziti/ziti-cli" # this builds docker.io/openziti/ziti-controller FROM ${ZITI_CLI_IMAGE}:${ZITI_CLI_TAG} +ARG CONTROLLER_PACKAGE=./dist/dist-packages/linux/openziti-controller ARG DOCKER_BUILD_DIR=. # This build stage grabs artifacts that are copied into the final image. @@ -15,9 +16,40 @@ LABEL name="openziti/ziti-controller" \ summary="Run the OpenZiti Controller" \ description="Run the OpenZiti Controller" +# set up image as root, then drop privs to ziggy USER root -COPY ${DOCKER_BUILD_DIR}/entrypoint.sh / -RUN chmod +x /entrypoint.sh + +# selectively toggle bootstrapping steps +ENV ZITI_BOOTSTRAP_PKI=true +ENV ZITI_BOOTSTRAP_CONFIG=true +ENV ZITI_BOOTSTRAP_DATABASE=true + +# defaults for bootstrapping PKI +ENV ZITI_PKI_ROOT=pki +ENV ZITI_CA_FILE=root +ENV ZITI_INTERMEDIATE_FILE=intermediate +ENV ZITI_SERVER_FILE=server +ENV ZITI_CLIENT_FILE=client + +# defaults for bootstrapping config +ENV ZITI_CTRL_ADVERTISED_PORT=1280 + +# defaults for bootstrapping database +ENV ZITI_CTRL_DATABASE_FILE=bbolt.db +ENV ZITI_USER=admin + +# emit human-friendly text logs +ENV PFXLOG_NO_JSON=true +# used by entrypoint.bash to source the bootstrapping script in this image +ENV ZITI_CTRL_BOOTSTRAP_BASH=/bootstrap.bash +# used by "ziti create config controller" as filename for the controller's identity files +ENV ZITI_NETWORK_NAME=ctrl +# used by ziti to format timestamps in output +ENV ZITI_TIME_FORMAT=utc + +COPY ${CONTROLLER_PACKAGE}/bootstrap.bash ${ZITI_CTRL_BOOTSTRAP_BASH} +COPY ${CONTROLLER_PACKAGE}/entrypoint.bash / + +# run as ziggy (2171:2171) by default USER ziggy -ENTRYPOINT [ "/entrypoint.sh" ] -CMD [ "run" ] +ENTRYPOINT [ "/entrypoint.bash" ] diff --git a/dist/docker-images/ziti-controller/README.md b/dist/docker-images/ziti-controller/README.md new file mode 100644 index 000000000..7a9cbd94d --- /dev/null +++ b/dist/docker-images/ziti-controller/README.md @@ -0,0 +1,41 @@ + +# Run Ziti Controller in Docker + +You can use this container image to run a Ziti Controller in a Docker container. + +## Container Image + +The `openziti/ziti-controller` image is thin and is based on the `openziti/ziti-cli` image, which only provides the +`ziti` CLI. The `ziti-controller` image uses the same bootstrapping defaults and option variables as the Linux package. + +## Docker Compose + +The included `compose.yml` demonstrates how to bootstrap a controller container. + +### Example + +At a minimum, you must set the address and password options in the parent env or set every recurrence in the compose file. + +```bash +ZITI_PWD="mypass" \ +ZITI_CTRL_ADVERTISED_ADDRESS=ctrl.127.0.0.1.sslip.io \ + docker compose up +``` + +After a few seconds, `docker compose ps` will show a "healthy" status for the controller. + +Then, you may log in to the controller using the `ziti` CLI. + +```bash +ziti edge login ctrl.127.0.0.1.sslip.io:1280 -u admin -p mypass +``` + +It's not always necessary to publish ports on every one of the Docker host's interfaces. You can instead publish the +controller port only on a particular interface address by setting `ZITI_INTERFACE`. + +```bash +ZITI_PWD="mypass" \ +ZITI_INTERFACE=127.21.71.0 \ +ZITI_CTRL_ADVERTISED_ADDRESS=ctrl.127.21.71.0.sslip.io \ + docker compose up +``` diff --git a/dist/docker-images/ziti-controller/compose.yml b/dist/docker-images/ziti-controller/compose.yml new file mode 100644 index 000000000..7ac19f026 --- /dev/null +++ b/dist/docker-images/ziti-controller/compose.yml @@ -0,0 +1,50 @@ + +volumes: + ziti-controller: + driver: local + +services: + chown-volume: + image: busybox + command: chown -R ${ZIGGY_UID:-2171} /mnt + volumes: + - ziti-controller:/mnt + + ziti-controller: + image: ${ZITI_CONTROLLER_IMAGE:-openziti/ziti-controller} + depends_on: + chown-volume: + condition: service_completed_successfully + volumes: + - ziti-controller:/mnt + working_dir: /mnt + # these declared vars pass through to container and should be assigned in an .env file or exported from parent env + # to ensure consistency throughout the compose project + environment: + # *** these are the important vars to set *** + ZITI_CTRL_ADVERTISED_ADDRESS: # domain name of this controller (required) + ZITI_CTRL_ADVERTISED_PORT: ${ZITI_CTRL_ADVERTISED_PORT:-1280} # exposed port of this controller + ZITI_PWD: ${ZITI_PWD:-} # password for the default admin user + + # *** less relevant vars below *** + ZITI_BOOTSTRAP_PKI: true + ZITI_BOOTSTRAP_CONFIG: true # make config file from env vars and defaults if "true," overwrite if "force" + ZITI_BOOTSTRAP_DATABASE: true # make the default admin user if "true" + ZITI_AUTO_RENEW_CERTS: true # renew certs automatically every startup + command: run config.yml + ports: + # ensure this port matches the value of ZITI_CTRL_PORT in the container + - ${ZITI_INTERFACE:-0.0.0.0}:${ZITI_CTRL_ADVERTISED_PORT:-1280}:${ZITI_CTRL_ADVERTISED_PORT:-1280} + expose: + - ${ZITI_CTRL_ADVERTISED_PORT:-1280} + restart: unless-stopped + healthcheck: + test: + - CMD + - ziti + - agent + - stats + interval: 3s + timeout: 3s + retries: 5 + start_period: 15s