Skip to content

Commit

Permalink
add Linux package openziti-controller
Browse files Browse the repository at this point in the history
  • Loading branch information
qrkourier committed Mar 6, 2024
1 parent fab2dab commit 68dc9d8
Show file tree
Hide file tree
Showing 11 changed files with 271 additions and 10 deletions.
3 changes: 3 additions & 0 deletions dist/dist-packages/linux/nfpm-openziti-controller.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ contents:
src: ./dist/dist-packages/linux/openziti-controller/env
type: config|noreplace

- dst: /opt/openziti/etc/controller/
src: ./dist/dist-packages/linux/openziti-controller/bootstrap.bash

- dst: /opt/openziti/etc/controller/
src: ./dist/dist-packages/linux/openziti-controller/entrypoint.bash

168 changes: 168 additions & 0 deletions dist/dist-packages/linux/openziti-controller/bootstrap.bash
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#!/usr/bin/env bash
#
# bootstrap the OpenZiti Controller with PKI, config file, and database
#

set -o errexit
set -o nounset
set -o pipefail

# use the ziti executable that the 'openziti' package installed
PATH=/opt/openziti/bin:$PATH

#
# defaults
#

# used by "ziti pki create server" as DNS SAN and "ziti create config controller" as advertised address
: "${ZITI_CONTROLLER_ADVERTISED_ADDRESS:=$(hostname -f)}"

function makePki() {
#
# create root and intermediate CA
#

if [ "$ZITI_CA_FILE" == "$ZITI_INTERMEDIATE_FILE" ]; then
echo "ERROR: ZITI_CA_FILE and ZITI_INTERMEDIATE_FILE must be different" >&2
exit 1
fi

ROOT_CA_DIR="./${ZITI_PKI_ROOT}/${ZITI_CA_FILE}"
if ! [ -d "$ROOT_CA_DIR" ]; then
ziti pki create ca \
--pki-root "./${ZITI_PKI_ROOT}" \
--ca-file "${ZITI_CA_FILE}"
fi

ZITI_PKI_SIGNER_CERT="./${ZITI_PKI_ROOT}/${ZITI_INTERMEDIATE_FILE}/certs/${ZITI_INTERMEDIATE_FILE}.cert"
ZITI_PKI_SIGNER_KEY="./${ZITI_PKI_ROOT}/${ZITI_INTERMEDIATE_FILE}/keys/${ZITI_INTERMEDIATE_FILE}.key"
if [[ ! -s "$ZITI_PKI_SIGNER_CERT" && ! -s "$ZITI_PKI_SIGNER_KEY" ]]; then
ziti pki create intermediate \
--pki-root "./${ZITI_PKI_ROOT}" \
--ca-name "${ZITI_CA_FILE}" \
--intermediate-file "${ZITI_INTERMEDIATE_FILE}"
elif [[ ! -s "$ZITI_PKI_SIGNER_CERT" || ! -s "$ZITI_PKI_SIGNER_KEY" ]]; then
echo "ERROR: $ZITI_PKI_SIGNER_CERT and $ZITI_PKI_SIGNER_KEY must both exist or neither exist as non-empty files" >&2
exit 1
fi

#
# create server and client keys
#

if [ "$ZITI_SERVER_FILE" == "$ZITI_CLIENT_FILE" ]; then
echo "ERROR: ZITI_SERVER_FILE and ZITI_CLIENT_FILE must be different" >&2
exit 1
fi

ZITI_PKI_CTRL_KEY="./${ZITI_PKI_ROOT}/${ZITI_INTERMEDIATE_FILE}/keys/${ZITI_SERVER_FILE}.key"
if ! [ -s "$ZITI_PKI_CTRL_KEY" ]; then
ziti pki create key \
--pki-root "./${ZITI_PKI_ROOT}" \
--ca-name "${ZITI_INTERMEDIATE_FILE}" \
--key-file "${ZITI_SERVER_FILE}"
fi

# use the server key for both client and server certs until "ziti create config controller" supports separate keys for
# each
# CLIENT_KEY_FILE="./${ZITI_PKI_ROOT}/${ZITI_INTERMEDIATE_FILE}/keys/${ZITI_CLIENT_FILE}.key"
# if ! [ -s "$CLIENT_KEY_FILE" ]; then
# ziti pki create key \
# --pki-root "./${ZITI_PKI_ROOT}" \
# --ca-name "${ZITI_INTERMEDIATE_FILE}" \
# --key-file "${ZITI_CLIENT_FILE}"
# fi

#
# create server and client certs
#

# server cert
ZITI_PKI_CTRL_SERVER_CERT="./${ZITI_PKI_ROOT}/${ZITI_INTERMEDIATE_FILE}/certs/${ZITI_SERVER_FILE}.chain.pem"
if [[ "${ZITI_AUTO_RENEW_CERTS}" == true || ! -s "$ZITI_PKI_CTRL_SERVER_CERT" ]]; then
ziti pki create server \
--pki-root "./${ZITI_PKI_ROOT}" \
--ca-name "${ZITI_INTERMEDIATE_FILE}" \
--key-file "${ZITI_SERVER_FILE}" \
--server-file "${ZITI_SERVER_FILE}" \
--dns "${ZITI_CONTROLLER_ADVERTISED_ADDRESS}" \
--allow-overwrite
fi

# client cert
# use the server key for both client and server certs until "ziti create config controller" supports separate keys for
# each
ZITI_PKI_CTRL_CERT="./${ZITI_PKI_ROOT}/${ZITI_INTERMEDIATE_FILE}/certs/${ZITI_CLIENT_FILE}.cert"
if [[ "${ZITI_CERT_AUTO_RENEW}" == true || ! -s "$ZITI_PKI_CTRL_CERT" ]]; then
ziti pki create client \
--pki-root "./${ZITI_PKI_ROOT}" \
--ca-name "${ZITI_INTERMEDIATE_FILE}" \
--key-file "${ZITI_SERVER_FILE}" \
--client-file "${ZITI_CLIENT_FILE}" \
--allow-overwrite
fi

}

function makeConfig() {
#
# create config file
#

# set the path to the root CA cert
export ZITI_PKI_CTRL_CA="./${ZITI_PKI_ROOT}/${ZITI_CA_FILE}/certs/${ZITI_CA_FILE}.cert"

# set the interface address on which to listen for connections; e.g., 0.0.0.0
export ZITI_CTRL_BIND_ADDRESS="${ZITI_CONTROLLER_BIND_ADDRESS}"
export ZITI_CTRL_EDGE_BIND_ADDRESS="${ZITI_CONTROLLER_BIND_ADDRESS}"

# set the URI of the router ctrl plane; e.g., ctrl.endpoint: ziti.example.com:443
export ZITI_CTRL_ADVERTISED_ADDRESS="${ZITI_CONTROLLER_ADVERTISED_ADDRESS}"
export ZITI_CTRL_ADVERTISED_PORT="${ZITI_CONTROLLER_ADVERTISED_PORT}"

# set the URI of the edge-client API (uses same TCP port); e.g., ztAPI: ziti.example.com:443
export ZITI_CTRL_EDGE_ADVERTISED_ADDRESS="${ZITI_CONTROLLER_ADVERTISED_ADDRESS}"
export ZITI_CTRL_EDGE_ADVERTISED_PORT="${ZITI_CONTROLLER_ADVERTISED_PORT}"

# export the vars that were assigned inside this script to set the path to the server and client certs and their common
# private key, and the intermediate (signer) CA cert and key
export ZITI_PKI_CTRL_SERVER_CERT \
ZITI_PKI_CTRL_CERT \
ZITI_PKI_CTRL_KEY \
ZITI_PKI_SIGNER_CERT \
ZITI_PKI_SIGNER_KEY

if [ ! -s "./${ZITI_CONTROLLER_CONFIG_FILE}" ]; then
ziti create config controller \
--output "./${ZITI_CONTROLLER_CONFIG_FILE}"
fi

}

function makeDatabase() {

#
# create default admin in database
#

# if the database file is in a subdirectory, create the directory so that "ziti controller edge init" can load the
# controller config.yml which contains a check to ensure the directory exists
DB_DIR="$(dirname "${ZITI_CTRL_DATABASE_FILE}")"
if ! [ "$DB_DIR" == "." ]; then
mkdir -p "./$DB_DIR"
fi

if [[ $(wc -c <<< "${ZITI_PWD:-}") -gt 5 || -s /run/credentials/ZITI_PWD ]]; then
ziti controller edge init "./${ZITI_CONTROLLER_CONFIG_FILE}" \
--username "${ZITI_USER}" \
--password "${ZITI_PWD:-$(< /run/credentials/ZITI_PWD)}"
else
echo "ERROR: use SetCredential or LoadCredential in"\
" ziti-controller.service or set env var ZITI_PWD of at least 5 characters" >&2
fi

}

if [ "${ZITI_BOOTSTRAP_PKI}" == true ]; then makePki; fi
if [ "${ZITI_BOOTSTRAP_CONFIG}" == true ]; then makeConfig; fi
if [ "${ZITI_BOOTSTRAP_DATABASE}" == true ]; then makeDatabase; fi
9 changes: 4 additions & 5 deletions dist/dist-packages/linux/openziti-controller/entrypoint.bash
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
#!/usr/bin/env bash
#
# this thin wrapper script for the OpenZiti Controller enables
# - evaluating arguments from the env file
# - future: bootstrapping a default run environment with PKI and initialized database
# this thin wrapper script for the OpenZiti Controller evaluates arguments from the env file
#

set -o errexit
set -o nounset
set -o pipefail

# shellcheck disable=SC2068 # because we want to word-split args
exec /opt/openziti/bin/ziti controller run $@
# shellcheck disable=SC2068 # because we must
# shellcheck disable=SC2086 # word-split args
exec /opt/openziti/bin/ziti controller run ${ZITI_CONTROLLER_CONFIG_FILE} ${ZITI_CONTROLLER_RUN_ARGS} $@
56 changes: 54 additions & 2 deletions dist/dist-packages/linux/openziti-controller/env
Original file line number Diff line number Diff line change
@@ -1,2 +1,54 @@
ZITI_CONTROLLER_RUN_ARGS="config.yml --log-formatter text --verbose"
#PFXLOG_NO_JSON=true
#
# this is a systemd env file allowing simple assignments for ziti-controller.service environment
#

ZITI_CONTROLLER_RUN_ARGS="--log-formatter text"
# ZITI_CONTROLLER_RUN_ARGS="--log-formatter text --verbose"
# PFXLOG_NO_JSON=true

#
# for "ziti pki" and "ziti create config" commands in bootstrap.bash
#

# the advertised address of the controller is a domain name that can be resolved by all devices (default: hostname -f)
ZITI_CONTROLLER_ADVERTISED_ADDRESS=
# the advertised and listening port of the controller (default: 443)
ZITI_CONTROLLER_ADVERTISED_PORT=443
# the interface address to bind the controller to (default: 0.0.0.0)
ZITI_CONTROLLER_BIND_ADDRESS=0.0.0.0

#
# for "ziti pki" commands in bootstrap.bash
#

# create a new PKI unless it exists
ZITI_BOOTSTRAP_PKI=true
# renew server and client certificates every startup (invalidates unused enrollment tokens)
ZITI_AUTO_RENEW_CERTS=true
# relative to systemd service WorkingDirectory; e.g., /var/lib/ziti-controller/pki
ZITI_PKI_ROOT=pki
# relative to ZITI_PKI_ROOT; root CA dir; e.g., /var/lib/ziti-controller/pki/root
ZITI_CA_FILE=root
# relative to ZITI_PKI_ROOT; intermediate CA dir; e.g., /var/lib/ziti-controller/pki/intermediate
ZITI_INTERMEDIATE_FILE=intermediate
# relative to intermediate CA "keys" and "certs" dirs
ZITI_SERVER_FILE=controller-server-identity
# relative to intermediate CA "keys" and "certs" dirs
ZITI_CLIENT_FILE=controller-client-identity

#
# for "ziti create config" command in bootstrap.bash
#
# create a config file unless it exists
ZITI_BOOTSTRAP_CONFIG=true
# create a new config file relative to working directory unless it exists
ZITI_CONTROLLER_CONFIG_FILE=config.yml
ZITI_CTRL_DATABASE_FILE=ctrl.db

#
# for "ziti controller edge init" command in bootstrap.bash
#
# create a database unless it exists
ZITI_BOOTSTRAP_DATABASE=true
# must be 4 < 100 characters
ZITI_USER=admin
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,20 @@ After=network-online.target
[Service]
Type=simple
DynamicUser=yes
AmbientCapabilities=CAP_NET_BIND_SERVICE
StateDirectory=ziti-controller
# WorkingDirectory=/var/lib/ziti-controller
# set a password as literal string
SetCredential=ZITI_PWD:admin
# or load password from a file; set owner to root and chmod 0400 for security
# LoadCredential=ZITI_PWD:/opt/openziti/etc/controller/.ziti_pwd
UMask=0007
Restart=always
RestartSec=3
LimitNOFILE=65535
EnvironmentFile=/opt/openziti/etc/controller/env
ExecStart=/opt/openziti/etc/controller/entrypoint.bash ${ZITI_CONTROLLER_RUN_ARGS}
ExecStartPre=/opt/openziti/etc/controller/bootstrap.bash
ExecStart=/opt/openziti/etc/controller/entrypoint.bash

[Install]
WantedBy=multi-user.target
2 changes: 1 addition & 1 deletion ziti/cmd/create/config_templates/controller.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ v: 3
# memory:
# path: ctrl.memprof

db: "{{ .ZitiHome }}/db/ctrl.db"
db: "{{ .ZitiHome }}/{{ .Controller.Database.DatabaseFile }}"

identity:
cert: "{{ .Controller.Identity.Cert }}"
Expand Down
5 changes: 5 additions & 0 deletions ziti/cmd/create/create_config.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ type IdentityValues struct {
AltCertsEnabled bool
}

type DatabaseValues struct {
DatabaseFile string
}

type WebOptionsValues struct {
IdleTimeout time.Duration
ReadTimeout time.Duration
Expand All @@ -130,6 +134,7 @@ type WebOptionsValues struct {

type ControllerTemplateValues struct {
Identity IdentityValues
Database DatabaseValues
Ctrl CtrlValues
HealthChecks HealthChecksValues
EdgeApi EdgeApiValues
Expand Down
19 changes: 18 additions & 1 deletion ziti/cmd/create/create_config_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,10 @@ func NewCmdCreateConfigController() *CreateControllerConfigCmd {
data.Controller.EdgeEnrollment.EdgeRouterDuration = controllerOptions.EdgeRouterEnrollmentDuration
}

if controllerOptions.DatabaseFile != "" && controllerOptions.DatabaseFile != constants.DefaultCtrlDatabaseFile {
data.Controller.Database.DatabaseFile = controllerOptions.DatabaseFile
}

// process identity information
SetControllerIdentity(&data.Controller)
SetEdgeConfig(&data.Controller)
Expand All @@ -147,7 +151,7 @@ func NewCmdCreateConfigController() *CreateControllerConfigCmd {

func (options *CreateConfigControllerOptions) addFlags(cmd *cobra.Command) {
cmd.Flags().StringVar(&options.CtrlPort, optionCtrlPort, constants.DefaultCtrlAdvertisedPort, "port used for the router to controller communication")
cmd.Flags().StringVar(&options.DatabaseFile, optionDatabaseFile, "ctrl.db", "location of the database file")
cmd.Flags().StringVar(&options.DatabaseFile, optionDatabaseFile, constants.DefaultCtrlDatabaseFile, "location of the database file")
cmd.Flags().DurationVar(&options.EdgeIdentityEnrollmentDuration, optionEdgeIdentityEnrollmentDuration, edge.DefaultEdgeEnrollmentDuration, "the edge identity enrollment duration, use 0h0m0s format")
cmd.Flags().DurationVar(&options.EdgeRouterEnrollmentDuration, optionEdgeRouterEnrollmentDuration, edge.DefaultEdgeEnrollmentDuration, "the edge router enrollment duration, use 0h0m0s format")
}
Expand Down Expand Up @@ -194,6 +198,7 @@ func SetControllerIdentity(data *ControllerTemplateValues) {
SetControllerIdentityServerCert(data)
SetControllerIdentityKey(data)
SetControllerIdentityCA(data)
setDatabaseFile(data)
}
func SetControllerIdentityCert(c *ControllerTemplateValues) {
val := os.Getenv(constants.PkiCtrlCertVarName)
Expand Down Expand Up @@ -224,6 +229,18 @@ func SetControllerIdentityCA(c *ControllerTemplateValues) {
c.Identity.Ca = helpers.NormalizePath(val)
}

func setDatabaseFile(c *ControllerTemplateValues) {
if c.Database.DatabaseFile != "" {
c.Database.DatabaseFile = helpers.NormalizePath(c.Database.DatabaseFile)
} else {
val := os.Getenv(constants.CtrlDatabaseFileVarName)
if val == "" {
val = constants.DefaultCtrlDatabaseFile // default
}
c.Database.DatabaseFile = helpers.NormalizePath(val)
}
}

func SetEdgeConfig(data *ControllerTemplateValues) {
SetEdgeSigningCert(data)
SetEdgeSigningKey(data)
Expand Down
4 changes: 4 additions & 0 deletions ziti/cmd/helpers/env_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ func GetCtrlEdgeAdvertisedPort() string {
return getFromEnv(constants.CtrlEdgeAdvertisedPortVarName, defaultValue(constants.DefaultCtrlEdgeAdvertisedPort))
}

func GetCtrlDatabaseFile() string {
return getFromEnv(constants.CtrlDatabaseFileVarName, defaultValue(constants.DefaultCtrlDatabaseFile))
}

func GetZitiEdgeRouterPort() string {
return getFromEnv(constants.ZitiEdgeRouterPortVarName, defaultValue(constants.DefaultZitiEdgeRouterPort))
}
Expand Down
2 changes: 2 additions & 0 deletions ziti/cmd/pki/pki_create_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ func (o *PKICreateClientOptions) addPKICreateClientFlags(cmd *cobra.Command) {
cmd.Flags().IntVarP(&o.Flags.CAPrivateKeySize, "private-key-size", "", 4096, "Size of the RSA private key, ignored if -curve is set")
cmd.Flags().StringVarP(&o.Flags.EcCurve, "curve", "", "", "If set an EC private key is generated and -private-key-size is ignored, options: P224, P256, P384, P521")
cmd.Flags().StringVar(&o.Flags.SpiffeID, "spiffe-id", "", "Optionally provide the path portion of a SPIFFE id. The trust domain will be taken from the signing certificate.")
cmd.Flags().BoolVar(&o.Flags.AllowOverwrite, "allow-overwrite", false, "Allow overwrite existing certs")
}

// Run implements this command
Expand Down Expand Up @@ -158,6 +159,7 @@ func (o *PKICreateClientOptions) Run() error {
Template: template,
IsClientCertificate: true,
PrivateKeyOptions: privateKeyOptions,
AllowOverwrite: o.Flags.AllowOverwrite,
}

if err := o.Flags.PKI.Sign(signer, req); err != nil {
Expand Down
4 changes: 4 additions & 0 deletions ziti/constants/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ const (
DefaultCtrlBindAddress = "0.0.0.0"
DefaultCtrlAdvertisedPort = "6262"

DefaultCtrlDatabaseFile = "db/ctrl.db"

DefaultCtrlEdgeBindAddress = "0.0.0.0"
DefaultCtrlEdgeAdvertisedPort = "1280"

Expand Down Expand Up @@ -82,6 +84,8 @@ const (
CtrlEdgeAltAdvertisedAddressVarDescription = "The publicly addressable, alternative controller address value. Overrides ZITI_CTRL_EDGE_ADVERTISED_ADDRESS"
CtrlEdgeAdvertisedPortVarName = "ZITI_CTRL_EDGE_ADVERTISED_PORT"
CtrlEdgeAdvertisedPortVarDescription = "The publicly addressable controller port value"
CtrlDatabaseFileVarName = "ZITI_CTRL_DATABASE_FILE"
CtrlDatabaseFileVarDescription = "Path to the Ziti Controller Database File"
PkiSignerCertVarName = "ZITI_PKI_SIGNER_CERT"
PkiSignerCertVarDescription = "Path to the Ziti Signing Cert"
PkiSignerKeyVarName = "ZITI_PKI_SIGNER_KEY"
Expand Down

0 comments on commit 68dc9d8

Please sign in to comment.