diff --git a/.gitallowed b/.gitallowed new file mode 100644 index 000000000..7f8e29e7b --- /dev/null +++ b/.gitallowed @@ -0,0 +1 @@ +KCTF_CLOUD_API_KEY=.* diff --git a/vrp/README.md b/vrp/README.md new file mode 100644 index 000000000..aee3af0f3 --- /dev/null +++ b/vrp/README.md @@ -0,0 +1,5 @@ += kCTF VRP = + +This directory contains the code for the kCTF VRP. +For more information, check out: +https://google.github.io/kctf/vrp.html diff --git a/vrp/full-chain/challenge.yaml b/vrp/full-chain/challenge.yaml new file mode 100644 index 000000000..02062ce7d --- /dev/null +++ b/vrp/full-chain/challenge.yaml @@ -0,0 +1,26 @@ +apiVersion: kctf.dev/v1 +kind: Challenge +metadata: + name: full-chain +spec: + deployed: true + powDifficultySeconds: 0 + network: + public: false + healthcheck: + enabled: false + podTemplate: + template: + spec: + containers: + - name: challenge + volumeMounts: + - mountPath: /flag + name: flag + readOnly: true + volumes: + - name: flag + secret: + defaultMode: 0555 + secretName: full-chain-flag + optional: true diff --git a/vrp/full-chain/challenge/Dockerfile b/vrp/full-chain/challenge/Dockerfile new file mode 100644 index 000000000..07b5c55b4 --- /dev/null +++ b/vrp/full-chain/challenge/Dockerfile @@ -0,0 +1,16 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM gcr.io/kctf-docker/challenge@sha256:e550af5df266cb89a26ace1ba5dcc685981f01d1cb61e45a898cce0c9753de7a + +CMD while true; do sleep 60; done diff --git a/vrp/kctf/.gitignore b/vrp/kctf/.gitignore new file mode 100644 index 000000000..b549a08d0 --- /dev/null +++ b/vrp/kctf/.gitignore @@ -0,0 +1,4 @@ +config/* +bin/kind +bin/kubectl +bin/yq diff --git a/vrp/kctf/VERSION b/vrp/kctf/VERSION new file mode 100644 index 000000000..21e8796a0 --- /dev/null +++ b/vrp/kctf/VERSION @@ -0,0 +1 @@ +1.0.3 diff --git a/vrp/kctf/activate b/vrp/kctf/activate new file mode 100644 index 000000000..c65f4136e --- /dev/null +++ b/vrp/kctf/activate @@ -0,0 +1,276 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +KCTF_YQ_URL="https://github.com/mikefarah/yq/releases/download/v4.2.0/yq_linux_amd64" +KCTF_YQ_HASH="5d44bd64e264e9029c5f06bcd960ba162d7ed7ddd1781f02a28d62f50577b632" + +KCTF_KIND_URL="https://kind.sigs.k8s.io/dl/v0.10.0/kind-linux-amd64" +KCTF_KIND_HASH="74767776488508d847b0bb941212c1cb76ace90d9439f4dee256d8a04f1309c6" + +KCTF_KUBECTL_URL="https://dl.k8s.io/release/v1.20.4/bin/linux/amd64/kubectl" +KCTF_KUBECTL_HASH="98e8aea149b00f653beeb53d4bd27edda9e73b48fed156c4a0aa1dabe4b1794c" + +export KCTF_CTF_DIR="$(realpath --no-symlinks "$(dirname "${BASH_SOURCE-$0}")/..")" +export KCTF_BIN="${KCTF_CTF_DIR}/kctf/bin" +source "${KCTF_BIN}/kctf-log" + +function _kctf_check_umask { + if [[ $((8#$(umask) & 8#755)) -ne 0 ]]; then + _kctf_log_err "umask is too prohibitive. Please set it to 022 when using kctf" + return 1 + fi + if [[ "$(stat "${KCTF_BIN}/kctf-cluster" --format '%a')" -ne "755" ]]; then + _kctf_log_err "${KCTF_BIN}/kctf-cluster has unexpected permissions. Maybe a umask problem during checkout?" + return 1 + fi +} + +function _kctf_setup_environment { + KCTF_CONFIG_DIR="$(mktemp -d --tmpdir kctf.XXXXXXXXXX)" + if [[ $? -ne 0 ]]; then + return 1 + fi + export KCTF_CTF_NAME=$(basename "${KCTF_CTF_DIR}") + export KCTF_SESSION="$(dd if=/dev/urandom bs=1 count=10 2>/dev/null | xxd -ps -c 10)" + + export KUBECONFIG="${KCTF_CONFIG_DIR}/kube.conf" +} + +function _kctf_download_dependencies { + if [[ ! -x "${KCTF_BIN}/yq" ]]; then + if [[ -e "${KCTF_BIN}/yq" ]]; then + rm "${KCTF_BIN}/yq" >/dev/null + fi + wget "${KCTF_YQ_URL}" -O "${KCTF_BIN}/yq" --quiet || return 1 + sha256sum --status -c <(echo "${KCTF_YQ_HASH} ${KCTF_BIN}/yq") || return 1 + chmod u+x "${KCTF_BIN}/yq" + fi + + if [[ ! -x "${KCTF_BIN}/kind" ]]; then + curl -Lo "${KCTF_BIN}/kind" "${KCTF_KIND_URL}" || return 1 + sha256sum --status -c <(echo "${KCTF_KIND_HASH} ${KCTF_BIN}/kind") || return 1 + chmod u+x "${KCTF_BIN}/kind" + fi + alias "kind=${KCTF_BIN}/kind" + + if [[ ! -x "${KCTF_BIN}/kubectl" ]]; then + curl -Lo "${KCTF_BIN}/kubectl" "${KCTF_KUBECTL_URL}" || return 1 + sha256sum --status -c <(echo "${KCTF_KUBECTL_HASH} ${KCTF_BIN}/kubectl") || return 1 + chmod u+x "${KCTF_BIN}/kubectl" + fi + alias "kubectl=${KCTF_BIN}/kubectl" +} + +function _kctf_cleanup { + if command -v gcloud >/dev/null 2>&1; then + unset CLOUDSDK_ACTIVE_CONFIG_NAME + # regenerate this name in case the user changed the variable + GCLOUD_CONFIG_NAME="kctf-${KCTF_SESSION}" + if gcloud config configurations describe "${GCLOUD_CONFIG_NAME}" >/dev/null 2>&1; then + echo "Deleting gcloud config ${GCLOUD_CONFIG_NAME}" + CLOUDSDK_CORE_DISABLE_PROMPTS=1 gcloud config configurations delete "${GCLOUD_CONFIG_NAME}" + fi + fi + # regenerate this name in case the user changed the variable + KUBE_CONFIG_NAME="${KCTF_CONFIG_DIR}/kube-${KCTF_SESSION}.conf" + if [[ -e "${KUBE_CONFIG_NAME}" ]]; then + rm "${KUBE_CONFIG_NAME}" >/dev/null + fi +} + +function _kctf_usage { + echo -e "usage: kctf command subcommand [args]" >&2 + echo -e "available commands:" >&2 + echo -e " chal: commands for challenges (creating, deploying, etc.)" >&2 + echo -e " cluster: commands for clusters (creating, managing, etc.) " >&2 +} + +# Implemented as a function so that we can set environment variables where needed +function kctf { + if [[ $# -lt 1 ]]; then + _kctf_log_err "missing required argument" + _kctf_usage + return 1 + fi + case "$1" in + -h|--help) + _kctf_usage + return 0 + ;; + chal) + _kctf_set_active_challenge + shift + "${KCTF_CTF_DIR}/kctf/bin/kctf-challenge" $@ + return + ;; + cluster) + shift + if [[ "$1" == "create" ]] || [[ "$1" == "load" ]]; then + CONFIG_NAME=$("${KCTF_CTF_DIR}/kctf/bin/kctf-cluster" $@) + if [[ $? -ne 0 ]]; then + return 1 + fi + if [[ -z "${CONFIG_NAME}" ]]; then + return 0 + fi + source "${KCTF_CTF_DIR}/kctf/config/${CONFIG_NAME}" + export CLUSTER_TYPE + export PROJECT + export ZONE + export REGISTRY + export CLUSTER_NAME + export DOMAIN_NAME + export EMAIL_ADDRESS + if [[ "${CLUSTER_TYPE}" == "gce" ]]; then + export CLOUDSDK_ACTIVE_CONFIG_NAME="kctf-${KCTF_SESSION}" + fi + KCTF_CONFIG="${CONFIG_NAME}" + else + "${KCTF_CTF_DIR}/kctf/bin/kctf-cluster" $@ + fi + return + ;; + *) + _kctf_usage + return 1 + ;; + esac +} + +function _kctf_enable_completion { + source "${KCTF_BIN}/kctf-completion" +} + +function _kctf_error_cleanup { + unset -f _kctf_download_dependencies + # don't unset _kctf_cleanup since it's used in a trap below + #unset -f _kctf_cleanup + unset -f _kctf_usage + unset -f _kctf_error_cleanup + unset -f _kctf_enable_completion + unset -f _kctf_set_active_challenge + unset -f _kctf_setup_environment + unset -f _kctf_check_umask + unset -f _kctf_activate + unset -f _kctf_chal_string + unset -f _kctf_log + unset -f _kctf_log_err + unset -f kctf + unset -f deactivate + + unset KCTF_CONFIG + unset KCTF_CONFIG_DIR + unset KCTF_CTF_DIR + unset KCTF_CTF_NAME + unset KCTF_BIN + unset KCTF_SESSION + unset KCTF_YQ_URL + unset KCTF_YQ_HASH + unset KUBECONFIG + unset CHALLENGE_NAMESPACE + unset CHALLENGE_NAME + unset CHALLENGE_DIR + + unset _KCTF_PROMPT_COLOR1 + unset _KCTF_PROMPT_COLOR2 + unset _KCTF_PROMPT_COLOR_END + + unset CLUSTER_TYPE + unset PROJECT + unset ZONE + unset REGISTRY + unset CLUSTER_NAME + unset DOMAIN_NAME + unset EMAIL_ADDRESS +} + +function _kctf_set_active_challenge { + current_dir="${PWD}" + while [[ "${current_dir}" == "${KCTF_CTF_DIR}"/* ]]; do + if [[ -e "${current_dir}/challenge.yaml" ]]; then + CHALLENGE_NAME=$("${KCTF_BIN}/yq" eval --exit-status '.metadata.name' "${current_dir}/challenge.yaml" 2>/dev/null) + if [[ $? -ne 0 ]]; then + unset CHALLENGE_NAME + fi + CHALLENGE_NAMESPACE="default" + if "${KCTF_BIN}/yq" eval --exit-status '.metadata.namespace' "${current_dir}/challenge.yaml" >/dev/null 2>/dev/null; then + CHALLENGE_NAMESPACE=$("${KCTF_BIN}/yq" eval '.metadata.namespace' "${current_dir}/challenge.yaml" 2>/dev/null) + fi + export CHALLENGE_DIR="${current_dir}" + export CHALLENGE_NAME + export CHALLENGE_NAMESPACE + return 0 + fi + current_dir="$(dirname ${current_dir})" + done + unset CHALLENGE_NAME +} + +if [[ -n "${ZSH_VERSION:-}" ]]; then + _KCTF_PROMPT_COLOR1=$'%F{green}' + _KCTF_PROMPT_COLOR2=$'%F{cyan}' + _KCTF_PROMPT_COLOR_END=$'%f' +else + _KCTF_PROMPT_COLOR1=$'\001\e[0;32m\002' + _KCTF_PROMPT_COLOR2=$'\001\e[0;36m\002' + _KCTF_PROMPT_COLOR_END=$'\001\e[0m\002' +fi + +function _kctf_config_string { + if [ ! -z "${KCTF_CONFIG}" ]; then + echo "${_KCTF_PROMPT_COLOR1},config=${_KCTF_PROMPT_COLOR2}${KCTF_CONFIG}" + fi +} + +function _kctf_chal_string { + _kctf_set_active_challenge + if [ ! -z "${CHALLENGE_NAME}" ]; then + echo "${_KCTF_PROMPT_COLOR1},chal=${_KCTF_PROMPT_COLOR2}${CHALLENGE_NAME}" + fi +} + +function _kctf_activate { + _kctf_check_umask || return 1 + + if ! _kctf_setup_environment; then + _kctf_log_err 'error setting up the environment' + return 1 + fi + if ! _kctf_download_dependencies; then + _kctf_log_err 'error downloading dependencies' + return 1 + fi + _kctf_enable_completion || echo "loading shell completion failed" >&2 + SAVED_PS1="${PS1}" + _kctf_log "kCTF environment activated. Run \"deactivate\" to exit." + if kctf cluster load .lastconfig >/dev/null 2>/dev/null; then + _kctf_log "automatically loaded last config" + else + _kctf_log "To create a cluster config, run \"kctf cluster create\"" + fi + PS1="${PS1}${_KCTF_PROMPT_COLOR1}kCTF[ctf=${_KCTF_PROMPT_COLOR2}${KCTF_CTF_NAME}\$(_kctf_config_string)\$(_kctf_chal_string)${_KCTF_PROMPT_COLOR1}] >${_KCTF_PROMPT_COLOR_END} " +} + +function deactivate { + _kctf_cleanup + _kctf_error_cleanup + PS1="${SAVED_PS1}" + unset SAVED_PS1 +} + +if _kctf_activate; then + trap _kctf_cleanup EXIT +else + _kctf_error_cleanup +fi diff --git a/vrp/kctf/bin/kctf-challenge b/vrp/kctf/bin/kctf-challenge new file mode 100755 index 000000000..b795428b3 --- /dev/null +++ b/vrp/kctf/bin/kctf-challenge @@ -0,0 +1,619 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source "${KCTF_BIN}/kctf-log" + +function has_cluster_config { + [[ ! -z "${CLUSTER_NAME-}" ]] +} + +function require_cluster_config { + if ! has_cluster_config; then + _kctf_log_err "No config loaded. You need to run \"kctf config\" first." + exit 1 + fi +} + +function require_active_challenge { + if [[ -z "${CHALLENGE_DIR-}" ]]; then + _kctf_log_err "No active challenge, please cd to a challenge directory first." + exit 1 + fi +} + +function parse_help_arg_only_usage { + echo -e "usage: kctf chal ${COMMAND} [args]" >&2 + echo -e "" >&2 + echo -e "${DESCRIPTION}" >&2 + echo -e "" >&2 + echo -e "Args:" >&2 + echo -e " -h|--help print this help" >&2 +} + +function parse_help_arg_only { + OPTS="h" + LONGOPTS="help" + PARSED=$(getopt --options=$OPTS --longoptions=$LONGOPTS --name "kctf chal ${COMMAND}" -- "$@") + if [[ $? -ne 0 ]]; then + parse_help_arg_only_usage + exit 1 + fi + eval set -- "$PARSED" + + while true; do + case "$1" in + -h|--help) + parse_help_arg_only_usage + exit 0 + ;; + --) + shift + break + ;; + *) + _kctf_log_err "Unrecognized argument \"$1\"." + parse_help_arg_only_usage + exit 1 + ;; + esac + done + + require_active_challenge +} + +function parse_container_name_usage { + echo -e "usage: kctf chal ${COMMAND} [args]" >&2 + echo -e " -h|--help print this help" >&2 + echo -e " --container name of the container to interact with, e.g. challenge (default) or healthcheck" >&2 +} + +function parse_container_name { + OPTS="h" + LONGOPTS="help,container:" + PARSED=$(getopt --options=$OPTS --longoptions=$LONGOPTS --name "kctf chal ${COMMAND}" -- "$@") + if [[ $? -ne 0 ]]; then + parse_container_name_usage + exit 1 + fi + eval set -- "$PARSED" + + CONTAINER="challenge" + while true; do + case "$1" in + -h|--help) + parse_container_name_usage + exit 0 + ;; + --container) + CONTAINER="$2" + shift 2 + ;; + --) + shift + break + ;; + *) + _kctf_log_err "Unrecognized argument \"$1\"." + parse_container_name_usage + exit 1 + ;; + esac + done + + require_active_challenge +} + +function build_image { + # build the image + CONTAINER_NAME="$1" + CONTAINER_DIR="${CHALLENGE_DIR}/${CONTAINER_NAME}" + _kctf_log "building image in \"${CONTAINER_DIR}\"" + IMAGE_ID=$(docker build -q "${CONTAINER_DIR}") + if [[ $? -ne 0 ]]; then + return 1 + fi + + # strip optional sha256 prefix + if [[ "${IMAGE_ID}" = sha256:* ]]; then + IMAGE_ID=$(echo "${IMAGE_ID}" | cut -d ':' -f 2) + fi + _kctf_log "Image ID \"${IMAGE_ID}\"" +} + +function healthcheck_enabled { + [[ $("${KCTF_BIN}/yq" eval '.spec.healthcheck.enabled' "${CHALLENGE_DIR}/challenge.yaml") == "true" ]] +} + +function build_images { + build_image challenge || return + CHALLENGE_IMAGE_LOCAL="${IMAGE_ID}" + if healthcheck_enabled; then + build_image healthcheck || return + HEALTHCHECK_IMAGE_LOCAL="${IMAGE_ID}" + fi +} + +function push_image { + IMAGE_NAME=$1 + IMAGE_ID=$2 + + case "${CLUSTER_TYPE}" in + gce) + IMAGE_URL="${REGISTRY}/${PROJECT}/${CHALLENGE_NAME}-${IMAGE_NAME}:${IMAGE_ID}" + docker tag "${IMAGE_ID}" "${IMAGE_URL}" || return + docker push "${IMAGE_URL}" || return + ;; + kind) + IMAGE_URL="kind/${IMAGE_NAME}:${IMAGE_ID}" + docker tag "${IMAGE_ID}" "${IMAGE_URL}" || return + "${KCTF_BIN}/kind" load docker-image --name "${CLUSTER_NAME}" "${IMAGE_URL}" || return + ;; + *) + _kctf_log_err "unknown cluster type \"${CLUSTER_TYPE}\"" + return 1 + ;; + esac + _kctf_log "Image pushed to \"${IMAGE_URL}\"" +} + +function push_images { + push_image "challenge" "${CHALLENGE_IMAGE_LOCAL}" || return + CHALLENGE_IMAGE_REMOTE="${IMAGE_URL}" + if healthcheck_enabled; then + push_image "healthcheck" "${HEALTHCHECK_IMAGE_LOCAL}" || return + HEALTHCHECK_IMAGE_REMOTE="${IMAGE_URL}" + fi +} + +function kctf_chal_start { + require_cluster_config + COMMAND="start" DESCRIPTION="Deploy the challenge to the cluster." parse_help_arg_only $@ || return + build_images || return + push_images || return + + # update challenge.yaml with the image urls + "${KCTF_BIN}/yq" eval ".spec.image = \"${CHALLENGE_IMAGE_REMOTE}\"" --inplace "${CHALLENGE_DIR}/challenge.yaml" + if healthcheck_enabled; then + "${KCTF_BIN}/yq" eval ".spec.healthcheck.image = \"${HEALTHCHECK_IMAGE_REMOTE}\"" --inplace "${CHALLENGE_DIR}/challenge.yaml" + fi + + "${KCTF_BIN}/kubectl" apply -f "${CHALLENGE_DIR}/challenge.yaml" || return +} + +function kctf_chal_stop { + require_cluster_config + COMMAND="stop" DESCRIPTION="Stop a challenge running on the cluster." parse_help_arg_only $@ || return + "${KCTF_BIN}/kubectl" delete -f "${CHALLENGE_DIR}/challenge.yaml" || return +} + +function kctf_chal_status { + require_cluster_config + COMMAND="status" DESCRIPTION="Print the challenge status." parse_help_arg_only $@ || return + + echo "= CHALLENGE RESOURCE =" + echo + "${KCTF_BIN}/kubectl" get "challenge/${CHALLENGE_NAME}" --namespace "${CHALLENGE_NAMESPACE}" + echo + echo "= INSTANCES / PODs =" + echo + echo "Challenge execution status" + echo "This shows you how many instances of the challenges are running." + echo + "${KCTF_BIN}/kubectl" get pods -l "app=${CHALLENGE_NAME}" -o wide --namespace "${CHALLENGE_NAMESPACE}" + echo + echo + echo "= DEPLOYMENTS =" + echo + echo "Challenge deployment status" + echo "This shows you if the challenge was deployed to the cluster." + echo + "${KCTF_BIN}/kubectl" get deployments -l "app=${CHALLENGE_NAME}" -o wide --namespace "${CHALLENGE_NAMESPACE}" + echo + echo "= EXTERNAL SERVICES =" + echo + echo "Challenge external status" + echo "This shows you if the challenge is exposed externally." + echo + echo "SERVICES:" + "${KCTF_BIN}/kubectl" get services -l "app=${CHALLENGE_NAME}" -o custom-columns="NAME:.metadata.name,TYPE:.spec.type,EXTERNAL-IP:.status.loadBalancer.ingress[*]['ip'],PORT:.spec.ports[*].port,DNS:.metadata.annotations['external-dns\\.alpha\\.kubernetes\\.io/hostname']" --namespace "${CHALLENGE_NAMESPACE}" + echo + echo "Ingresses:" + "${KCTF_BIN}/kubectl" get ingress -l "app=${CHALLENGE_NAME}" -o wide --namespace "${CHALLENGE_NAMESPACE}" +} + +function kctf_chal_debug_logs_usage { + echo -e "usage: kctf chal debug logs [args]" >&2 + echo -e " -h|--help print this help" >&2 + echo -e " --container name of the container to interact with, e.g. challenge (default) or healthcheck" >&2 + echo -e " --tail how many lines to print per pod (default 20)" >&2 +} + +function kctf_chal_debug_logs { + require_cluster_config + + OPTS="h" + LONGOPTS="help,container:,tail:" + PARSED=$(getopt --options=$OPTS --longoptions=$LONGOPTS --name "kctf chal ${COMMAND}" -- "$@") + if [[ $? -ne 0 ]]; then + kctf_chal_debug_logs_usage + exit 1 + fi + eval set -- "$PARSED" + + CONTAINER="challenge" + TAIL="20" + while true; do + case "$1" in + -h|--help) + kctf_chal_debug_logs_usage + exit 0 + ;; + --container) + CONTAINER="$2" + shift 2 + ;; + --tail) + TAIL="$2" + shift 2 + ;; + --) + shift + break + ;; + *) + _kctf_log_err "Unrecognized argument \"$1\"." + kctf_chal_debug_logs_usage + exit 1 + ;; + esac + done + + require_active_challenge + + pods=($("${KCTF_BIN}/kubectl" get pods -l "app=${CHALLENGE_NAME}" -o jsonpath='{.items[*].metadata.name}')) + + if [[ ${#pods[@]} -eq 0 ]]; then + _kctf_log_err 'No pods found. Is the challenge running?' + return 1 + fi + + for pod in "${pods[@]}"; do + startTime=$("${KCTF_BIN}/kubectl" get "pods/${pod}" -o jsonpath='{.status.startTime}') + _kctf_log "== ${pod} (started @ ${startTime}) ==" + "${KCTF_BIN}/kubectl" logs "pods/${pod}" --tail="${TAIL}" -c "${CONTAINER}" --namespace "${CHALLENGE_NAMESPACE}" + done +} + +function kctf_chal_debug_ssh { + require_cluster_config + COMMAND="debug ssh" parse_container_name $@ || return + + pods=($("${KCTF_BIN}/kubectl" get pods -l "app=${CHALLENGE_NAME}" -o jsonpath='{.items[*].metadata.name}')) + + if [[ ${#pods[@]} -eq 0 ]]; then + _kctf_log_err 'No pods found. Is the challenge running?' + return 1 + fi + + pod="${pods[0]}" + if [[ ${#pods[@]} -ne 1 ]]; then + _kctf_log "Found ${#pods[@]} pods, connecting to the most recent one." + _kctf_log "You can list the other pods with 'kubectl get pods'" + _kctf_log "and connect to them using 'kubectl exec pod/PODNAME --namespace ${CHALLENGE_NAMESPACE} -c ${CONTAINER} -it -- /bin/bash'" + + latestStartTime=$(date -d "$("${KCTF_BIN}/kubectl" get "pods/${pod}" -o jsonpath='{.status.startTime}')" '+%s') + for (( i=1; i < ${#pods[@]}; i++ )); do + otherPod="${pods[$i]}" + otherStartTime=$(date -d "$("${KCTF_BIN}/kubectl" get "pods/${otherPod}" -o jsonpath='{.status.startTime}')" '+%s') + if [[ -z "$(kubectl get "pod/${otherPod}" -o jsonpath="{.status.containerStatuses[?(@.name==\"${CONTAINER}\")].state.running}")" ]]; then + _kctf_log_warn "skipping pod/${otherPod} since the container \"${CONTAINER}\" is not running" + continue + fi + if [[ "${otherStartTime}" -gt "${latestStartTime}" ]]; then + latestStartTime="${otherStartTime}" + pod="${otherPod}" + fi + done + fi + + _kctf_log "Connecting to pod ${pod}" + "${KCTF_BIN}/kubectl" exec "pod/${pod}" --namespace "${CHALLENGE_NAMESPACE}" -c "${CONTAINER}" -it -- /bin/bash +} + +function kctf_chal_debug_port_forward_usage { + echo -e "usage: kctf chal debug port-forward [args]" >&2 + echo -e "args:" >&2 + echo -e " -h|--help print this help" >&2 + echo -e " --port: port in the challenge to connect to (default 1337)" >&2 + echo -e " --local-port: local port to listen on (defaults to random free port)" >&2 +} + +function kctf_chal_debug_port_forward { + REMOTE_PORT=1337 + LOCAL_PORT="" + + OPTS="h" + LONGOPTS="help,challenge-name:,port:,local-port:" + PARSED=$(getopt --options=$OPTS --longoptions=$LONGOPTS --name "kctf chal debug port-forward" -- "$@") + if [[ $? -ne 0 ]]; then + kctf_chal_debug_port_forward_usage + exit 1 + fi + eval set -- "$PARSED" + + while true; do + case "$1" in + -h|--help) + kctf_chal_debug_port_forward_usage + exit 0 + ;; + --port) + REMOTE_PORT="$2" + shift 2 + ;; + --local-port) + LOCAL_PORT="$2" + shift 2 + ;; + --) + shift + break + ;; + *) + _kctf_log_err "Unrecognized argument \"$1\"." + kctf_chal_debug_port_forward_usage + exit 1 + ;; + esac + done + + require_active_challenge + + _kctf_log 'starting port-forward, ctrl+c to exit' + "${KCTF_BIN}/kubectl" port-forward "deployment/${CHALLENGE_NAME}" --namespace "${CHALLENGE_NAMESPACE}" --address=127.0.0.1 "${LOCAL_PORT}:${REMOTE_PORT}" +} + +function kctf_chal_debug_docker { + COMMAND="debug docker" parse_container_name $@ || return + + build_image "${CONTAINER}" || return + + DOCKER_NAME="kctf-${KCTF_CTF_NAME}-${CHALLENGE_NAME}-${CONTAINER}" + + # kill any existing containers + docker kill "${DOCKER_NAME}" >/dev/null 2>/dev/null + docker container rm "${DOCKER_NAME}" >/dev/null 2>/dev/null + + _kctf_log "Running docker container ${IMAGE_ID} using name ${DOCKER_NAME}" + docker run -d --name "${DOCKER_NAME}" -it -p 1337 --privileged "${IMAGE_ID}" || return 1 + docker ps -f "name=${DOCKER_NAME}" || return 1 + _kctf_log "Container running, ctrl+c to exit" + docker attach "${DOCKER_NAME}" +} + + +function kctf_chal_debug_usage { + echo -e "usage: kctf chal debug command" >&2 + echo -e "available commands:" >&2 + echo -e " logs: print logs of the container" >&2 + echo -e " ssh: spawn an interactive bash in the container" >&2 + echo -e " port-forward: create a port-forward to the container's default port" >&2 + echo -e " docker: run the docker container locally" >&2 + echo -e "NOTE: you can use --container=healthcheck flag to debug the healthcheck" >&2 +} + +function kctf_chal_debug { + if [[ $# -lt 1 ]]; then + _kctf_log_err "unexpected argument count" + kctf_chal_debug_usage + exit 1 + fi + + case "$1" in + -h|--help) + kctf_chal_debug_usage + exit 0 + ;; + logs) + shift + kctf_chal_debug_logs $@ + ;; + ssh) + shift + kctf_chal_debug_ssh $@ + ;; + port-forward) + shift + kctf_chal_debug_port_forward $@ + ;; + docker) + shift + kctf_chal_debug_docker $@ + ;; + *) + _kctf_log_err "unknown command" + kctf_chal_debug_usage + exit 1 + ;; + esac +} + +function kctf_chal_create_usage { + echo "usage: kctf chal create [args] name" >&2 + echo "args:" >&2 + echo " -h|--help print this help" >&2 + echo " --template which template to use (run --template list to print available templates)" >&2 + echo " --challenge-dir path where to create the new challenge" >&2 + echo " default: \"${KCTF_CTF_DIR}/\${CHALLENGE_NAME}\"" >&2 +} + +function kctf_chal_create { + OPTS="h" + LONGOPTS="help,template:,challenge-dir:" + PARSED=$(getopt --options=$OPTS --longoptions=$LONGOPTS --name "kctf chal create" -- "$@") + if [[ $? -ne 0 ]]; then + kctf_chal_create_usage + exit 1 + fi + eval set -- "$PARSED" + + CHALLENGE_DIR= + TEMPLATE=pwn + while true; do + case "$1" in + -h|--help) + kctf_chal_create_usage + exit 0 + ;; + --template) + TEMPLATE="$2" + shift 2 + ;; + --challenge-dir) + CHALLENGE_DIR="$2" + shift 2 + ;; + --) + shift + break + ;; + *) + _kctf_log_err "Unrecognized argument \"$1\"." + parse_help_arg_only_usage + exit 1 + ;; + esac + done + + if [[ "${TEMPLATE}" == "list" ]]; then + echo "available templates:" + for template in ${KCTF_CTF_DIR}/kctf/challenge-templates/*; do + echo " $(basename ${template})" + done + exit 0 + fi + + if [[ $# -ne 1 ]]; then + _kctf_log_err "kctf chal create: name missing" + kctf_chal_create_usage + exit 1 + fi + + TEMPLATE_DIR="${KCTF_CTF_DIR}/kctf/challenge-templates/${TEMPLATE}" + if [[ ! -e "${TEMPLATE_DIR}/challenge.yaml" ]]; then + _kctf_log_err "kctf chal create: template \"${TEMPLATE}\" not found" + _kctf_log_err " run \"kctf chal create --template list\" to list available templates" + exit 1 + fi + + CHALLENGE_NAME="$1" + shift + + if [[ -z "${CHALLENGE_DIR}" ]]; then + CHALLENGE_DIR="${KCTF_CTF_DIR}/${CHALLENGE_NAME}" + else + CHALLENGE_DIR_REALPATH=$(realpath --canonicalize-missing "${CHALLENGE_DIR}") + if [[ "${CHALLENGE_DIR_REALPATH}" != "${KCTF_CTF_DIR}"/* ]]; then + _kctf_log_err "Challenge dir needs to be under the CTF dir:" + _kctf_log_err " \"${CHALLENGE_DIR_REALPATH}\"" + _kctf_log_err " not under" + _kctf_log_err " \"${KCTF_CTF_DIR}\"" + exit 1 + fi + fi + if [[ -e "${CHALLENGE_DIR}" ]]; then + _kctf_log_err "error: challenge dir \"${CHALLENGE_DIR}\" does already exist" + exit 1 + fi + + mkdir -p $(dirname "${CHALLENGE_DIR}") >/dev/null 2>/dev/null + + umask a+rx + cp -p -r "${TEMPLATE_DIR}" "${CHALLENGE_DIR}" + ${KCTF_BIN}/yq eval ".metadata.name = \"${CHALLENGE_NAME}\"" --inplace "${CHALLENGE_DIR}/challenge.yaml" +} + +function kctf_chal_list { + echo '== challenges in repository ==' + + for challenge_yaml in $(find "${KCTF_CTF_DIR}" -path "${KCTF_CTF_DIR}/kctf" -prune -false -o -name "challenge.yaml"); do + challenge_name=$(${KCTF_BIN}/yq eval ".metadata.name" "${challenge_yaml}") + challenge_dir=$(realpath --relative-to "${KCTF_CTF_DIR}" $(dirname "${challenge_yaml}")) + if [[ "${challenge_name}" == ${challenge_dir} ]]; then + echo "${challenge_name}" + else + echo "${challenge_name} (dir: ${challenge_dir})" + fi + done + + if has_cluster_config; then + echo '== deployed challenges ==' + "${KCTF_BIN}/kubectl" get challenges + fi +} + +function kctf_chal_usage { + echo -e "usage: kctf chal command" >&2 + echo -e "available commands:" >&2 + echo -e " create: create a new challenge from a template" >&2 + echo -e " list: list existing challenges" >&2 + echo -e " start: deploy the challenge to the cluster" >&2 + echo -e " stop: delete the challenge from the cluster" >&2 + echo -e " status: print the current status of the challenge" >&2 + echo -e " debug: commands for debugging the challenge" >&2 +} + +if [[ $# -lt 1 ]]; then + _kctf_log_err "unexpected argument count" + kctf_chal_usage + exit 1 +fi + +case "$1" in + -h|--help) + kctf_chal_usage + exit 0 + ;; + create) + shift + kctf_chal_create $@ + ;; + list) + shift + kctf_chal_list $@ + ;; + start) + shift + kctf_chal_start $@ + ;; + stop) + shift + kctf_chal_stop $@ + ;; + status) + shift + kctf_chal_status $@ + ;; + debug) + shift + kctf_chal_debug $@ + ;; + *) + _kctf_log_err "unknown command" + kctf_chal_usage + exit 1 + ;; +esac + diff --git a/vrp/kctf/bin/kctf-cluster b/vrp/kctf/bin/kctf-cluster new file mode 100755 index 000000000..63f3a3f77 --- /dev/null +++ b/vrp/kctf/bin/kctf-cluster @@ -0,0 +1,810 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +source "${KCTF_BIN}/kctf-log" + +BYE_MSG="" +KCTF_CLOUD_BASE_URL="https://kctf-cloud.appspot.com/v1" +# owned by kctf-cloud +KCTF_CLOUD_API_KEY="AIzaSyC7Jgu4e0IygmImZNPmJHrcfZ3lJA9ZrZs" + +function update_gcloud_config { + _kctf_log "Updating gcloud config." + ACTIVE_ACCOUNT="$(CLOUDSDK_ACTIVE_CONFIG_NAME= gcloud config get-value core/account 2>/dev/null)" + export CLOUDSDK_ACTIVE_CONFIG_NAME="kctf-${KCTF_SESSION}" + if ! gcloud config configurations describe "${CLOUDSDK_ACTIVE_CONFIG_NAME}" >/dev/null 2>/dev/null; then + gcloud config configurations create --no-activate "${CLOUDSDK_ACTIVE_CONFIG_NAME}" >/dev/null 2>/dev/null || return + fi + gcloud config set core/account "${ACTIVE_ACCOUNT}" >/dev/null 2>/dev/null || return + gcloud config set core/project "${PROJECT}" >/dev/null 2>/dev/null || return + gcloud config set compute/zone "${ZONE}" >/dev/null 2>/dev/null || return + gcloud config set container/cluster "${CLUSTER_NAME}" >/dev/null 2>/dev/null || return +} + +function set_lastconfig_link { + ln -sf "${CONFIG_NAME}" "${KCTF_CTF_DIR}/kctf/config/.lastconfig" +} + +function kctf_cluster_load_usage { + echo "usage: kctf cluster load config_name" >&2 + echo " run \"kctf cluster list\" to see a list of options" >&2 +} + +function kctf_cluster_load { + if [[ $# -ne 1 ]]; then + _kctf_log_err "missing config name" + kctf_cluster_load_usage + return 1 + fi + + if [[ "$1" == "-h" ]] || [[ "$1" == "--help" ]]; then + kctf_cluster_load_usage + return 0 + fi + + CONFIG_NAME="$1" + if [[ "${CONFIG_NAME}" == ".lastconfig" ]]; then + CONFIG_NAME=$(readlink "${KCTF_CTF_DIR}/kctf/config/.lastconfig") + if [[ $? -ne 0 ]]; then + _kctf_log_err "could not resolve .lastconfig link" + return 1 + fi + fi + CONFIG_PATH="${KCTF_CTF_DIR}/kctf/config/${CONFIG_NAME}" + shift + + source "${CONFIG_PATH}" || return + + case "${CLUSTER_TYPE}" in + gce) + if ! command -v gcloud >/dev/null 2>&1; then + _kctf_log_err "gcloud not installed" + return 1 + fi + update_gcloud_config || return + + # try to fetch the creds of the k8s cluster + gcloud container clusters get-credentials "${CLUSTER_NAME}" >/dev/null 2>/dev/null + ;; + kind) + kube_config=$("${KCTF_BIN}/kind" get kubeconfig --name "${CLUSTER_NAME}" 2>/dev/null) + if [[ $? -eq 0 ]]; then + echo "${kube_config}" > "${KUBECONFIG}" + fi + ;; + *) + _kctf_log_err "unknown cluster type \"${CLUSTER_TYPE}\"" + return 1 + ;; + esac + + _kctf_log "loaded config" + + set_lastconfig_link + + echo "${CONFIG_NAME}" +} + +function kctf_cluster_list { + echo "== available cluster configurations ==" + for file in ${KCTF_CTF_DIR}/kctf/config/*; do + CONFIG_NAME="$(basename $file)" + if [[ "${CONFIG_NAME}" = ".lastconfig" ]]; then + continue + fi + echo "${CONFIG_NAME}" + done +} + +function kctf_cluster_create_usage { + echo "usage: kctf cluster create [args] config_name" >&2 + echo " -h|--help print this help" >&2 + echo " --type what kind of cluster to create (default: gce)" >&2 + echo " supported values: \"gce\" (remote cluster) and \"kind\" (local cluster)" >&2 + echo " --project Required (gce): Google Cloud Platform project name" >&2 + echo " --zone GCP Zone (default: europe-west4-b)" >&2 + echo " For a list of zones run:" >&2 + echo " gcloud compute machine-types list --filter=\"name=( n2-standard-4 )\" --format 'value(zone)'" >&2 + echo " --registry Container Registry (default: eu.gcr.io)" >&2 + echo " Possible values are us.gcr.io, asia.gcr.io, and eu.gcr.io" >&2 + echo " --cluster-name Name of the kubernetes cluster (default: kctf-cluster)" >&2 + echo " --domain-name Required (gce): domain name to host challenges under" >&2 + echo " Please make sure not to put anything secret in the challenge name." >&2 + echo " Supported options:" >&2 + echo " \"none\": disable DNS support (might break some functionality)" >&2 + echo " \"your.domain.com\": use your own domain. You will have to follow some" >&2 + echo " additional steps to configure your nameserver." >&2 + echo " \"yourname.kctf.cloud\": automatically get a subdomain under kctf.cloud" >&2 + echo " --email-address Optional email address for LetsEncrypt registration (for wildcard certificates)" >&2 + echo " To use it, please read and agree to the ACME Subscriber Agreement:" >&2 + echo " https://letsencrypt.org/repository/" >&2 + echo " --start Start the cluster if it's not running yet" >&2 +} + +function kctf_cluster_create { + # Default Configuration + CLUSTER_TYPE="gce" + REGISTRY="eu.gcr.io" + PROJECT="" + ZONE="europe-west4-b" + CLUSTER_NAME="kctf-cluster" + DOMAIN_NAME="" + EMAIL_ADDRESS="" + START_CLUSTER="0" + + OPTS="h" + LONGOPTS="help,type:,project:,zone:,registry:,cluster-name:,domain-name:,email-address:,start" + PARSED=$(getopt --options=$OPTS --longoptions=$LONGOPTS --name "kctf cluster create" -- "$@") + if [[ $? -ne 0 ]]; then + kctf_cluster_create_usage + return 1 + fi + eval set -- "$PARSED" + + while true; do + case "$1" in + -h|--help) + kctf_cluster_create_usage + return 1 + ;; + --type) + CLUSTER_TYPE=$2 + shift 2 + ;; + --project) + PROJECT=$2 + shift 2 + ;; + --zone) + ZONE=$2 + shift 2 + ;; + --registry) + REGISTRY=$2 + shift 2 + ;; + --cluster-name) + CLUSTER_NAME=$2 + shift 2 + ;; + --domain-name) + DOMAIN_NAME="$2" + shift 2 + ;; + --email-address) + EMAIL_ADDRESS="$2" + shift 2 + ;; + --start) + START_CLUSTER="1" + shift + ;; + --) + shift + break + ;; + *) + _kctf_log_err "Unrecognized argument \"$1\"." + kctf_cluster_create_usage + return 1 + ;; + esac + done + + if [[ $# -ne 1 ]]; then + _kctf_log_err "kctf cluster create: cluster config name missing" + kctf_cluster_create_usage + return 1 + fi + + CONFIG_NAME="$1" + if [[ "${CONFIG_NAME}" == ".lastconfig" ]]; then + CONFIG_NAME=$(readlink "${KCTF_CTF_DIR}/kctf/config/.lastconfig") + if [[ $? -ne 0 ]]; then + _kctf_log_err "could not resolve .lastconfig link" + return 1 + fi + fi + CONFIG_PATH="${KCTF_CTF_DIR}/kctf/config/${CONFIG_NAME}" + shift + + case "${CLUSTER_TYPE}" in + gce) + if [[ -z "$PROJECT" ]]; then + _kctf_log_err "Missing required argument \"--project\"." + kctf_cluster_create_usage + return 1 + fi + if [[ -z "${DOMAIN_NAME}" ]]; then + _kctf_log_err "Missing required argument \"--domain-name\"." + kctf_cluster_create_usage + return 1 + fi + ;; + kind) + ;; + *) + _kctf_log_err "unknown cluster type \"${CLUSTER_TYPE}\"" + return 1 + ;; + esac + + if [[ "${DOMAIN_NAME}" == "none" ]]; then + DOMAIN_NAME="" + fi + + + mkdir -p "${KCTF_CTF_DIR}/kctf/config" || return + + if [ -e "${CONFIG_PATH}" ]; then + _kctf_log_warn "Overwriting existing cluster config file. Old content:" + cat "${CONFIG_PATH}" >&2 + rm "${CONFIG_PATH}" >&2 + fi + + cat > "${CONFIG_PATH}" << EOF +CLUSTER_TYPE=${CLUSTER_TYPE} +PROJECT=${PROJECT} +ZONE=${ZONE} +REGISTRY=${REGISTRY} +CLUSTER_NAME=${CLUSTER_NAME} +DOMAIN_NAME=${DOMAIN_NAME} +EMAIL_ADDRESS=${EMAIL_ADDRESS} +EOF + if [[ $? -ne 0 ]]; then return 1; fi + + set_lastconfig_link || return + + case "${CLUSTER_TYPE}" in + gce) + if ! command -v gcloud >/dev/null 2>&1; then + if [[ "${START_CLUSTER}" == "1" ]]; then + _kctf_log_err "Can't start cluster, configuration created only locally. Gcloud not installed." + return 1 + else + _kctf_log_warn "Configuration created only locally. Gcloud not installed." + return 0 + fi + fi + update_gcloud_config || return + # try to fetch the creds of the k8s cluster + gcloud container clusters get-credentials "${CLUSTER_NAME}" >/dev/null 2>/dev/null + GET_CLUSTER_CREDS_RESULT=$? + ;; + kind) + kube_config=$("${KCTF_BIN}/kind" get kubeconfig --name "${CLUSTER_NAME}" 2>/dev/null) + GET_CLUSTER_CREDS_RESULT=$? + if [[ "${GET_CLUSTER_CREDS_RESULT}" -eq 0 ]]; then + echo "${kube_config}" > "${KUBECONFIG}" + fi + ;; + *) + _kctf_log_err "unknown cluster type \"${CLUSTER_TYPE}\"" + return 1 + ;; + esac + + # there might be an existing cluster + # if it already exists, we try to update it + # otherwise, start it if requested + if [[ "${START_CLUSTER}" == "1" ]]; then + if [[ ${GET_CLUSTER_CREDS_RESULT} -eq 0 ]]; then + _kctf_log "Existing cluster found, updating cluster." + else + _kctf_log "Starting cluster." + fi + export CLUSTER_TYPE + export PROJECT + export ZONE + export REGISTRY + export CLUSTER_NAME + export DOMAIN_NAME + export EMAIL_ADDRESS + "${KCTF_BIN}/kctf-cluster" start >&2 || return + elif [[ ${GET_CLUSTER_CREDS_RESULT} -eq 0 ]]; then + _kctf_log_warn "Existing cluster found. If it's running an old version of kCTF, remember to upgrade it with cluster start." + fi + + echo "${CONFIG_NAME}" +} + +function create_operator { + # Creating CRD, rbac and operator + "${KCTF_BIN}/kubectl" apply -f "${KCTF_CTF_DIR}/kctf/resources/kctf.dev_challenges_crd.yaml" || return + "${KCTF_BIN}/kubectl" apply -f "${KCTF_CTF_DIR}/kctf/resources/rbac.yaml" || return + "${KCTF_BIN}/kubectl" apply -f "${KCTF_CTF_DIR}/kctf/resources/operator.yaml" || return + + OPERATOR_IMAGE=$("${KCTF_BIN}/yq" eval '.spec.template.spec.containers[0].image' "${KCTF_CTF_DIR}/kctf/resources/operator.yaml") + if [[ $? -ne 0 ]]; then + echo "Failed to find the operator image." >&2 + return 1 + fi + + # The operator needs to create some subresources, e.g. the gcsfuse service account + for i in {1..100}; do + "${KCTF_BIN}/kubectl" get pods --namespace kctf-system -o=jsonpath="{.items[?(@.spec.containers[0].image==\"${OPERATOR_IMAGE}\")].status.containerStatuses[0].ready}" | grep "true" && break + if [ "$i" == "100" ]; then + _kctf_log_err "Couldn't find a kctf-operator pod with status ready=true and image=\"${OPERATOR_IMAGE}\" after 5 minutes" + "${KCTF_BIN}/kubectl" get pods --namespace kctf-system -o=yaml >&2 + exit 1 + fi + sleep 3 + done +} + +function wait_for_nameserver { + nameserver="$1" + initial_timeout=300 + timeout=$initial_timeout + sleep_time=10 + while [[ "${timeout}" -gt 0 ]]; do + if nslookup -nosearch -norecurse -type=NS "${DOMAIN_NAME}." "${nameserver}" >/dev/null 2>/dev/null; then + return 0 + fi + _kctf_log "nameserver didn't serve NS record yet, sleeping for ${sleep_time}s" + sleep ${sleep_time} + timeout=$(($timeout - $sleep_time)) + done + _kctf_log_err "nameserver didn't serve NS record after ${initial_timeout}s" + return 1 +} + +required_apis=("containerregistry.googleapis.com" "compute.googleapis.com" "container.googleapis.com" "dns.googleapis.com") + +function check_required_apis { + GCP_APIS="$(gcloud services list --format 'get(config.name)')" + for required_api in "${required_apis[@]}"; do + if [[ ! "${GCP_APIS}" =~ "${required_api}" ]]; then + _kctf_log_err "Required GCP API \"${required_api}\" is not enabled" + return 1 + fi + done + return 0 +} + +function kctf_cluster_start_gce { + MIN_NODES="1" + MAX_NODES="2" + NUM_NODES="1" + MACHINE_TYPE="n2-standard-4" + SUFFIX=$(echo "${PROJECT}-${CLUSTER_NAME}-${ZONE}" | sha1sum) + NETWORK="kctf-network-${SUFFIX:0:16}" + + if ! check_required_apis; then + _kctf_log_err "Please enable the required APIs by running 'gcloud services enable ${required_apis[@]}'" + return 1 + fi + + EXISTING_NETWORK=$(gcloud compute networks list --filter="name=${NETWORK}" --format 'get(name)') + if [ -z "${EXISTING_NETWORK}" ]; then + gcloud compute networks create ${NETWORK} --description "kCTF network for cluster ${CLUSTER_NAME}" >/dev/null || return + fi + + EXISTING_CLUSTER=$(gcloud container clusters list --filter "name=${CLUSTER_NAME}" --format 'get(name)') + if [ -z "${EXISTING_CLUSTER}" ]; then + CIDR="172.16.0.32/28" + gcloud container clusters create --release-channel=regular --enable-network-policy --enable-autoscaling --min-nodes ${MIN_NODES} --max-nodes ${MAX_NODES} --num-nodes ${NUM_NODES} --network ${NETWORK} --create-subnetwork name=kctf-subnet-${NETWORK} --no-enable-master-authorized-networks --enable-ip-alias --enable-private-nodes --master-ipv4-cidr ${CIDR} --enable-autorepair --preemptible --machine-type ${MACHINE_TYPE} --workload-pool=${PROJECT}.svc.id.goog ${CLUSTER_NAME} || return + fi + + EXISTING_ROUTER=$(gcloud compute routers list --filter "name=kctf-${CLUSTER_NAME}-nat-router" --format 'get(name)') + if [ -z "${EXISTING_ROUTER}" ]; then + gcloud compute routers create "kctf-${CLUSTER_NAME}-nat-router" --network="${NETWORK}" --region "${ZONE::-2}" || return + fi + + EXISTING_NAT=$(gcloud compute routers nats list --router "kctf-${CLUSTER_NAME}-nat-router" --router-region "${ZONE::-2}" --format 'get(name)') + if [ -z "${EXISTING_NAT}" ]; then + gcloud compute routers nats create "kctf-${CLUSTER_NAME}-nat-config" --router-region "${ZONE::-2}" --router kctf-${CLUSTER_NAME}-nat-router --nat-all-subnet-ip-ranges --auto-allocate-nat-external-ips || return + fi + + "${KCTF_BIN}/kubectl" create namespace "kctf-system" --dry-run=client -oyaml | "${KCTF_BIN}/kubectl" apply -f - >&2 || return + + # GCSFUSE + + BUCKET_NAME="kctf-gcsfuse-${SUFFIX:0:16}" + GCS_GSA_NAME="${BUCKET_NAME}" + GCS_GSA_EMAIL=$(gcloud iam service-accounts list --filter "email=${GCS_GSA_NAME}@${PROJECT}.iam.gserviceaccount.com" --format 'get(email)' || true) + if [ -z "${GCS_GSA_EMAIL}" ]; then + gcloud iam service-accounts create "${GCS_GSA_NAME}" --description "kCTF GCSFUSE service account ${CLUSTER_NAME} ${ZONE}" --display-name "kCTF GCSFUSE ${CLUSTER_NAME} ${ZONE}" || return + GCS_GSA_EMAIL=$(gcloud iam service-accounts list --filter "email=${GCS_GSA_NAME}@${PROJECT}.iam.gserviceaccount.com" --format 'get(email)') + while [ -z "${GCS_GSA_EMAIL}" ]; do + sleep 1 + GCS_GSA_EMAIL=$(gcloud iam service-accounts list --filter "email=${GCS_GSA_NAME}@${PROJECT}.iam.gserviceaccount.com" --format 'get(email)') + done + fi + + GCS_KSA_NAME="gcsfuse-sa" + + gcloud iam service-accounts add-iam-policy-binding --role roles/iam.workloadIdentityUser --member "serviceAccount:${PROJECT}.svc.id.goog[kctf-system/${GCS_KSA_NAME}]" ${GCS_GSA_EMAIL} || return + "${KCTF_BIN}/kubectl" create serviceaccount --namespace kctf-system ${GCS_KSA_NAME} --save-config --dry-run=client -o yaml | "${KCTF_BIN}/kubectl" apply -f - || return + "${KCTF_BIN}/kubectl" annotate serviceaccount --namespace kctf-system ${GCS_KSA_NAME} iam.gke.io/gcp-service-account=${GCS_GSA_EMAIL} --overwrite || return + + if ! gsutil du "gs://${BUCKET_NAME}/"; then + gsutil mb -l eu "gs://${BUCKET_NAME}/" || return + fi + + if gsutil uniformbucketlevelaccess get "gs://${BUCKET_NAME}" | grep -q "Enabled: True"; then + gsutil iam ch "serviceAccount:${GCS_GSA_EMAIL}:roles/storage.legacyBucketOwner" "gs://${BUCKET_NAME}" || return + gsutil iam ch "serviceAccount:${GCS_GSA_EMAIL}:roles/storage.legacyObjectOwner" "gs://${BUCKET_NAME}" || return + else + gsutil acl ch -u "${GCS_GSA_EMAIL}:O" "gs://${BUCKET_NAME}" || return + fi + + "${KCTF_BIN}/kubectl" create configmap gcsfuse-config --from-literal=gcs_bucket="${BUCKET_NAME}" --namespace kctf-system --dry-run=client -o yaml | "${KCTF_BIN}/kubectl" apply -f - || return + + "${KCTF_BIN}/kubectl" patch ServiceAccount default --patch "automountServiceAccountToken: false" || return + + # Cloud DNS + + if [ ! -z "${DOMAIN_NAME}" ]; then + ZONE_NAME="$(echo ${DOMAIN_NAME} | sed 's/[.]/--/g')-dns-zone" + + DNS_ZONE=$(gcloud dns managed-zones list --filter "name:${ZONE_NAME}" --format 'get(name)') + if [ -z "${DNS_ZONE}" ]; then + _kctf_log "creating new managed-zone \"${ZONE_NAME}\"" + gcloud dns managed-zones create "${ZONE_NAME}" --description "DNS Zone for ${DOMAIN_NAME}" --dns-name="${DOMAIN_NAME}." || return + soa_ttl="$(gcloud dns record-sets list --zone=${ZONE_NAME} --type=SOA --name="${DOMAIN_NAME}." --format='get(ttl)')" + if [[ $? -ne 0 ]]; then return 1; fi + soa_data="$(gcloud dns record-sets list --zone=${ZONE_NAME} --type=SOA --name="${DOMAIN_NAME}." --format='get(DATA)')" + if [[ $? -ne 0 ]]; then return 1; fi + new_soa=($soa_data) + # update the serial no + new_soa[2]=$((${new_soa[2]} + 1)) + # change the ttl + new_soa[6]="60" + + _kctf_log "changing the SOA entry to reduce TTL" + gcloud dns record-sets transaction start --zone="${ZONE_NAME}" || return + gcloud dns record-sets transaction remove --zone="${ZONE_NAME}" --name "${DOMAIN_NAME}." --ttl "${soa_ttl}" --type "SOA" "${soa_data}" || return + gcloud dns record-sets transaction add --zone="${ZONE_NAME}" --name "${DOMAIN_NAME}." --ttl "60" --type "SOA" "${new_soa[*]}" || return + gcloud dns record-sets transaction describe --zone="${ZONE_NAME}" || return + if ! gcloud dns record-sets transaction execute --zone="${ZONE_NAME}"; then + gcloud dns record-sets transaction abort --zone="${ZONE_NAME}" || return + _kctf_log_err 'updating the SOA entry failed' + exit 1 + fi + _kctf_log "SOA updated" + else + _kctf_log "managed-zone \"${ZONE_NAME}\" exists, reusing" + fi + + DNS_ZONE_NAMESERVERS=$(gcloud dns managed-zones describe "${ZONE_NAME}" --format 'value[delimiter="\n"](nameServers)') + if [[ "${DOMAIN_NAME}" == *".kctf.cloud" ]]; then + _kctf_log "waiting for nameservers to be updated (should take roughly 1m)" + for nameserver in ${DNS_ZONE_NAMESERVERS}; do + wait_for_nameserver "${nameserver}" || return + done + KCTF_CLOUD_URL="${KCTF_CLOUD_BASE_URL}/subdomain?name=${DOMAIN_NAME%.kctf.cloud}&nameservers=$(paste -sd ',' <(echo "${DNS_ZONE_NAMESERVERS}"))" + _kctf_log 'requesting kctf.cloud subdomain' + kctf_cloud_tries=3 + kctf_cloud_timeout=10 + while true; do + curl --fail -X POST -H "x-api-key: ${KCTF_CLOUD_API_KEY}" "${KCTF_CLOUD_URL}" >/dev/null && break + kctf_cloud_tries=$(($kctf_cloud_tries - 1)) + if [[ $kctf_cloud_tries -le 0 ]]; then + _kctf_log_err 'could not register kctf.cloud subdomain' + exit 1 + fi + _kctf_log_warn "registering kctf.cloud subdomain failed, retrying in ${kctf_cloud_timeout}s" + sleep "${kctf_cloud_timeout}" + done + else + # print in red for attention + _kctf_log $'\001\e[0;31m\002'"ATTENTION: "$'\001\e[0m\002'"You need to add the following NS entries for your domain \"${DOMAIN_NAME}\":"$'\n'"${DNS_ZONE_NAMESERVERS}" + BYE_MSG=$'\001\e[0;31m\002'"ATTENTION: "$'\001\e[0m\002'"You need to add the following NS entries for your domain \"${DOMAIN_NAME}\":"$'\n'"${DNS_ZONE_NAMESERVERS}" + fi + + DNS_GSA_NAME="kctf-cloud-dns" + DNS_GSA_EMAIL=$(gcloud iam service-accounts list --filter "email=${DNS_GSA_NAME}@${PROJECT}.iam.gserviceaccount.com" --format 'get(email)' || true) + + if [ -z "${DNS_GSA_EMAIL}" ]; then + gcloud iam service-accounts create "${DNS_GSA_NAME}" --description "kCTF Cloud DNS service account ${CLUSTER_NAME} ${ZONE}" --display-name "kCTF Cloud DNS ${CLUSTER_NAME} ${ZONE}" || return + DNS_GSA_EMAIL=$(gcloud iam service-accounts list --filter "email=${DNS_GSA_NAME}@${PROJECT}.iam.gserviceaccount.com" --format 'get(email)') + while [ -z "${DNS_GSA_EMAIL}" ]; do + sleep 1 + DNS_GSA_EMAIL=$(gcloud iam service-accounts list --filter "email=${DNS_GSA_NAME}@${PROJECT}.iam.gserviceaccount.com" --format 'get(email)') + done + fi + + DNS_KSA_NAME="external-dns-sa" + + gcloud iam service-accounts add-iam-policy-binding --role roles/iam.workloadIdentityUser --member "serviceAccount:${PROJECT}.svc.id.goog[kctf-system/${DNS_KSA_NAME}]" ${DNS_GSA_EMAIL} || return + "${KCTF_BIN}/kubectl" create serviceaccount --namespace kctf-system ${DNS_KSA_NAME} --save-config --dry-run=client -o yaml | "${KCTF_BIN}/kubectl" apply -f - || return + "${KCTF_BIN}/kubectl" annotate serviceaccount --namespace kctf-system ${DNS_KSA_NAME} iam.gke.io/gcp-service-account=${DNS_GSA_EMAIL} --overwrite || return + + gcloud projects add-iam-policy-binding ${PROJECT} --member=serviceAccount:${DNS_GSA_EMAIL} --role=roles/dns.admin || return + + "${KCTF_BIN}/kubectl" create configmap --namespace kctf-system external-dns --from-literal=DOMAIN_NAME=${DOMAIN_NAME} --from-literal=EMAIL_ADDRESS=${EMAIL_ADDRESS:-} --dry-run=client -o yaml | "${KCTF_BIN}/kubectl" apply -f - || return + fi + + create_operator || return +} + +function kctf_cluster_start { + case "${CLUSTER_TYPE}" in + gce) + kctf_cluster_start_gce + return + ;; + kind) + kctf_cluster_start_kind + return + ;; + *) + _kctf_log_err "unknown cluster type \"${CLUSTER_TYPE}\"" + return 1 + ;; + esac +} + +function kctf_cluster_stop_gce { + _kctf_log "deleting all challenges so that load balancers etc can be cleaned up" + CHALLENGES=$("${KCTF_BIN}/kubectl" get challenge --all-namespaces -o=jsonpath='{range .items[*]}{@.metadata.namespace}{"/"}{@.metadata.name}{" "}{end}') + if [[ ! -z "${CHALLENGES}" ]]; then + for chal_and_ns in ${CHALLENGES}; do + IFS='/' read -r -a chal_and_ns_array <<< "$chal_and_ns" + chal_namespace="${chal_and_ns_array[0]}" + chal_name="${chal_and_ns_array[1]}" + "${KCTF_BIN}/kubectl" delete "challenge/${chal_name}" --namespace "${chal_namespace}" + done + fi + + # deleting the cluster below takes a while, so sleeping for a bit doesn't hurt + _kctf_log "Sleeping 20s to give time to delete resources" + sleep 20 + + CLOUDSDK_CORE_DISABLE_PROMPTS=1 gcloud container clusters delete ${CLUSTER_NAME} + gcloud compute routers delete "kctf-${CLUSTER_NAME}-nat-router" --region "${ZONE::-2}" --quiet + + SUFFIX=$(echo "${PROJECT}-${CLUSTER_NAME}-${ZONE}" | sha1sum) + + NETWORK="kctf-network-${SUFFIX:0:16}" + gcloud compute networks delete ${NETWORK} --quiet + + GSA_NAME="kctf-gcsfuse-${SUFFIX:0:16}" + GSA_EMAIL=$(gcloud iam service-accounts list --filter "email=${GSA_NAME}@${PROJECT}.iam.gserviceaccount.com" --format 'get(email)' || true) + if [ -z "${GSA_EMAIL}" ]; then + gcloud iam service-accounts delete "${GSA_EMAIL}" + fi +} + +function kctf_cluster_start_kind { + if ! "${KCTF_BIN}/kind" get kubeconfig --name "${CLUSTER_NAME}" >/dev/null 2>/dev/null; then + "${KCTF_BIN}/kind" create cluster --name "${CLUSTER_NAME}" || return + fi + + "${KCTF_BIN}/kubectl" create namespace "kctf-system" --dry-run=client -oyaml | "${KCTF_BIN}/kubectl" apply -f - >&2 || return + + create_operator + + "${KCTF_BIN}/kubectl" patch ServiceAccount default --patch "automountServiceAccountToken: false" || return +} + +function kctf_cluster_stop_kind { + "${KCTF_BIN}/kind" delete cluster --name "${CLUSTER_NAME}" || return +} + +function kctf_cluster_stop { + case "${CLUSTER_TYPE}" in + gce) + kctf_cluster_stop_gce || return + return + ;; + kind) + kctf_cluster_stop_kind || return + return + ;; + *) + _kctf_log_err "unknown cluster type \"${CLUSTER_TYPE}\"" + return 1 + ;; + esac +} + +function kctf_cluster_resize_usage { + echo -e "usage: kctf cluster resize [args]" >&2 + echo -e "args:" >&2 + echo -e " -h|--help print this help" >&2 + echo -e " --machine-type machine type to use" >&2 + echo -e " to list available types, run: gcloud compute machine-types list --zones=\"${ZONE}\"" >&2 + echo -e " --min-nodes (required) minimum number of nodes in the cluster" >&2 + echo -e " --max-nodes (required) maximum number of nodes in the cluster" >&2 + echo -e " --num-nodes (required) initial number of nodes in the cluster" >&2 + echo -e " --pool-name name of the node pool" >&2 + echo -e " --old-pool name of the old pool to replace" >&2 +} + +function kctf_cluster_resize { + if [[ "${CLUSTER_TYPE}" != "gce" ]]; then + _kctf_log_err "only cluster type \"gce\" is supported by resize" + return 1 + fi + + OPTS="h" + LONGOPTS="help,machine-type:,min-nodes:,max-nodes:,num-nodes:,pool-name:,old-pool" + PARSED=$(getopt --options=$OPTS --longoptions=$LONGOPTS --name "kctf chal create" -- "$@") + if [[ $? -ne 0 ]]; then + kctf_cluster_resize_usage + exit 1 + fi + eval set -- "$PARSED" + + MACHINE_TYPE="n2-standard-4" + MIN_NODES= + MAX_NODES= + NUM_NODES= + NEW_POOL_NAME= + OLD_POOL_NAME= + while true; do + case "$1" in + -h|--help) + kctf_cluster_resize_usage + exit 0 + ;; + --machine-type) + MACHINE_TYPE="$2" + shift 2 + ;; + --min-nodes) + MIN_NODES="$2" + shift 2 + ;; + --max-nodes) + MAX_NODES="$2" + shift 2 + ;; + --num-nodes) + NUM_NODES="$2" + shift 2 + ;; + --pool-name) + NEW_POOL_NAME="$2" + shift 2 + ;; + --old-pool) + OLD_POOL_NAME="$2" + shift 2 + ;; + --) + shift + break + ;; + *) + _kctf_log_err "Unrecognized argument \"$1\"." + kctf_cluster_resize_usage + exit 1 + ;; + esac + done + + if [[ -z "${MIN_NODES}" ]] || [[ -z "${MAX_NODES}" ]] || [[ -z "${NUM_NODES}" ]]; then + _kctf_log_err "Required arguments missing" + kctf_cluster_resize_usage + exit 1 + fi + + if [[ -z "${OLD_POOL_NAME}" ]]; then + OLD_POOL_NAME=$(gcloud container node-pools list --cluster ${CLUSTER_NAME} --format 'value(name)') + if [ $(echo "${OLD_POOL_NAME}" | wc -l) != "1" ]; then + _kctf_log_err 'Multiple node pools found. Please choose which to replace with --old-pool.' + echo '== node pools ==' >&2 + echo "${OLD_POOL_NAME}" >&2 + exit 1 + fi + fi + + if [[ -z "${NEW_POOL_NAME}" ]]; then + NEW_POOL_NAME="${OLD_POOL_NAME}-resized" + fi + + if [ "${OLD_POOL_NAME}" = "${NEW_POOL_NAME}" ]; then + _kctf_log_err "New pool can't have the same name as the old pool." + exit 1 + fi + + if [[ $# -ne 0 ]]; then + _kctf_log_err "Unrecognized arguments \"$@\"." + kctf_cluster_resize_usage + exit 1 + fi + + _kctf_log 'Creating the new node pool' + gcloud container node-pools create "${NEW_POOL_NAME}" \ + --cluster="${CLUSTER_NAME}" \ + --machine-type="${MACHINE_TYPE}" \ + --enable-autorepair \ + --enable-autoupgrade \ + --num-nodes="${NUM_NODES}" \ + --enable-autoscaling \ + --min-nodes="${MIN_NODES}" \ + --max-nodes="${MAX_NODES}" || return + + _kctf_log 'Cordoning old nodes' + for node in $("${KCTF_BIN}/kubectl" get nodes -l cloud.google.com/gke-nodepool="${OLD_POOL_NAME}" -o=name); do + "${KCTF_BIN}/kubectl" cordon "$node" || return + done + + _kctf_log 'Draining old nodes' + for node in $("${KCTF_BIN}/kubectl" get nodes -l cloud.google.com/gke-nodepool="${OLD_POOL_NAME}" -o=name); do + "${KCTF_BIN}/kubectl" drain --force --ignore-daemonsets --delete-local-data --grace-period=10 "$node" || return + done + + _kctf_log "Deleting old node pool \"${OLD_POOL_NAME}\"" + gcloud container node-pools delete "${OLD_POOL_NAME}" --cluster "${CLUSTER_NAME}" || return +} + +function kctf_cluster_usage { + echo -e "usage: kctf cluster command" >&2 + echo -e "available commands:" >&2 + echo -e " create: create a new cluster config" >&2 + echo -e " list: list available cluster configs" >&2 + echo -e " load: load an existing cluster config" >&2 + echo -e " start: start the cluster" >&2 + echo -e " stop: stop the cluster" >&2 + echo -e " resize: resize the cluster" >&2 +} + +function check_cluster_name { + if [[ -z "${CLUSTER_NAME-}" ]]; then + _kctf_log_err "No cluster config loaded. You need to run \"kctf cluster create\" or \"kctf cluster load\" first." + exit 1 + fi +} + +if [[ $# -lt 1 ]]; then + _kctf_log_err "unexpected argument count" + kctf_cluster_usage + exit 1 +fi + +case "$1" in + -h|--help) + kctf_cluster_usage + exit 0 + ;; + start) + shift + check_cluster_name + kctf_cluster_start $@ + # This is used for printing DNS settings once again at the end + echo $BYE_MSG + ;; + stop) + shift + check_cluster_name + kctf_cluster_stop $@ + ;; + resize) + shift + check_cluster_name + kctf_cluster_resize $@ + ;; + create) + shift + kctf_cluster_create $@ + ;; + list) + shift + kctf_cluster_list $@ + ;; + load) + shift + kctf_cluster_load $@ + ;; + *) + _kctf_log_err "unknown command" + kctf_cluster_usage + exit 1 + ;; +esac diff --git a/vrp/kctf/bin/kctf-completion b/vrp/kctf/bin/kctf-completion new file mode 100644 index 000000000..32b97c7d4 --- /dev/null +++ b/vrp/kctf/bin/kctf-completion @@ -0,0 +1,193 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +if [[ -n "${ZSH_VERSION:-}" ]]; then + autoload -U bashcompinit + bashcompinit +fi + +function _kctf_complete_chal_debug() { + if [ "$COMP_CWORD" == "3" ]; then + COMPREPLY=($(compgen -W "--help logs ssh port-forward docker" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + + case "${COMP_WORDS[3]}" in + logs|ssh|docker) + if [ "${PREV_IS_FLAG}" = 1 ]; then + if [ "${PREV_WORD}" = "--container" ]; then + # TODO get containers from challenge.yaml + COMPREPLY=($(compgen -W "challenge healthcheck" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + return 0 + fi + if [ "${COMP_WORDS[3]}" = "logs" ]; then + COMPREPLY=($(compgen -W "--help --container --tail" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + COMPREPLY=($(compgen -W "--help --container" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + ;; + port-forward) + if [ "${PREV_IS_FLAG}" = 1 ]; then + return + fi + COMPREPLY=($(compgen -W "--help --port --local-port" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + ;; + *) + return 1 + ;; + esac +} + +function _kctf_complete_chal() { + if [ "$COMP_CWORD" == "2" ]; then + COMPREPLY=($(compgen -W "--help create list start stop status debug" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + + case "${COMP_WORDS[2]}" in + list) + return + ;; + create) + if [ "${PREV_IS_FLAG}" = 1 ]; then + if [ "${PREV_WORD}" == "--challenge-dir" ]; then + COMPREPLY=($(compgen -d -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + if [ "${PREV_WORD}" == "--template" ]; then + COMPREPLY=($(compgen -W "list $(ls ${KCTF_CTF_DIR}/kctf/challenge-templates/)" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + return 0 + fi + COMPREPLY=($(compgen -W "--help --template --challenge-dir" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + ;; + start|stop|status) + COMPREPLY=($(compgen -W "--help" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + ;; + debug) + _kctf_complete_chal_debug + return + ;; + *) + return 1 + ;; + esac +} + +# This will be out of date at some point, but still useful so worth it? +# We can't fetch it easily with gcloud unfortunately since it requires a project +GCP_REGIONS="us-east1-b us-east1-c us-east1-d us-east4-c us-east4-b us-east4-a us-central1-c us-central1-a us-central1-f us-central1-b us-west1-b us-west1-c us-west1-a europe-west4-a europe-west4-b europe-west4-c europe-west1-b europe-west1-d europe-west1-c europe-west3-c europe-west3-a europe-west3-b europe-west2-c europe-west2-b europe-west2-a asia-east1-b asia-east1-a asia-east1-c asia-southeast1-b asia-southeast1-a asia-southeast1-c asia-northeast1-b asia-northeast1-c asia-northeast1-a asia-south1-c asia-south1-b asia-south1-a australia-southeast1-b australia-southeast1-c australia-southeast1-a southamerica-east1-b southamerica-east1-c southamerica-east1-a asia-east2-a asia-east2-b asia-east2-c asia-northeast2-a asia-northeast2-b asia-northeast2-c asia-northeast3-a asia-northeast3-b asia-northeast3-c asia-southeast2-a asia-southeast2-b asia-southeast2-c europe-north1-a europe-north1-b europe-north1-c europe-west6-a europe-west6-b europe-west6-c northamerica-northeast1-a northamerica-northeast1-b northamerica-northeast1-c us-west2-a us-west2-b us-west2-c us-west3-a us-west3-b us-west3-c us-west4-a us-west4-b us-west4-c" +GCP_REGISTRIES="gcr.io asia.gcr.io eu.gcr.io us.gcr.io" + +function _kctf_complete_cluster() { + if [ "$COMP_CWORD" == "2" ]; then + COMPREPLY=($(compgen -W "--help start stop resize create load list" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + + case "${COMP_WORDS[2]}" in + start|stop) + return 0 + ;; + resize) + if [ "${PREV_IS_FLAG}" = 1 ]; then + if [ "${PREV_WORD}" == "--old-pool" ]; then + COMPREPLY=($(compgen -W "$(gcloud container node-pools list --cluster ${CLUSTER_NAME} --format 'get(name)')" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + if [ "${PREV_WORD}" == "--machine-type" ]; then + COMPREPLY=($(compgen -W "$(gcloud compute machine-types list --zones="${ZONE}" --format 'get(name)')" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + return 0 + fi + COMPREPLY=($(compgen -W "--machine-type --min-nodes --max-nodes --num-nodes --pool-name --old-pool" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + ;; + create) + if [ "${PREV_IS_FLAG}" = 1 ] && [ "${PREV_WORD}" != "--start" ]; then + if [ "${PREV_WORD}" == "--type" ]; then + COMPREPLY=($(compgen -W "gce kind" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + if [ "${PREV_WORD}" == "--project" ]; then + COMPREPLY=($(compgen -W "$(gcloud projects list --format 'get(project_id)')" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + if [ "${PREV_WORD}" == "--zone" ]; then + COMPREPLY=($(compgen -W "${GCP_REGIONS}" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + if [ "${PREV_WORD}" == "--registry" ]; then + COMPREPLY=($(compgen -W "${GCP_REGISTRIES}" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + if [ "${PREV_WORD}" == "--domain-name" ]; then + COMPREPLY=($(compgen -W "none" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + return 0 + fi + COMPREPLY=($(compgen -W "--help --type --project --zone --registry --cluster-name --domain-name --start" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + ;; + load) + if [ "${COMP_CWORD}" == "3" ]; then + COMPREPLY=($(compgen -W "$(find ${KCTF_CTF_DIR}/kctf/config/ -type f -printf '%f\n')" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + return 0 + ;; + list) + return 0 + ;; + *) + return 1 + ;; + esac +} + +function _kctf_complete() { + if [ "$COMP_CWORD" == "1" ]; then + COMPREPLY=($(compgen -W "--help chal cluster" -- "${COMP_WORDS[${COMP_CWORD}]}")) + return + fi + PREV_WORD="${COMP_WORDS[$(($COMP_CWORD - 1))]}" + if [ "${PREV_WORD:0:1}" = "-" ]; then + PREV_IS_FLAG="1" + else + PREV_IS_FLAG="0" + fi + case "${COMP_WORDS[1]}" in + chal) + _kctf_complete_chal + return + ;; + cluster) + _kctf_complete_cluster + return + ;; + *) + return 1 + ;; + esac +} +complete -F _kctf_complete kctf diff --git a/vrp/kctf/bin/kctf-log b/vrp/kctf/bin/kctf-log new file mode 100644 index 000000000..94929248c --- /dev/null +++ b/vrp/kctf/bin/kctf-log @@ -0,0 +1,35 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +_KCTF_COLOR_RED=$'\e[0;31m' +_KCTF_COLOR_GREEN=$'\e[0;32m' +_KCTF_COLOR_YELLOW=$'\e[0;33m' +_KCTF_COLOR_END=$'\e[0m' + +function _kctf_log { + echo -n "${_KCTF_COLOR_GREEN}[*]${_KCTF_COLOR_END} " >&2 + echo "$@" >&2 +} + +function _kctf_log_warn { + echo -n "${_KCTF_COLOR_YELLOW}[W]${_KCTF_COLOR_END} " >&2 + echo "$@" >&2 +} + +function _kctf_log_err { + echo -n "${_KCTF_COLOR_RED}[E]${_KCTF_COLOR_END} " >&2 + echo "$@" >&2 +} + diff --git a/vrp/kctf/challenge-templates/pwn/README.md b/vrp/kctf/challenge-templates/pwn/README.md new file mode 100644 index 000000000..44415f63a --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/README.md @@ -0,0 +1,55 @@ +# Quickstart guide to writing a challenge + +The basic steps when preparing a challenge are: + +* A Docker image is built from the `challenge` directory. For the simplest challenges, replacing `challenge/chal.c` is sufficient. +* Edit `challenge/Dockerfile` to change the commandline or the files you want to include. +* To try the challenge locally, you will need to + * create a a local cluster with `kctf cluster create --type kind --start $configname` + * build the challenge binary with `make -C challenge` + * and then deploy the challenge with `kctf chal start` +* To access the challenge, create a port forward with `kctf chal debug port-forward` and connect to it via `nc localhost PORT` using the printed port. +* Check out `kctf chal ` for more commands. + +## Directory layout + +The following files/directories are available: + +### /challenge.yaml + +`challenge.yaml` is the main configuration file. You can use it to change +settings like the name and namespace of the challenge, the exposed ports, the +proof-of-work difficulty etc. +For documentation on the available fields, you can run `kubectl explain challenge` and +`kubectl explain challenge.spec`. + +### /challenge + +The `challenge` directory contains a Dockerfile that describes the challenge and +any challenge files. This template comes with a Makefile to build the challenge, +which is the recommended way for pwnables if the deployed binary matters, e.g. +if you hand it out as an attachment for ROP gadgets. +If the binary layout doesn't matter, you can build it using an intermediate +container as part of the Dockerfile similar to how the chroot is created. + +### /healthcheck + +The `healthcheck` directory is optional. If you don't want to write a healthcheck, feel free to delete it. However, we strongly recommend that you implement a healthcheck :). + +We provide a basic healthcheck skeleton that uses pwntools to implement the +healthcheck code. The only requirement is that the healthcheck replies to GET +requests to http://$host:45281/healthz with either a success or an error status +code. + +In most cases, you will only have to modify `healthcheck/healthcheck.py`. + +## API contract + +Ensure your setup fulfills the following requirements to ensure it works with kCTF: + +* Verify `kctf_setup` is used as the first command in the CMD instruction of your `challenge/Dockerfile`. +* You can do pretty much whatever you want in the `challenge` directory but: +* We strongly recommend using nsjail in all challenges. While nsjail is already installed, you need to configure it in `challenge/nsjail.cfg`. For more information on nsjail, see the [official website](https://nsjail.dev/). +* Your challenge receives connections on port 1337. The port can be changed in `challenge.yaml`. +* The healthcheck directory is optional. + * If it exists, the image should run a webserver on port 45281 and respond to `/healthz` requests. diff --git a/vrp/kctf/challenge-templates/pwn/challenge.yaml b/vrp/kctf/challenge-templates/pwn/challenge.yaml new file mode 100644 index 000000000..3815d9415 --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/challenge.yaml @@ -0,0 +1,12 @@ +apiVersion: kctf.dev/v1 +kind: Challenge +metadata: + name: pwn +spec: + deployed: true + powDifficultySeconds: 0 + network: + public: false + healthcheck: + # TIP: disable the healthcheck during development + enabled: true diff --git a/vrp/kctf/challenge-templates/pwn/challenge/Dockerfile b/vrp/kctf/challenge-templates/pwn/challenge/Dockerfile new file mode 100644 index 000000000..466baae68 --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/challenge/Dockerfile @@ -0,0 +1,31 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM ubuntu:20.04 as chroot + +RUN /usr/sbin/useradd --no-create-home -u 1000 user + +COPY flag / +COPY chal /home/user/ + +FROM gcr.io/kctf-docker/challenge@sha256:e550af5df266cb89a26ace1ba5dcc685981f01d1cb61e45a898cce0c9753de7a + +COPY --from=chroot / /chroot + +COPY nsjail.cfg /home/user/ + +CMD kctf_setup && \ + kctf_drop_privs \ + socat \ + TCP-LISTEN:1337,reuseaddr,fork \ + EXEC:"kctf_pow nsjail --config /home/user/nsjail.cfg -- /home/user/chal" diff --git a/vrp/kctf/challenge-templates/pwn/challenge/Makefile b/vrp/kctf/challenge-templates/pwn/challenge/Makefile new file mode 100644 index 000000000..17d257b45 --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/challenge/Makefile @@ -0,0 +1 @@ +chal: chal.c diff --git a/vrp/kctf/challenge-templates/pwn/challenge/chal.c b/vrp/kctf/challenge-templates/pwn/challenge/chal.c new file mode 100644 index 000000000..808f87bbd --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/challenge/chal.c @@ -0,0 +1,6 @@ +#include + +int main(int argc, char *argv[]) { + system("cat /flag"); + return 0; +} diff --git a/vrp/kctf/challenge-templates/pwn/challenge/flag b/vrp/kctf/challenge-templates/pwn/challenge/flag new file mode 100644 index 000000000..9ecc2ebfb --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/challenge/flag @@ -0,0 +1 @@ +CTF{TestFlag} \ No newline at end of file diff --git a/vrp/kctf/challenge-templates/pwn/challenge/nsjail.cfg b/vrp/kctf/challenge-templates/pwn/challenge/nsjail.cfg new file mode 100644 index 000000000..35148d800 --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/challenge/nsjail.cfg @@ -0,0 +1,40 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# See options available at https://github.com/google/nsjail/blob/master/config.proto + +name: "default-nsjail-configuration" +description: "Default nsjail configuration for pwnable-style CTF task." + +mode: ONCE +uidmap {inside_id: "1000"} +gidmap {inside_id: "1000"} +mount_proc: true +rlimit_as_type: HARD +rlimit_cpu_type: HARD +rlimit_nofile_type: HARD +rlimit_nproc_type: HARD + +mount: [ + { + src: "/chroot" + dst: "/" + is_bind: true + }, + { + src: "/etc/resolv.conf" + dst: "/etc/resolv.conf" + is_bind: true + } +] diff --git a/vrp/kctf/challenge-templates/pwn/healthcheck/Dockerfile b/vrp/kctf/challenge-templates/pwn/healthcheck/Dockerfile new file mode 100644 index 000000000..ec37cc90c --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/healthcheck/Dockerfile @@ -0,0 +1,18 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM gcr.io/kctf-docker/healthcheck@sha256:06c6f051583b84d8dc4d77962256b7d1f1f247f405972e0649c821837b66c894 + +COPY healthcheck_loop.sh healthcheck.py healthz_webserver.py /home/user/ + +CMD kctf_drop_privs /home/user/healthcheck_loop.sh & /home/user/healthz_webserver.py diff --git a/vrp/kctf/challenge-templates/pwn/healthcheck/README.md b/vrp/kctf/challenge-templates/pwn/healthcheck/README.md new file mode 100644 index 000000000..8dbcd6a8a --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/healthcheck/README.md @@ -0,0 +1,14 @@ +# Healthcheck + +kCTF checks the health of challenges by accessing the healthcheck via +http://host:45281/healthz which needs to return either 200 ok or an error +depending on the status of the challenge. + +The default healthcheck consists of: +* a loop that repeatedly calls a python script and writes the status to a file +* a webserver that checks the file and serves /healthz +* the actual healthcheck code using pwntools for convenience + +To modify it, you will likely only have to change the script in healthcheck.py. +You can test if the challenge replies as expected or better add a full example +solution that will try to get the flag from the challenge. diff --git a/vrp/kctf/challenge-templates/pwn/healthcheck/healthcheck.py b/vrp/kctf/challenge-templates/pwn/healthcheck/healthcheck.py new file mode 100755 index 000000000..0a58090ae --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/healthcheck/healthcheck.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pwnlib + +def handle_pow(r): + print(r.recvuntil(b'python3 ')) + print(r.recvuntil(b' solve ')) + challenge = r.recvline().decode('ascii').strip() + p = pwnlib.tubes.process.process(['kctf_bypass_pow', challenge]) + solution = p.readall().strip() + r.sendline(solution) + print(r.recvuntil(b'Correct\n')) + +r = pwnlib.tubes.remote.remote('127.0.0.1', 1337) +print(r.recvuntil('== proof-of-work: ')) +if r.recvline().startswith(b'enabled'): + handle_pow(r) + +print(r.recvuntil(b'CTF{')) +print(r.recvuntil(b'}')) + +exit(0) diff --git a/vrp/kctf/challenge-templates/pwn/healthcheck/healthcheck_loop.sh b/vrp/kctf/challenge-templates/pwn/healthcheck/healthcheck_loop.sh new file mode 100755 index 000000000..acf691589 --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/healthcheck/healthcheck_loop.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set -Eeuo pipefail + +TIMEOUT=20 +PERIOD=30 + +export TERM=linux +export TERMINFO=/etc/terminfo + +while true; do + echo -n "[$(date)] " + if timeout "${TIMEOUT}" /home/user/healthcheck.py; then + echo 'ok' | tee /tmp/healthz + else + echo -n "$? " + echo 'err' | tee /tmp/healthz + fi + sleep "${PERIOD}" +done diff --git a/vrp/kctf/challenge-templates/pwn/healthcheck/healthz_webserver.py b/vrp/kctf/challenge-templates/pwn/healthcheck/healthz_webserver.py new file mode 100755 index 000000000..62cf01986 --- /dev/null +++ b/vrp/kctf/challenge-templates/pwn/healthcheck/healthz_webserver.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import http.server + +class HealthzHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + if self.path != '/healthz': + self.send_response(404) + self.send_header("Content-length", "0") + self.end_headers() + return + + content = b'err' + try: + with open('/tmp/healthz', 'rb') as fd: + content = fd.read().strip() + except: + pass + self.send_response(200 if content == b'ok' else 400) + self.send_header("Content-type", "text/plain") + self.send_header("Content-length", str(len(content))) + self.end_headers() + self.wfile.write(content) + +httpd = http.server.HTTPServer(('', 45281), HealthzHandler) +httpd.serve_forever() diff --git a/vrp/kctf/challenge-templates/web/README.md b/vrp/kctf/challenge-templates/web/README.md new file mode 100644 index 000000000..8e61670aa --- /dev/null +++ b/vrp/kctf/challenge-templates/web/README.md @@ -0,0 +1,87 @@ +# Quickstart guide to writing a challenge + +The basic steps when preparing a challenge are: + +* A Docker image is built from the `challenge` directory. For the simplest challenges, replacing `challenge/chal.c` is sufficient. +* Edit `challenge/Dockerfile` to change the commandline or the files you want to include. +* To try the challenge locally, you will need to + * create a a local cluster with `kctf cluster create --type kind --start $configname` + * and then deploy the challenge with `kctf chal start` +* To access the challenge, create a port forward with `kctf chal debug port-forward` and connect to it via `nc localhost PORT` using the printed port. +* Check out `kctf chal ` for more commands. + +## Sandboxing + +In order to make it possible to have RCE-style web challenges, kCTF provides two ways to sandbox a web server: + 1. **CGI-sandbox**: You can configure PHP (or any other CGI) to be sandboxed. + 2. **Proxy sandbox**: You can configure an HTTP server that sandboxes every HTTP request. + +A Proxy sandbox is a bit expensive, it starts an HTTP server on every TCP connection, hence it is a bit slow. A CGI sandbox is cheaper, and it just calls the normal CGI endpoint but with nsjail. + +The template challenge has an example of both (NodeJS running as a proxy, and PHP running as CGI). It is recommended that static resources are served with only Apache, as to save CPU and RAM. + +## Directory layout + +The following files/directories are available: + +### /challenge.yaml + +`challenge.yaml` is the main configuration file. You can use it to change +settings like the name and namespace of the challenge, the exposed ports, the +proof-of-work difficulty etc. +For documentation on the available fields, you can run `kubectl explain challenge` and +`kubectl explain challenge.spec`. + +If you would like to have a shared directory (for sessions, or uploads), you can mount it using: + + +```yaml +spec: + persistentVolumeClaims: + - name: $PUT_THE_NAME_OF_THE_CHALLENGE_HERE + size: 21Gi + podTemplate: + containers: + - name: challenge + volumeMounts: + - name: gcsfuse + subPath: sessions # this this a folder inside volume + mountPath: /mnt/disks/sessions + - name: gcsfuse + subPath: uploads + mountPath: /mnt/disks/uploads + volumes: + - name: gcsfuse + persistentVolumeClaim: + claimName: $PUT_THE_NAME_OF_THE_CHALLENGE_HERE +``` + +This will mount a file across all challenges in that directory. You can test this setup on a remote cluster using the PHP/CGI sandbox. + +### /challenge + +The `challenge` directory contains a Dockerfile that describes the challenge and +any challenge files. You can use the Dockerfile to build your challenge as well +if required. + +### /healthcheck + +The `healthcheck` directory is optional. If you don't want to write a healthcheck, feel free to delete it. However, we strongly recommend that you implement a healthcheck :). + +We provide a basic healthcheck skeleton that uses pwntools to implement the +healthcheck code. The only requirement is that the healthcheck replies to GET +requests to http://$host:45281/healthz with either a success or an error status +code. + +In most cases, you will only have to modify `healthcheck/healthcheck.py`. + +## API contract + +Ensure your setup fulfills the following requirements to ensure it works with kCTF: + +* Verify `kctf_setup` is used as the first command in the CMD instruction of your `challenge/Dockerfile`. +* You can do pretty much whatever you want in the `challenge` directory but: +* We strongly recommend using nsjail in all challenges. While nsjail is already installed, you need to configure it in `challenge/nsjail.cfg`. For more information on nsjail, see the [official website](https://nsjail.dev/). +* Your challenge receives connections on port 1337. The port can be changed in `challenge.yaml`. +* The healthcheck directory is optional. + * If it exists, the image should run a webserver on port 45281 and respond to `/healthz` requests. diff --git a/vrp/kctf/challenge-templates/web/challenge.yaml b/vrp/kctf/challenge-templates/web/challenge.yaml new file mode 100644 index 000000000..ed731a9cb --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge.yaml @@ -0,0 +1,15 @@ +apiVersion: kctf.dev/v1 +kind: Challenge +metadata: + name: apache-others +spec: + deployed: true + powDifficultySeconds: 0 + network: + public: false + ports: + - protocol: "HTTPS" + targetPort: 1337 + healthcheck: + # TIP: disable the healthcheck during development + enabled: true diff --git a/vrp/kctf/challenge-templates/web/challenge/Dockerfile b/vrp/kctf/challenge-templates/web/challenge/Dockerfile new file mode 100644 index 000000000..2c99da6ff --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/Dockerfile @@ -0,0 +1,72 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM ubuntu:20.04 as chroot + +RUN /usr/sbin/useradd -u 1000 user + +RUN apt-get update \ + && apt-get install -yq --no-install-recommends \ + curl ca-certificates socat gnupg lsb-release software-properties-common php-cgi \ + && rm -rf /var/lib/apt/lists/* + +RUN curl -sSL https://deb.nodesource.com/gpgkey/nodesource.gpg.key | apt-key add - \ + && (echo "deb https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main";\ + echo "deb-src https://deb.nodesource.com/node_10.x $(lsb_release -s -c) main") \ + > /etc/apt/sources.list.d/nodesource.list \ + && add-apt-repository universe \ + && apt-get update \ + && apt-get install -yq --no-install-recommends nodejs socat \ + && rm -rf /var/lib/apt/lists/* + +VOLUME /mnt/disks/sessions +VOLUME /mnt/disks/uploads + +COPY web-apps /web-apps +COPY web-servers /web-servers + +COPY flag / + +FROM gcr.io/kctf-docker/challenge@sha256:e550af5df266cb89a26ace1ba5dcc685981f01d1cb61e45a898cce0c9753de7a + +RUN apt-get update \ + && DEBIAN_FRONTEND=noninteractive apt-get install -yq --no-install-recommends tzdata apache2 \ + && ln -fs /usr/share/zoneinfo/Europe/Berlin /etc/localtime \ + && dpkg-reconfigure --frontend noninteractive tzdata \ + && rm -rf /var/lib/apt/lists/* + +RUN service apache2 start + +COPY --from=chroot / /chroot + +# For Proxy +RUN ln -s /etc/apache2/mods-available/proxy.load /etc/apache2/mods-enabled/ +RUN ln -s /etc/apache2/mods-available/proxy_http.load /etc/apache2/mods-enabled/ + +# For CGI sandboxing +RUN ln -s /etc/apache2/mods-available/cgi.load /etc/apache2/mods-enabled/cgi.load +RUN ln -s /etc/apache2/mods-available/actions.load /etc/apache2/mods-enabled/actions.load +RUN ln -s /chroot/web-apps /web-apps +COPY cgi-bin /usr/lib/cgi-bin + +COPY apache2-kctf-nsjail.conf /etc/apache2/conf-enabled/ + +COPY web-servers.nsjail.cfg /home/user/web-servers.nsjail.cfg +COPY cgi-bin.nsjail.cfg /home/user/cgi-bin.nsjail.cfg + +VOLUME /var/log/apache2 +VOLUME /var/run/apache2 + +CMD kctf_setup \ + && (kctf_drop_privs nsjail --config /home/user/web-servers.nsjail.cfg --port 8081 -- /web-servers/nodejs.sh &) \ + && bash -c 'source /etc/apache2/envvars && APACHE_RUN_USER=user APACHE_RUN_GROUP=user /usr/sbin/apache2 -D FOREGROUND' diff --git a/vrp/kctf/challenge-templates/web/challenge/apache2-kctf-nsjail.conf b/vrp/kctf/challenge-templates/web/challenge/apache2-kctf-nsjail.conf new file mode 100644 index 000000000..da12b9c86 --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/apache2-kctf-nsjail.conf @@ -0,0 +1,20 @@ +ServerName kctf-nsjail +Listen 1337 +User user + +# This is only necessary for CGI sandboxing + + Options +ExecCGI + Options +FollowSymLinks + Action application/x-nsjail-httpd-php /cgi-bin/nsjail-php-cgi + AddHandler application/x-nsjail-httpd-php php + Require all granted + + + + # For proxy sandboxing use the two lines below + ProxyPreserveHost On + ProxyPass "/nodejs" "http://localhost:8081/" + # For CGI sandboxing use the line below + DocumentRoot "/web-apps/php" + diff --git a/vrp/kctf/challenge-templates/web/challenge/cgi-bin.nsjail.cfg b/vrp/kctf/challenge-templates/web/challenge/cgi-bin.nsjail.cfg new file mode 100644 index 000000000..43d9255a4 --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/cgi-bin.nsjail.cfg @@ -0,0 +1,68 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# See options available at https://github.com/google/nsjail/blob/master/config.proto + +name: "apache2-proxy-nsjail" +description: "Example nsjail configuration for containing a web server." + +mode: ONCE +uidmap {inside_id: "1000"} +gidmap {inside_id: "1000"} +mount_proc: true +keep_env: true +rlimit_as_type: HARD +rlimit_cpu_type: HARD +rlimit_nofile_type: HARD +rlimit_nproc_type: HARD + +mount: [ + { + src: "/chroot" + dst: "/" + is_bind: true + }, + { + src: "/dev" + dst: "/dev" + is_bind: true + }, + { + src: "/dev/null" + dst: "/dev/null" + is_bind: true + }, + { + src: "/etc/resolv.conf" + dst: "/etc/resolv.conf" + is_bind: true + }, + { + src: "/mnt/disks/sessions" + dst: "/mnt/disks/sessions" + is_bind: true + rw: true + }, + { + src: "/mnt/disks/uploads" + dst: "/mnt/disks/uploads" + is_bind: true + rw: true + }, + { + dst: "/tmp" + fstype: "tmpfs" + rw: true + } +] diff --git a/vrp/kctf/challenge-templates/web/challenge/cgi-bin/nsjail-php-cgi b/vrp/kctf/challenge-templates/web/challenge/cgi-bin/nsjail-php-cgi new file mode 100755 index 000000000..b4dc881c3 --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/cgi-bin/nsjail-php-cgi @@ -0,0 +1,3 @@ +#!/bin/bash + +/usr/bin/nsjail --config /home/user/cgi-bin.nsjail.cfg -- /usr/lib/cgi-bin/php $@ diff --git a/vrp/kctf/challenge-templates/web/challenge/flag b/vrp/kctf/challenge-templates/web/challenge/flag new file mode 100644 index 000000000..9ecc2ebfb --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/flag @@ -0,0 +1 @@ +CTF{TestFlag} \ No newline at end of file diff --git a/vrp/kctf/challenge-templates/web/challenge/web-apps/nodejs/app.js b/vrp/kctf/challenge-templates/web/challenge/web-apps/nodejs/app.js new file mode 100644 index 000000000..bb0d78fda --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/web-apps/nodejs/app.js @@ -0,0 +1,14 @@ +const http = require('http'); + +const hostname = '127.0.0.1'; +const port = 8080; + +const server = http.createServer((req, res) => { + res.statusCode = 200; + res.setHeader('Content-Type', 'text/plain'); + res.end(req.url.split('').reverse().join('')); +}); + +server.listen(port, hostname, () => { + console.log(`Server running at http://${hostname}:${port}/`); +}); diff --git a/vrp/kctf/challenge-templates/web/challenge/web-apps/php/index.php b/vrp/kctf/challenge-templates/web/challenge/web-apps/php/index.php new file mode 100755 index 000000000..aab4a4eb8 --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/web-apps/php/index.php @@ -0,0 +1,27 @@ + +
+
+
+
+
+ + + +
+
+ diff --git a/vrp/kctf/challenge-templates/web/challenge/web-servers.nsjail.cfg b/vrp/kctf/challenge-templates/web/challenge/web-servers.nsjail.cfg new file mode 100644 index 000000000..f304aec62 --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/web-servers.nsjail.cfg @@ -0,0 +1,56 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# See options available at https://github.com/google/nsjail/blob/master/config.proto + +name: "apache2-proxy-nsjail" +description: "Example nsjail configuration for containing a web server." + +mode: LISTEN +uidmap {inside_id: "1000"} +gidmap {inside_id: "1000"} +mount_proc: true +rlimit_as_type: HARD +rlimit_cpu_type: HARD +rlimit_nofile_type: HARD +rlimit_nproc_type: HARD + +mount: [ + { + src: "/chroot" + dst: "/" + is_bind: true + }, + { + src: "/dev" + dst: "/dev" + is_bind: true + }, + { + src: "/dev/null" + dst: "/dev/null" + is_bind: true + rw: true + }, + { + src: "/etc/resolv.conf" + dst: "/etc/resolv.conf" + is_bind: true + }, + { + dst: "/tmp" + fstype: "tmpfs" + rw: true + } +] diff --git a/vrp/kctf/challenge-templates/web/challenge/web-servers/nodejs.sh b/vrp/kctf/challenge-templates/web/challenge/web-servers/nodejs.sh new file mode 100755 index 000000000..7752f2bf5 --- /dev/null +++ b/vrp/kctf/challenge-templates/web/challenge/web-servers/nodejs.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +# Start node web server +(&>/dev/null node /web-apps/nodejs/app.js)& + +# Proxy stdin/stdout to web server +socat - TCP:127.0.0.1:8080,forever diff --git a/vrp/kctf/challenge-templates/web/healthcheck/Dockerfile b/vrp/kctf/challenge-templates/web/healthcheck/Dockerfile new file mode 100644 index 000000000..ec37cc90c --- /dev/null +++ b/vrp/kctf/challenge-templates/web/healthcheck/Dockerfile @@ -0,0 +1,18 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM gcr.io/kctf-docker/healthcheck@sha256:06c6f051583b84d8dc4d77962256b7d1f1f247f405972e0649c821837b66c894 + +COPY healthcheck_loop.sh healthcheck.py healthz_webserver.py /home/user/ + +CMD kctf_drop_privs /home/user/healthcheck_loop.sh & /home/user/healthz_webserver.py diff --git a/vrp/kctf/challenge-templates/web/healthcheck/README.md b/vrp/kctf/challenge-templates/web/healthcheck/README.md new file mode 100644 index 000000000..8dbcd6a8a --- /dev/null +++ b/vrp/kctf/challenge-templates/web/healthcheck/README.md @@ -0,0 +1,14 @@ +# Healthcheck + +kCTF checks the health of challenges by accessing the healthcheck via +http://host:45281/healthz which needs to return either 200 ok or an error +depending on the status of the challenge. + +The default healthcheck consists of: +* a loop that repeatedly calls a python script and writes the status to a file +* a webserver that checks the file and serves /healthz +* the actual healthcheck code using pwntools for convenience + +To modify it, you will likely only have to change the script in healthcheck.py. +You can test if the challenge replies as expected or better add a full example +solution that will try to get the flag from the challenge. diff --git a/vrp/kctf/challenge-templates/web/healthcheck/healthcheck.py b/vrp/kctf/challenge-templates/web/healthcheck/healthcheck.py new file mode 100755 index 000000000..913af2f9f --- /dev/null +++ b/vrp/kctf/challenge-templates/web/healthcheck/healthcheck.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pwnlib + +if b"imanode" in pwnlib.util.web.wget("http://localhost:1337/nodejs?edonami"): + exit(0) + +exit(1) diff --git a/vrp/kctf/challenge-templates/web/healthcheck/healthcheck_loop.sh b/vrp/kctf/challenge-templates/web/healthcheck/healthcheck_loop.sh new file mode 100755 index 000000000..acf691589 --- /dev/null +++ b/vrp/kctf/challenge-templates/web/healthcheck/healthcheck_loop.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set -Eeuo pipefail + +TIMEOUT=20 +PERIOD=30 + +export TERM=linux +export TERMINFO=/etc/terminfo + +while true; do + echo -n "[$(date)] " + if timeout "${TIMEOUT}" /home/user/healthcheck.py; then + echo 'ok' | tee /tmp/healthz + else + echo -n "$? " + echo 'err' | tee /tmp/healthz + fi + sleep "${PERIOD}" +done diff --git a/vrp/kctf/challenge-templates/web/healthcheck/healthz_webserver.py b/vrp/kctf/challenge-templates/web/healthcheck/healthz_webserver.py new file mode 100755 index 000000000..62cf01986 --- /dev/null +++ b/vrp/kctf/challenge-templates/web/healthcheck/healthz_webserver.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import http.server + +class HealthzHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + if self.path != '/healthz': + self.send_response(404) + self.send_header("Content-length", "0") + self.end_headers() + return + + content = b'err' + try: + with open('/tmp/healthz', 'rb') as fd: + content = fd.read().strip() + except: + pass + self.send_response(200 if content == b'ok' else 400) + self.send_header("Content-type", "text/plain") + self.send_header("Content-length", str(len(content))) + self.end_headers() + self.wfile.write(content) + +httpd = http.server.HTTPServer(('', 45281), HealthzHandler) +httpd.serve_forever() diff --git a/vrp/kctf/challenge-templates/xss-bot/README.md b/vrp/kctf/challenge-templates/xss-bot/README.md new file mode 100644 index 000000000..9509de440 --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/README.md @@ -0,0 +1,5 @@ += Example XSS Bot = + +This bot will read a url from the user and then connect to it using chrome (puppeteer). +For the simplest setup, it should be enough to modify the `challenge/cookie` +file and deploy. diff --git a/vrp/kctf/challenge-templates/xss-bot/challenge.yaml b/vrp/kctf/challenge-templates/xss-bot/challenge.yaml new file mode 100644 index 000000000..c507b09a1 --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/challenge.yaml @@ -0,0 +1,19 @@ +apiVersion: kctf.dev/v1 +kind: Challenge +metadata: + name: xss-bot +spec: + deployed: true + powDifficultySeconds: 0 + network: + public: false + healthcheck: + # TIP: disable the healthcheck during development + enabled: true + # You can allow the bot to connect to other challenges internally. + # This can be useful during testing so that you don't have to make your + # challenge public. + # The challenge will be reachable at $name.default.svc.cluster.local or + # simply at $name with the default k8s search list. + #allowConnectTo: + # - otherchallenge diff --git a/vrp/kctf/challenge-templates/xss-bot/challenge/Dockerfile b/vrp/kctf/challenge-templates/xss-bot/challenge/Dockerfile new file mode 100644 index 000000000..a20bc2371 --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/challenge/Dockerfile @@ -0,0 +1,77 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM gcr.io/kctf-docker/challenge@sha256:e550af5df266cb89a26ace1ba5dcc685981f01d1cb61e45a898cce0c9753de7a + +RUN apt-get update && apt-get install -y gnupg2 + +# Install latest chrome dev package and fonts to support major charsets (Chinese, Japanese, Arabic, Hebrew, Thai and a few others) +# Note: this installs the necessary libs to make the bundled version of Chromium that Puppeteer installs, work. +# Deps from https://github.com/puppeteer/puppeteer/blob/main/docs/troubleshooting.md#chrome-headless-doesnt-launch-on-unix +# plus libxshmfence1 which seems to be missing +RUN wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | apt-key add - \ + && sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list' \ + && apt-get update \ + && apt-get install -yq --no-install-recommends \ + ca-certificates \ + fonts-liberation \ + libappindicator3-1 \ + libasound2 \ + libatk-bridge2.0-0 \ + libatk1.0-0 \ + libc6 \ + libcairo2 \ + libcups2 \ + libdbus-1-3 \ + libexpat1 \ + libfontconfig1 \ + libgbm1 \ + libgcc1 \ + libglib2.0-0 \ + libgtk-3-0 \ + libnspr4 \ + libnss3 \ + libpango-1.0-0 \ + libpangocairo-1.0-0 \ + libstdc++6 \ + libx11-6 \ + libx11-xcb1 \ + libxcb1 \ + libxcomposite1 \ + libxcursor1 \ + libxdamage1 \ + libxext6 \ + libxfixes3 \ + libxi6 \ + libxrandr2 \ + libxrender1 \ + libxshmfence1 \ + libxss1 \ + libxtst6 \ + lsb-release \ + wget \ + xdg-utils \ + nodejs npm \ + && rm -rf /var/lib/apt/lists/* + +COPY bot.js /home/user/ +COPY cookie /home/user/ +RUN cd /home/user && npm install puppeteer + +CMD kctf_setup && \ + mount -t tmpfs none /tmp && \ + while true; do kctf_drop_privs /usr/bin/node /home/user/bot.js; done & \ + kctf_drop_privs \ + socat \ + TCP-LISTEN:1337,reuseaddr,fork \ + EXEC:"kctf_pow socat STDIN TCP\:localhost\:1338" diff --git a/vrp/kctf/challenge-templates/xss-bot/challenge/bot.js b/vrp/kctf/challenge-templates/xss-bot/challenge/bot.js new file mode 100755 index 000000000..fd22f73ca --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/challenge/bot.js @@ -0,0 +1,63 @@ +const puppeteer = require('puppeteer'); +const fs = require('fs'); +const net = require('net'); + +(async function(){ + const browser = await puppeteer.launch(); + + function ask_for_url(socket) { + socket.state = 'URL'; + socket.write('Please send me a URL to open.\n'); + } + + async function load_url(socket, data) { + let url = data.toString().trim(); + console.log(`checking url: ${url}`); + if (!url.startsWith('http://') && !url.startsWith('https://')) { + socket.state = 'ERROR'; + socket.write('Invalid scheme (http/https only).\n'); + socket.destroy(); + return; + } + socket.state = 'LOADED'; + let cookie = JSON.parse(fs.readFileSync('/home/user/cookie')); + + const context = await browser.createIncognitoBrowserContext(); + const page = await context.newPage(); + await page.setCookie(cookie); + socket.write(`Loading page ${url}.\n`); + await page.goto(url); + setTimeout(()=>{ + try { + page.close(); + socket.write('timeout\n'); + socket.destroy(); + } catch (err) { + console.log(`err: ${err}`); + } + }, 60000); + } + + var server = net.createServer(); + server.listen(1338); + console.log('listening on port 1338'); + + server.on('connection', socket=>{ + socket.on('data', data=>{ + try { + if (socket.state == 'URL') { + load_url(socket, data); + } + } catch (err) { + console.log(`err: ${err}`); + } + }); + + try { + ask_for_url(socket); + } catch (err) { + console.log(`err: ${err}`); + } + }); +})(); + diff --git a/vrp/kctf/challenge-templates/xss-bot/challenge/cookie b/vrp/kctf/challenge-templates/xss-bot/challenge/cookie new file mode 100644 index 000000000..b3d289f84 --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/challenge/cookie @@ -0,0 +1,9 @@ +{ + "name": "session", + "value": "aiy3Uushcha4Zuzu", + "domain": "zero-entropy.de", + "url": "https://zero-entropy.de/", + "path": "/", + "httpOnly": true, + "secure": true +} diff --git a/vrp/kctf/challenge-templates/xss-bot/healthcheck/Dockerfile b/vrp/kctf/challenge-templates/xss-bot/healthcheck/Dockerfile new file mode 100644 index 000000000..ec37cc90c --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/healthcheck/Dockerfile @@ -0,0 +1,18 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM gcr.io/kctf-docker/healthcheck@sha256:06c6f051583b84d8dc4d77962256b7d1f1f247f405972e0649c821837b66c894 + +COPY healthcheck_loop.sh healthcheck.py healthz_webserver.py /home/user/ + +CMD kctf_drop_privs /home/user/healthcheck_loop.sh & /home/user/healthz_webserver.py diff --git a/vrp/kctf/challenge-templates/xss-bot/healthcheck/README.md b/vrp/kctf/challenge-templates/xss-bot/healthcheck/README.md new file mode 100644 index 000000000..8dbcd6a8a --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/healthcheck/README.md @@ -0,0 +1,14 @@ +# Healthcheck + +kCTF checks the health of challenges by accessing the healthcheck via +http://host:45281/healthz which needs to return either 200 ok or an error +depending on the status of the challenge. + +The default healthcheck consists of: +* a loop that repeatedly calls a python script and writes the status to a file +* a webserver that checks the file and serves /healthz +* the actual healthcheck code using pwntools for convenience + +To modify it, you will likely only have to change the script in healthcheck.py. +You can test if the challenge replies as expected or better add a full example +solution that will try to get the flag from the challenge. diff --git a/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthcheck.py b/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthcheck.py new file mode 100755 index 000000000..1129afd43 --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthcheck.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import socket +from pwn import * + +r = remote('127.0.0.1', 1337) +l = listen() + +r.readuntil(b'URL to open.', timeout=10) +r.send(bytes('http://localhost:{}/ok'.format(l.lport), 'ascii')) + +_ = l.wait_for_connection() +l.readuntil(b'GET /ok HTTP/1.1') + +exit (0) diff --git a/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthcheck_loop.sh b/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthcheck_loop.sh new file mode 100755 index 000000000..acf691589 --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthcheck_loop.sh @@ -0,0 +1,32 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set -Eeuo pipefail + +TIMEOUT=20 +PERIOD=30 + +export TERM=linux +export TERMINFO=/etc/terminfo + +while true; do + echo -n "[$(date)] " + if timeout "${TIMEOUT}" /home/user/healthcheck.py; then + echo 'ok' | tee /tmp/healthz + else + echo -n "$? " + echo 'err' | tee /tmp/healthz + fi + sleep "${PERIOD}" +done diff --git a/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthz_webserver.py b/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthz_webserver.py new file mode 100755 index 000000000..62cf01986 --- /dev/null +++ b/vrp/kctf/challenge-templates/xss-bot/healthcheck/healthz_webserver.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import http.server + +class HealthzHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + if self.path != '/healthz': + self.send_response(404) + self.send_header("Content-length", "0") + self.end_headers() + return + + content = b'err' + try: + with open('/tmp/healthz', 'rb') as fd: + content = fd.read().strip() + except: + pass + self.send_response(200 if content == b'ok' else 400) + self.send_header("Content-type", "text/plain") + self.send_header("Content-length", str(len(content))) + self.end_headers() + self.wfile.write(content) + +httpd = http.server.HTTPServer(('', 45281), HealthzHandler) +httpd.serve_forever() diff --git a/vrp/kctf/resources/kctf.dev_challenges_crd.yaml b/vrp/kctf/resources/kctf.dev_challenges_crd.yaml new file mode 100644 index 000000000..eaf1e47af --- /dev/null +++ b/vrp/kctf/resources/kctf.dev_challenges_crd.yaml @@ -0,0 +1,6360 @@ +apiVersion: apiextensions.k8s.io/v1 +kind: CustomResourceDefinition +metadata: + name: challenges.kctf.dev +spec: + group: kctf.dev + names: + kind: Challenge + listKind: ChallengeList + plural: challenges + singular: challenge + scope: Namespaced + versions: + - additionalPrinterColumns: + - jsonPath: .status.health + name: Health + type: string + - jsonPath: .status.status + name: Status + type: string + - jsonPath: .spec.deployed + name: Deployed + type: boolean + - jsonPath: .spec.network.public + name: Public + type: boolean + name: v1 + schema: + openAPIV3Schema: + description: Challenge is the Schema for the challenges API + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this representation + of an object. Servers should convert recognized schemas to the latest + internal value, and may reject unrecognized values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource this + object represents. Servers may infer this from the endpoint the client + submits requests to. Cannot be updated. In CamelCase. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + type: object + spec: + description: ChallengeSpec defines the desired state of Challenge + properties: + allowConnectTo: + items: + type: string + type: array + deployed: + default: false + description: Shows if the challenge is ready to be deployed, if not, + it sets the replicas to 0 and disables services/ingress + type: boolean + healthcheck: + description: Healthcheck checks if the challenge works If empty, healthcheck + is not enabled by default + properties: + enabled: + default: false + type: boolean + image: + default: healthcheck + description: Image for the healthcheck container + type: string + type: object + horizontalPodAutoscalerSpec: + description: Autoscaling features determine quantity of replicas and + CPU utilization If empty, autoscaling is not enabled by default + properties: + maxReplicas: + description: upper limit for the number of pods that can be set + by the autoscaler; cannot be smaller than MinReplicas. + format: int32 + type: integer + minReplicas: + description: minReplicas is the lower limit for the number of + replicas to which the autoscaler can scale down. It defaults + to 1 pod. minReplicas is allowed to be 0 if the alpha feature + gate HPAScaleToZero is enabled and at least one Object or External + metric is configured. Scaling is active as long as at least + one metric value is available. + format: int32 + type: integer + targetCPUUtilizationPercentage: + description: target average CPU utilization (represented as a + percentage of requested CPU) over all the pods; if not specified + the default autoscaling policy will be used. + format: int32 + type: integer + required: + - maxReplicas + type: object + image: + default: challenge + description: Image used by the deployment + type: string + network: + description: 'The network specifications: if it''s public or not and + specifications about ports' + properties: + ports: + description: By default, one port is set with default values + items: + properties: + name: + description: Name of the port + type: string + port: + description: Port + format: int32 + type: integer + protocol: + description: Protocol is not optional + type: string + targetPort: + anyOf: + - type: integer + - type: string + description: TargetPort is not optional + x-kubernetes-int-or-string: true + required: + - protocol + - targetPort + type: object + type: array + public: + default: false + type: boolean + type: object + persistentVolumeClaims: + description: Names of the desired PersistentVolumeClaims + items: + type: string + type: array + podTemplate: + description: PodTemplate is used to set the template for the deployment's + pod, so that an author can add volumeMounts and other extra features + properties: + apiVersion: + description: 'APIVersion defines the versioned schema of this + representation of an object. Servers should convert recognized + schemas to the latest internal value, and may reject unrecognized + values. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#resources' + type: string + kind: + description: 'Kind is a string value representing the REST resource + this object represents. Servers may infer this from the endpoint + the client submits requests to. Cannot be updated. In CamelCase. + More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#types-kinds' + type: string + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + template: + description: Template defines the pods that will be created from + this pod template. https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status + properties: + metadata: + description: 'Standard object''s metadata. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#metadata' + type: object + spec: + description: 'Specification of the desired behavior of the + pod. More info: https://git.k8s.io/community/contributors/devel/sig-architecture/api-conventions.md#spec-and-status' + properties: + activeDeadlineSeconds: + description: Optional duration in seconds the pod may + be active on the node relative to StartTime before the + system will actively try to mark it failed and kill + associated containers. Value must be a positive integer. + format: int64 + type: integer + affinity: + description: If specified, the pod's scheduling constraints + properties: + nodeAffinity: + description: Describes node affinity scheduling rules + for the pod. + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the affinity expressions + specified by this field, but it may choose a + node that violates one or more of the expressions. + The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node + that meets all of the scheduling requirements + (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by + iterating through the elements of this field + and adding "weight" to the sum if the node matches + the corresponding matchExpressions; the node(s) + with the highest sum are the most preferred. + items: + description: An empty preferred scheduling term + matches all objects with implicit weight 0 + (i.e. it's a no-op). A null preferred scheduling + term matches no objects (i.e. is also a no-op). + properties: + preference: + description: A node selector term, associated + with the corresponding weight. + properties: + matchExpressions: + description: A list of node selector + requirements by node's labels. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that + the selector applies to. + type: string + operator: + description: Represents a key's + relationship to a set of values. + Valid operators are In, NotIn, + Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string + values. If the operator is In + or NotIn, the values array must + be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + If the operator is Gt or Lt, + the values array must have a + single element, which will be + interpreted as an integer. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector + requirements by node's fields. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that + the selector applies to. + type: string + operator: + description: Represents a key's + relationship to a set of values. + Valid operators are In, NotIn, + Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string + values. If the operator is In + or NotIn, the values array must + be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + If the operator is Gt or Lt, + the values array must have a + single element, which will be + interpreted as an integer. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + weight: + description: Weight associated with matching + the corresponding nodeSelectorTerm, in + the range 1-100. + format: int32 + type: integer + required: + - preference + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified + by this field are not met at scheduling time, + the pod will not be scheduled onto the node. + If the affinity requirements specified by this + field cease to be met at some point during pod + execution (e.g. due to an update), the system + may or may not try to eventually evict the pod + from its node. + properties: + nodeSelectorTerms: + description: Required. A list of node selector + terms. The terms are ORed. + items: + description: A null or empty node selector + term matches no objects. The requirements + of them are ANDed. The TopologySelectorTerm + type implements a subset of the NodeSelectorTerm. + properties: + matchExpressions: + description: A list of node selector + requirements by node's labels. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that + the selector applies to. + type: string + operator: + description: Represents a key's + relationship to a set of values. + Valid operators are In, NotIn, + Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string + values. If the operator is In + or NotIn, the values array must + be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + If the operator is Gt or Lt, + the values array must have a + single element, which will be + interpreted as an integer. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchFields: + description: A list of node selector + requirements by node's fields. + items: + description: A node selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: The label key that + the selector applies to. + type: string + operator: + description: Represents a key's + relationship to a set of values. + Valid operators are In, NotIn, + Exists, DoesNotExist. Gt, and + Lt. + type: string + values: + description: An array of string + values. If the operator is In + or NotIn, the values array must + be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + If the operator is Gt or Lt, + the values array must have a + single element, which will be + interpreted as an integer. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + type: object + type: array + required: + - nodeSelectorTerms + type: object + type: object + podAffinity: + description: Describes pod affinity scheduling rules + (e.g. co-locate this pod in the same node, zone, + etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the affinity expressions + specified by this field, but it may choose a + node that violates one or more of the expressions. + The node that is most preferred is the one with + the greatest sum of weights, i.e. for each node + that meets all of the scheduling requirements + (resource request, requiredDuringScheduling + affinity expressions, etc.), compute a sum by + iterating through the elements of this field + and adding "weight" to the sum if the node has + pods which matches the corresponding podAffinityTerm; + the node(s) with the highest sum are the most + preferred. + items: + description: The weights of all of the matched + WeightedPodAffinityTerm fields are added per-node + to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, + associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set + of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is + a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector + requirement is a selector that + contains values, a key, and + an operator that relates the + key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid operators + are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In or + NotIn, the values array + must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be + empty. This array is replaced + during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map + of {key,value} pairs. A single + {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator is + "In", and the values array contains + only "value". The requirements + are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which + namespaces the labelSelector applies + to (matches against); null or empty + list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where + co-located is defined as running on + a node whose value of the label with + key topologyKey matches that of any + node on which any of the selected + pods is running. Empty topologyKey + is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching + the corresponding podAffinityTerm, in + the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the affinity requirements specified + by this field are not met at scheduling time, + the pod will not be scheduled onto the node. + If the affinity requirements specified by this + field cease to be met at some point during pod + execution (e.g. due to a pod label update), + the system may or may not try to eventually + evict the pod from its node. When there are + multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. + all terms must be satisfied. + items: + description: Defines a set of pods (namely those + matching the labelSelector relative to the + given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) + with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on + which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of + resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which + namespaces the labelSelector applies to + (matches against); null or empty list + means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose + value of the label with key topologyKey + matches that of any node on which any + of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + podAntiAffinity: + description: Describes pod anti-affinity scheduling + rules (e.g. avoid putting this pod in the same node, + zone, etc. as some other pod(s)). + properties: + preferredDuringSchedulingIgnoredDuringExecution: + description: The scheduler will prefer to schedule + pods to nodes that satisfy the anti-affinity + expressions specified by this field, but it + may choose a node that violates one or more + of the expressions. The node that is most preferred + is the one with the greatest sum of weights, + i.e. for each node that meets all of the scheduling + requirements (resource request, requiredDuringScheduling + anti-affinity expressions, etc.), compute a + sum by iterating through the elements of this + field and adding "weight" to the sum if the + node has pods which matches the corresponding + podAffinityTerm; the node(s) with the highest + sum are the most preferred. + items: + description: The weights of all of the matched + WeightedPodAffinityTerm fields are added per-node + to find the most preferred node(s) + properties: + podAffinityTerm: + description: Required. A pod affinity term, + associated with the corresponding weight. + properties: + labelSelector: + description: A label query over a set + of resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is + a list of label selector requirements. + The requirements are ANDed. + items: + description: A label selector + requirement is a selector that + contains values, a key, and + an operator that relates the + key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to + a set of values. Valid operators + are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an + array of string values. + If the operator is In or + NotIn, the values array + must be non-empty. If the + operator is Exists or DoesNotExist, + the values array must be + empty. This array is replaced + during a strategic merge + patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map + of {key,value} pairs. A single + {key,value} in the matchLabels + map is equivalent to an element + of matchExpressions, whose key + field is "key", the operator is + "In", and the values array contains + only "value". The requirements + are ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which + namespaces the labelSelector applies + to (matches against); null or empty + list means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where + co-located is defined as running on + a node whose value of the label with + key topologyKey matches that of any + node on which any of the selected + pods is running. Empty topologyKey + is not allowed. + type: string + required: + - topologyKey + type: object + weight: + description: weight associated with matching + the corresponding podAffinityTerm, in + the range 1-100. + format: int32 + type: integer + required: + - podAffinityTerm + - weight + type: object + type: array + requiredDuringSchedulingIgnoredDuringExecution: + description: If the anti-affinity requirements + specified by this field are not met at scheduling + time, the pod will not be scheduled onto the + node. If the anti-affinity requirements specified + by this field cease to be met at some point + during pod execution (e.g. due to a pod label + update), the system may or may not try to eventually + evict the pod from its node. When there are + multiple elements, the lists of nodes corresponding + to each podAffinityTerm are intersected, i.e. + all terms must be satisfied. + items: + description: Defines a set of pods (namely those + matching the labelSelector relative to the + given namespace(s)) that this pod should be + co-located (affinity) or not co-located (anti-affinity) + with, where co-located is defined as running + on a node whose value of the label with key + matches that of any node on + which a pod of the set of pods is running + properties: + labelSelector: + description: A label query over a set of + resources, in this case pods. + properties: + matchExpressions: + description: matchExpressions is a list + of label selector requirements. The + requirements are ANDed. + items: + description: A label selector requirement + is a selector that contains values, + a key, and an operator that relates + the key and values. + properties: + key: + description: key is the label + key that the selector applies + to. + type: string + operator: + description: operator represents + a key's relationship to a set + of values. Valid operators are + In, NotIn, Exists and DoesNotExist. + type: string + values: + description: values is an array + of string values. If the operator + is In or NotIn, the values array + must be non-empty. If the operator + is Exists or DoesNotExist, the + values array must be empty. + This array is replaced during + a strategic merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of + {key,value} pairs. A single {key,value} + in the matchLabels map is equivalent + to an element of matchExpressions, + whose key field is "key", the operator + is "In", and the values array contains + only "value". The requirements are + ANDed. + type: object + type: object + namespaces: + description: namespaces specifies which + namespaces the labelSelector applies to + (matches against); null or empty list + means "this pod's namespace" + items: + type: string + type: array + topologyKey: + description: This pod should be co-located + (affinity) or not co-located (anti-affinity) + with the pods matching the labelSelector + in the specified namespaces, where co-located + is defined as running on a node whose + value of the label with key topologyKey + matches that of any node on which any + of the selected pods is running. Empty + topologyKey is not allowed. + type: string + required: + - topologyKey + type: object + type: array + type: object + type: object + automountServiceAccountToken: + description: AutomountServiceAccountToken indicates whether + a service account token should be automatically mounted. + type: boolean + containers: + description: List of containers belonging to the pod. + Containers cannot currently be added or removed. There + must be at least one container in a Pod. Cannot be updated. + items: + description: A single application container that you + want to run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The docker + image''s CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using + the container''s environment. If a variable cannot + be resolved, the reference in the input string + will be unchanged. The $(VAR_NAME) syntax can + be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within + a shell. The docker image''s ENTRYPOINT is used + if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. The $(VAR_NAME) + syntax can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set + in the container. Cannot be updated. + items: + description: EnvVar represents an environment + variable present in a Container. + properties: + name: + description: Name of the environment variable. + Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previous defined + environment variables in the container and + any service environment variables. If a + variable cannot be resolved, the reference + in the input string will be unchanged. The + $(VAR_NAME) syntax can be escaped with a + double $$, ie: $$(VAR_NAME). Escaped references + will never be expanded, regardless of whether + the variable exists or not. Defaults to + "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: + supports metadata.name, metadata.namespace, + metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and + requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret + in the pod's namespace + properties: + key: + description: The key of the secret + to select from. Must be a valid + secret key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined within + a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container + is starting. When a key exists in multiple sources, + the value associated with the last source will + take precedence. Values defined by an Env with + a duplicate key will take precedence. Cannot be + updated. + items: + description: EnvFromSource represents the source + of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + must be defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend + to each key in the ConfigMap. Must be a + C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config + management to default or override container images + in workload controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, + Never, IfNotPresent. Defaults to Always if :latest + tag is specified, or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system + should take in response to container lifecycle + events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the handler + fails, the container is terminated and restarted + according to its restart policy. Other management + of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in HTTP + probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to + connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately + before a container is terminated due to an + API request or management event such as liveness/startup + probe failure, preemption, resource contention, + etc. The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop + hooked is executed. Regardless of the outcome + of the handler, the container will eventually + terminate within the Pod''s termination grace + period. Other management of the container + blocks until the hook completes or until the + termination grace period is reached. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in HTTP + probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to + connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as + a DNS_LABEL. Each container in a pod must have + a unique name (DNS_LABEL). Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Exposing a port here gives the system additional + information about the network connections a container + uses, but is primarily informational. Not specifying + a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default + "0.0.0.0" address inside a container will be accessible + from the network. Cannot be updated. + items: + description: ContainerPort represents a network + port in a single container. + properties: + containerPort: + description: Number of port to expose on the + pod's IP address. This must be a valid port + number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external + port to. + type: string + hostPort: + description: Number of port to expose on the + host. If specified, this must be a valid + port number, 0 < x < 65536. If HostNetwork + is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an + IANA_SVC_NAME and unique within the pod. + Each named port in a pod must have a unique + name. Name for the port that can be referred + to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, + TCP, or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service + readiness. Container will be removed from service + endpoints if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this + container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. If Requests + is omitted for a container, it defaults to + Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: 'Security options the pod should run + with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls + whether a process can gain more privileges + than its parent process. This bool directly + controls if the no_new_privs flag will be + set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run + as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when + running containers. Defaults to the default + set of capabilities granted by the container + runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. + Processes in privileged containers are essentially + equivalent to root on the host. Defaults to + false. + type: boolean + procMount: + description: procMount denotes the type of proc + mount to use for the containers. The default + is DefaultProcMount which uses the container + runtime defaults for readonly paths and masked + paths. This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of + the container process. Uses runtime default + if unset. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must + run as a non-root user. If true, the Kubelet + will validate the image at runtime to ensure + that it does not run as UID 0 (root) and fail + to start the container if it does. If unset + or false, no such validation will be performed. + May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of + the container process. Defaults to user specified + in image metadata if unspecified. May also + be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied + to the container. If unspecified, the container + runtime will allocate a random SELinux context + for each container. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + level: + description: Level is SELinux level label + that applies to the container. + type: string + role: + description: Role is a SELinux role label + that applies to the container. + type: string + type: + description: Type is a SELinux type label + that applies to the container. + type: string + user: + description: User is a SELinux user label + that applies to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied + to all containers. If unspecified, the options + from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where + the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential + spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the + name of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to + run the entrypoint of the container process. + Defaults to the user specified in image + metadata if unspecified. May also be set + in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod + has successfully initialized. If specified, no + other probes are executed until this completes + successfully. If this probe fails, the Pod will + be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, when it + might take a long time to load data or warm a + cache, than during steady-state operation. This + cannot be updated. This is a beta feature enabled + by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate + a buffer for stdin in the container runtime. If + this is not set, reads from stdin in the container + will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should + close the stdin channel after it has been opened + by a single attach. When stdin is true the stdin + stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is + opened on container start, is empty until the + first client attaches to stdin, and then remains + open and accepts data until the client disconnects, + at which time stdin is closed and remains closed + until the container is restarted. If this flag + is false, a container processes that reads from + stdin will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to + which the container''s termination message will + be written is mounted into the container''s filesystem. + Message written is intended to be brief final + status, such as an assertion failure message. + Will be truncated by the node if greater than + 4096 bytes. The total message length across all + containers will be limited to 12kb. Defaults to + /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message + should be populated. File will use the contents + of terminationMessagePath to populate the container + status message on both success and failure. FallbackToLogsOnError + will use the last chunk of container log output + if the termination message file is empty and the + container exited with an error. The log output + is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate + a TTY for itself, also requires 'stdin' to be + true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block + devices to be used by the container. + items: + description: volumeDevice describes a mapping + of a raw block device within a container. + properties: + devicePath: + description: devicePath is the path inside + of the container that the device will be + mapped to. + type: string + name: + description: name must match the name of a + persistentVolumeClaim in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's + filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting + of a Volume within a container. + properties: + mountPath: + description: Path within the container at + which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how + mounts are propagated from the host to container + and the other way around. When not set, + MountPropagationNone is used. This field + is beta in 1.10. + type: string + name: + description: This must match the Name of a + Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume from which + the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume + from which the container's volume should + be mounted. Behaves similarly to SubPath + but environment variable references $(VAR_NAME) + are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not + specified, the container runtime's default will + be used, which might be configured in the container + image. Cannot be updated. + type: string + required: + - name + type: object + type: array + dnsConfig: + description: Specifies the DNS parameters of a pod. Parameters + specified here will be merged to the generated DNS configuration + based on DNSPolicy. + properties: + nameservers: + description: A list of DNS name server IP addresses. + This will be appended to the base nameservers generated + from DNSPolicy. Duplicated nameservers will be removed. + items: + type: string + type: array + options: + description: A list of DNS resolver options. This + will be merged with the base options generated from + DNSPolicy. Duplicated entries will be removed. Resolution + options given in Options will override those that + appear in the base DNSPolicy. + items: + description: PodDNSConfigOption defines DNS resolver + options of a pod. + properties: + name: + description: Required. + type: string + value: + type: string + type: object + type: array + searches: + description: A list of DNS search domains for host-name + lookup. This will be appended to the base search + paths generated from DNSPolicy. Duplicated search + paths will be removed. + items: + type: string + type: array + type: object + dnsPolicy: + description: Set DNS policy for the pod. Defaults to "ClusterFirst". + Valid values are 'ClusterFirstWithHostNet', 'ClusterFirst', + 'Default' or 'None'. DNS parameters given in DNSConfig + will be merged with the policy selected with DNSPolicy. + To have DNS options set along with hostNetwork, you + have to specify DNS policy explicitly to 'ClusterFirstWithHostNet'. + type: string + enableServiceLinks: + description: 'EnableServiceLinks indicates whether information + about services should be injected into pod''s environment + variables, matching the syntax of Docker links. Optional: + Defaults to true.' + type: boolean + ephemeralContainers: + description: List of ephemeral containers run in this + pod. Ephemeral containers may be run in an existing + pod to perform user-initiated actions such as debugging. + This list cannot be specified when creating a pod, and + it cannot be modified by updating the pod spec. In order + to add an ephemeral container to an existing pod, use + the pod's ephemeralcontainers subresource. This field + is alpha-level and is only honored by servers that enable + the EphemeralContainers feature. + items: + description: An EphemeralContainer is a container that + may be added temporarily to an existing pod for user-initiated + activities such as debugging. Ephemeral containers + have no resource or scheduling guarantees, and they + will not be restarted when they exit or when a pod + is removed or restarted. If an ephemeral container + causes a pod to exceed its resource allocation, the + pod may be evicted. Ephemeral containers may not be + added by directly updating the pod spec. They must + be added via the pod's ephemeralcontainers subresource, + and they will appear in the pod spec once added. This + is an alpha feature enabled by the EphemeralContainers + feature flag. + properties: + args: + description: 'Arguments to the entrypoint. The docker + image''s CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using + the container''s environment. If a variable cannot + be resolved, the reference in the input string + will be unchanged. The $(VAR_NAME) syntax can + be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within + a shell. The docker image''s ENTRYPOINT is used + if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. The $(VAR_NAME) + syntax can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set + in the container. Cannot be updated. + items: + description: EnvVar represents an environment + variable present in a Container. + properties: + name: + description: Name of the environment variable. + Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previous defined + environment variables in the container and + any service environment variables. If a + variable cannot be resolved, the reference + in the input string will be unchanged. The + $(VAR_NAME) syntax can be escaped with a + double $$, ie: $$(VAR_NAME). Escaped references + will never be expanded, regardless of whether + the variable exists or not. Defaults to + "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: + supports metadata.name, metadata.namespace, + metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and + requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret + in the pod's namespace + properties: + key: + description: The key of the secret + to select from. Must be a valid + secret key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined within + a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container + is starting. When a key exists in multiple sources, + the value associated with the last source will + take precedence. Values defined by an Env with + a duplicate key will take precedence. Cannot be + updated. + items: + description: EnvFromSource represents the source + of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + must be defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend + to each key in the ConfigMap. Must be a + C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, + Never, IfNotPresent. Defaults to Always if :latest + tag is specified, or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Lifecycle is not allowed for ephemeral + containers. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the handler + fails, the container is terminated and restarted + according to its restart policy. Other management + of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in HTTP + probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to + connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately + before a container is terminated due to an + API request or management event such as liveness/startup + probe failure, preemption, resource contention, + etc. The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop + hooked is executed. Regardless of the outcome + of the handler, the container will eventually + terminate within the Pod''s termination grace + period. Other management of the container + blocks until the hook completes or until the + termination grace period is reached. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in HTTP + probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to + connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the ephemeral container specified + as a DNS_LABEL. This name must be unique among + all containers, init containers and ephemeral + containers. + type: string + ports: + description: Ports are not allowed for ephemeral + containers. + items: + description: ContainerPort represents a network + port in a single container. + properties: + containerPort: + description: Number of port to expose on the + pod's IP address. This must be a valid port + number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external + port to. + type: string + hostPort: + description: Number of port to expose on the + host. If specified, this must be a valid + port number, 0 < x < 65536. If HostNetwork + is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an + IANA_SVC_NAME and unique within the pod. + Each named port in a pod must have a unique + name. Name for the port that can be referred + to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, + TCP, or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + readinessProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: Resources are not allowed for ephemeral + containers. Ephemeral containers use spare resources + already allocated to the pod. + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. If Requests + is omitted for a container, it defaults to + Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: SecurityContext is not allowed for + ephemeral containers. + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls + whether a process can gain more privileges + than its parent process. This bool directly + controls if the no_new_privs flag will be + set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run + as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when + running containers. Defaults to the default + set of capabilities granted by the container + runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. + Processes in privileged containers are essentially + equivalent to root on the host. Defaults to + false. + type: boolean + procMount: + description: procMount denotes the type of proc + mount to use for the containers. The default + is DefaultProcMount which uses the container + runtime defaults for readonly paths and masked + paths. This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of + the container process. Uses runtime default + if unset. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must + run as a non-root user. If true, the Kubelet + will validate the image at runtime to ensure + that it does not run as UID 0 (root) and fail + to start the container if it does. If unset + or false, no such validation will be performed. + May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of + the container process. Defaults to user specified + in image metadata if unspecified. May also + be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied + to the container. If unspecified, the container + runtime will allocate a random SELinux context + for each container. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + level: + description: Level is SELinux level label + that applies to the container. + type: string + role: + description: Role is a SELinux role label + that applies to the container. + type: string + type: + description: Type is a SELinux type label + that applies to the container. + type: string + user: + description: User is a SELinux user label + that applies to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied + to all containers. If unspecified, the options + from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where + the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential + spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the + name of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to + run the entrypoint of the container process. + Defaults to the user specified in image + metadata if unspecified. May also be set + in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + startupProbe: + description: Probes are not allowed for ephemeral + containers. + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate + a buffer for stdin in the container runtime. If + this is not set, reads from stdin in the container + will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should + close the stdin channel after it has been opened + by a single attach. When stdin is true the stdin + stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is + opened on container start, is empty until the + first client attaches to stdin, and then remains + open and accepts data until the client disconnects, + at which time stdin is closed and remains closed + until the container is restarted. If this flag + is false, a container processes that reads from + stdin will never receive an EOF. Default is false + type: boolean + targetContainerName: + description: If set, the name of the container from + PodSpec that this ephemeral container targets. + The ephemeral container will be run in the namespaces + (IPC, PID, etc) of this container. If not set + then the ephemeral container is run in whatever + namespaces are shared for the pod. Note that the + container runtime must support this feature. + type: string + terminationMessagePath: + description: 'Optional: Path at which the file to + which the container''s termination message will + be written is mounted into the container''s filesystem. + Message written is intended to be brief final + status, such as an assertion failure message. + Will be truncated by the node if greater than + 4096 bytes. The total message length across all + containers will be limited to 12kb. Defaults to + /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message + should be populated. File will use the contents + of terminationMessagePath to populate the container + status message on both success and failure. FallbackToLogsOnError + will use the last chunk of container log output + if the termination message file is empty and the + container exited with an error. The log output + is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate + a TTY for itself, also requires 'stdin' to be + true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block + devices to be used by the container. + items: + description: volumeDevice describes a mapping + of a raw block device within a container. + properties: + devicePath: + description: devicePath is the path inside + of the container that the device will be + mapped to. + type: string + name: + description: name must match the name of a + persistentVolumeClaim in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's + filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting + of a Volume within a container. + properties: + mountPath: + description: Path within the container at + which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how + mounts are propagated from the host to container + and the other way around. When not set, + MountPropagationNone is used. This field + is beta in 1.10. + type: string + name: + description: This must match the Name of a + Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume from which + the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume + from which the container's volume should + be mounted. Behaves similarly to SubPath + but environment variable references $(VAR_NAME) + are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not + specified, the container runtime's default will + be used, which might be configured in the container + image. Cannot be updated. + type: string + required: + - name + type: object + type: array + hostAliases: + description: HostAliases is an optional list of hosts + and IPs that will be injected into the pod's hosts file + if specified. This is only valid for non-hostNetwork + pods. + items: + description: HostAlias holds the mapping between IP + and hostnames that will be injected as an entry in + the pod's hosts file. + properties: + hostnames: + description: Hostnames for the above IP address. + items: + type: string + type: array + ip: + description: IP address of the host file entry. + type: string + type: object + type: array + hostIPC: + description: 'Use the host''s ipc namespace. Optional: + Default to false.' + type: boolean + hostNetwork: + description: Host networking requested for this pod. Use + the host's network namespace. If this option is set, + the ports that will be used must be specified. Default + to false. + type: boolean + hostPID: + description: 'Use the host''s pid namespace. Optional: + Default to false.' + type: boolean + hostname: + description: Specifies the hostname of the Pod If not + specified, the pod's hostname will be set to a system-defined + value. + type: string + imagePullSecrets: + description: 'ImagePullSecrets is an optional list of + references to secrets in the same namespace to use for + pulling any of the images used by this PodSpec. If specified, + these secrets will be passed to individual puller implementations + for them to use. For example, in the case of docker, + only DockerConfig type secrets are honored. More info: + https://kubernetes.io/docs/concepts/containers/images#specifying-imagepullsecrets-on-a-pod' + items: + description: LocalObjectReference contains enough information + to let you locate the referenced object inside the + same namespace. + properties: + name: + description: 'Name of the referent. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, kind, + uid?' + type: string + type: object + type: array + initContainers: + description: 'List of initialization containers belonging + to the pod. Init containers are executed in order prior + to containers being started. If any init container fails, + the pod is considered to have failed and is handled + according to its restartPolicy. The name for an init + container or normal container must be unique among all + containers. Init containers may not have Lifecycle actions, + Readiness probes, Liveness probes, or Startup probes. + The resourceRequirements of an init container are taken + into account during scheduling by finding the highest + request/limit for each resource type, and then using + the max of of that value or the sum of the normal containers. + Limits are applied to init containers in a similar fashion. + Init containers cannot currently be added or removed. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/init-containers/' + items: + description: A single application container that you + want to run within a pod. + properties: + args: + description: 'Arguments to the entrypoint. The docker + image''s CMD is used if this is not provided. + Variable references $(VAR_NAME) are expanded using + the container''s environment. If a variable cannot + be resolved, the reference in the input string + will be unchanged. The $(VAR_NAME) syntax can + be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + command: + description: 'Entrypoint array. Not executed within + a shell. The docker image''s ENTRYPOINT is used + if this is not provided. Variable references $(VAR_NAME) + are expanded using the container''s environment. + If a variable cannot be resolved, the reference + in the input string will be unchanged. The $(VAR_NAME) + syntax can be escaped with a double $$, ie: $$(VAR_NAME). + Escaped references will never be expanded, regardless + of whether the variable exists or not. Cannot + be updated. More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell' + items: + type: string + type: array + env: + description: List of environment variables to set + in the container. Cannot be updated. + items: + description: EnvVar represents an environment + variable present in a Container. + properties: + name: + description: Name of the environment variable. + Must be a C_IDENTIFIER. + type: string + value: + description: 'Variable references $(VAR_NAME) + are expanded using the previous defined + environment variables in the container and + any service environment variables. If a + variable cannot be resolved, the reference + in the input string will be unchanged. The + $(VAR_NAME) syntax can be escaped with a + double $$, ie: $$(VAR_NAME). Escaped references + will never be expanded, regardless of whether + the variable exists or not. Defaults to + "".' + type: string + valueFrom: + description: Source for the environment variable's + value. Cannot be used if value is not empty. + properties: + configMapKeyRef: + description: Selects a key of a ConfigMap. + properties: + key: + description: The key to select. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its key must be defined + type: boolean + required: + - key + type: object + fieldRef: + description: 'Selects a field of the pod: + supports metadata.name, metadata.namespace, + metadata.labels, metadata.annotations, + spec.nodeName, spec.serviceAccountName, + status.hostIP, status.podIP, status.podIPs.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and + requests (limits.cpu, limits.memory, + limits.ephemeral-storage, requests.cpu, + requests.memory and requests.ephemeral-storage) + are currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + secretKeyRef: + description: Selects a key of a secret + in the pod's namespace + properties: + key: + description: The key of the secret + to select from. Must be a valid + secret key. + type: string + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + required: + - key + type: object + type: object + required: + - name + type: object + type: array + envFrom: + description: List of sources to populate environment + variables in the container. The keys defined within + a source must be a C_IDENTIFIER. All invalid keys + will be reported as an event when the container + is starting. When a key exists in multiple sources, + the value associated with the last source will + take precedence. Values defined by an Env with + a duplicate key will take precedence. Cannot be + updated. + items: + description: EnvFromSource represents the source + of a set of ConfigMaps + properties: + configMapRef: + description: The ConfigMap to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + must be defined + type: boolean + type: object + prefix: + description: An optional identifier to prepend + to each key in the ConfigMap. Must be a + C_IDENTIFIER. + type: string + secretRef: + description: The Secret to select from + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + must be defined + type: boolean + type: object + type: object + type: array + image: + description: 'Docker image name. More info: https://kubernetes.io/docs/concepts/containers/images + This field is optional to allow higher level config + management to default or override container images + in workload controllers like Deployments and StatefulSets.' + type: string + imagePullPolicy: + description: 'Image pull policy. One of Always, + Never, IfNotPresent. Defaults to Always if :latest + tag is specified, or IfNotPresent otherwise. Cannot + be updated. More info: https://kubernetes.io/docs/concepts/containers/images#updating-images' + type: string + lifecycle: + description: Actions that the management system + should take in response to container lifecycle + events. Cannot be updated. + properties: + postStart: + description: 'PostStart is called immediately + after a container is created. If the handler + fails, the container is terminated and restarted + according to its restart policy. Other management + of the container blocks until the hook completes. + More info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in HTTP + probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to + connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + preStop: + description: 'PreStop is called immediately + before a container is terminated due to an + API request or management event such as liveness/startup + probe failure, preemption, resource contention, + etc. The handler is not called if the container + crashes or exits. The reason for termination + is passed to the handler. The Pod''s termination + grace period countdown begins before the PreStop + hooked is executed. Regardless of the outcome + of the handler, the container will eventually + terminate within the Pod''s termination grace + period. Other management of the container + blocks until the hook completes or until the + termination grace period is reached. More + info: https://kubernetes.io/docs/concepts/containers/container-lifecycle-hooks/#container-hooks' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the + action to take. + properties: + command: + description: Command is the command + line to execute inside the container, + the working directory for the command is + root ('/') in the container's filesystem. + The command is simply exec'd, it is + not run inside a shell, so traditional + shell instructions ('|', etc) won't + work. To use a shell, you need to + explicitly call out to that shell. + Exit status of 0 is treated as live/healthy + and non-zero is unhealthy. + items: + type: string + type: array + type: object + httpGet: + description: HTTPGet specifies the http + request to perform. + properties: + host: + description: Host name to connect to, + defaults to the pod IP. You probably + want to set "Host" in httpHeaders + instead. + type: string + httpHeaders: + description: Custom headers to set in + the request. HTTP allows repeated + headers. + items: + description: HTTPHeader describes + a custom header to be used in HTTP + probes + properties: + name: + description: The header field + name + type: string + value: + description: The header field + value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet + supported TODO: implement a realistic + TCP lifecycle hook' + properties: + host: + description: 'Optional: Host name to + connect to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number + must be in the range 1 to 65535. Name + must be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + type: object + type: object + livenessProbe: + description: 'Periodic probe of container liveness. + Container will be restarted if the probe fails. + Cannot be updated. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + name: + description: Name of the container specified as + a DNS_LABEL. Each container in a pod must have + a unique name (DNS_LABEL). Cannot be updated. + type: string + ports: + description: List of ports to expose from the container. + Exposing a port here gives the system additional + information about the network connections a container + uses, but is primarily informational. Not specifying + a port here DOES NOT prevent that port from being + exposed. Any port which is listening on the default + "0.0.0.0" address inside a container will be accessible + from the network. Cannot be updated. + items: + description: ContainerPort represents a network + port in a single container. + properties: + containerPort: + description: Number of port to expose on the + pod's IP address. This must be a valid port + number, 0 < x < 65536. + format: int32 + type: integer + hostIP: + description: What host IP to bind the external + port to. + type: string + hostPort: + description: Number of port to expose on the + host. If specified, this must be a valid + port number, 0 < x < 65536. If HostNetwork + is specified, this must match ContainerPort. + Most containers do not need this. + format: int32 + type: integer + name: + description: If specified, this must be an + IANA_SVC_NAME and unique within the pod. + Each named port in a pod must have a unique + name. Name for the port that can be referred + to by services. + type: string + protocol: + description: Protocol for port. Must be UDP, + TCP, or SCTP. Defaults to "TCP". + type: string + required: + - containerPort + type: object + type: array + x-kubernetes-list-map-keys: + - containerPort + x-kubernetes-list-type: map + readinessProbe: + description: 'Periodic probe of container service + readiness. Container will be removed from service + endpoints if the probe fails. Cannot be updated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + resources: + description: 'Compute Resources required by this + container. Cannot be updated. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + properties: + limits: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Limits describes the maximum amount + of compute resources allowed. More info: https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + requests: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Requests describes the minimum + amount of compute resources required. If Requests + is omitted for a container, it defaults to + Limits if that is explicitly specified, otherwise + to an implementation-defined value. More info: + https://kubernetes.io/docs/concepts/configuration/manage-compute-resources-container/' + type: object + type: object + securityContext: + description: 'Security options the pod should run + with. More info: https://kubernetes.io/docs/concepts/policy/security-context/ + More info: https://kubernetes.io/docs/tasks/configure-pod-container/security-context/' + properties: + allowPrivilegeEscalation: + description: 'AllowPrivilegeEscalation controls + whether a process can gain more privileges + than its parent process. This bool directly + controls if the no_new_privs flag will be + set on the container process. AllowPrivilegeEscalation + is true always when the container is: 1) run + as Privileged 2) has CAP_SYS_ADMIN' + type: boolean + capabilities: + description: The capabilities to add/drop when + running containers. Defaults to the default + set of capabilities granted by the container + runtime. + properties: + add: + description: Added capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + drop: + description: Removed capabilities + items: + description: Capability represent POSIX + capabilities type + type: string + type: array + type: object + privileged: + description: Run container in privileged mode. + Processes in privileged containers are essentially + equivalent to root on the host. Defaults to + false. + type: boolean + procMount: + description: procMount denotes the type of proc + mount to use for the containers. The default + is DefaultProcMount which uses the container + runtime defaults for readonly paths and masked + paths. This requires the ProcMountType feature + flag to be enabled. + type: string + readOnlyRootFilesystem: + description: Whether this container has a read-only + root filesystem. Default is false. + type: boolean + runAsGroup: + description: The GID to run the entrypoint of + the container process. Uses runtime default + if unset. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must + run as a non-root user. If true, the Kubelet + will validate the image at runtime to ensure + that it does not run as UID 0 (root) and fail + to start the container if it does. If unset + or false, no such validation will be performed. + May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of + the container process. Defaults to user specified + in image metadata if unspecified. May also + be set in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, the + value specified in SecurityContext takes precedence. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied + to the container. If unspecified, the container + runtime will allocate a random SELinux context + for each container. May also be set in PodSecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + level: + description: Level is SELinux level label + that applies to the container. + type: string + role: + description: Role is a SELinux role label + that applies to the container. + type: string + type: + description: Type is a SELinux type label + that applies to the container. + type: string + user: + description: User is a SELinux user label + that applies to the container. + type: string + type: object + windowsOptions: + description: The Windows specific settings applied + to all containers. If unspecified, the options + from the PodSecurityContext will be used. + If set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where + the GMSA admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential + spec named by the GMSACredentialSpecName + field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the + name of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to + run the entrypoint of the container process. + Defaults to the user specified in image + metadata if unspecified. May also be set + in PodSecurityContext. If set in both + SecurityContext and PodSecurityContext, + the value specified in SecurityContext + takes precedence. + type: string + type: object + type: object + startupProbe: + description: 'StartupProbe indicates that the Pod + has successfully initialized. If specified, no + other probes are executed until this completes + successfully. If this probe fails, the Pod will + be restarted, just as if the livenessProbe failed. + This can be used to provide different probe parameters + at the beginning of a Pod''s lifecycle, when it + might take a long time to load data or warm a + cache, than during steady-state operation. This + cannot be updated. This is a beta feature enabled + by the StartupProbe feature flag. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + properties: + exec: + description: One and only one of the following + should be specified. Exec specifies the action + to take. + properties: + command: + description: Command is the command line + to execute inside the container, the working + directory for the command is root ('/') + in the container's filesystem. The command + is simply exec'd, it is not run inside + a shell, so traditional shell instructions + ('|', etc) won't work. To use a shell, + you need to explicitly call out to that + shell. Exit status of 0 is treated as + live/healthy and non-zero is unhealthy. + items: + type: string + type: array + type: object + failureThreshold: + description: Minimum consecutive failures for + the probe to be considered failed after having + succeeded. Defaults to 3. Minimum value is + 1. + format: int32 + type: integer + httpGet: + description: HTTPGet specifies the http request + to perform. + properties: + host: + description: Host name to connect to, defaults + to the pod IP. You probably want to set + "Host" in httpHeaders instead. + type: string + httpHeaders: + description: Custom headers to set in the + request. HTTP allows repeated headers. + items: + description: HTTPHeader describes a custom + header to be used in HTTP probes + properties: + name: + description: The header field name + type: string + value: + description: The header field value + type: string + required: + - name + - value + type: object + type: array + path: + description: Path to access on the HTTP + server. + type: string + port: + anyOf: + - type: integer + - type: string + description: Name or number of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + scheme: + description: Scheme to use for connecting + to the host. Defaults to HTTP. + type: string + required: + - port + type: object + initialDelaySeconds: + description: 'Number of seconds after the container + has started before liveness probes are initiated. + More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + periodSeconds: + description: How often (in seconds) to perform + the probe. Default to 10 seconds. Minimum + value is 1. + format: int32 + type: integer + successThreshold: + description: Minimum consecutive successes for + the probe to be considered successful after + having failed. Defaults to 1. Must be 1 for + liveness and startup. Minimum value is 1. + format: int32 + type: integer + tcpSocket: + description: 'TCPSocket specifies an action + involving a TCP port. TCP hooks not yet supported + TODO: implement a realistic TCP lifecycle + hook' + properties: + host: + description: 'Optional: Host name to connect + to, defaults to the pod IP.' + type: string + port: + anyOf: + - type: integer + - type: string + description: Number or name of the port + to access on the container. Number must + be in the range 1 to 65535. Name must + be an IANA_SVC_NAME. + x-kubernetes-int-or-string: true + required: + - port + type: object + timeoutSeconds: + description: 'Number of seconds after which + the probe times out. Defaults to 1 second. + Minimum value is 1. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle#container-probes' + format: int32 + type: integer + type: object + stdin: + description: Whether this container should allocate + a buffer for stdin in the container runtime. If + this is not set, reads from stdin in the container + will always result in EOF. Default is false. + type: boolean + stdinOnce: + description: Whether the container runtime should + close the stdin channel after it has been opened + by a single attach. When stdin is true the stdin + stream will remain open across multiple attach + sessions. If stdinOnce is set to true, stdin is + opened on container start, is empty until the + first client attaches to stdin, and then remains + open and accepts data until the client disconnects, + at which time stdin is closed and remains closed + until the container is restarted. If this flag + is false, a container processes that reads from + stdin will never receive an EOF. Default is false + type: boolean + terminationMessagePath: + description: 'Optional: Path at which the file to + which the container''s termination message will + be written is mounted into the container''s filesystem. + Message written is intended to be brief final + status, such as an assertion failure message. + Will be truncated by the node if greater than + 4096 bytes. The total message length across all + containers will be limited to 12kb. Defaults to + /dev/termination-log. Cannot be updated.' + type: string + terminationMessagePolicy: + description: Indicate how the termination message + should be populated. File will use the contents + of terminationMessagePath to populate the container + status message on both success and failure. FallbackToLogsOnError + will use the last chunk of container log output + if the termination message file is empty and the + container exited with an error. The log output + is limited to 2048 bytes or 80 lines, whichever + is smaller. Defaults to File. Cannot be updated. + type: string + tty: + description: Whether this container should allocate + a TTY for itself, also requires 'stdin' to be + true. Default is false. + type: boolean + volumeDevices: + description: volumeDevices is the list of block + devices to be used by the container. + items: + description: volumeDevice describes a mapping + of a raw block device within a container. + properties: + devicePath: + description: devicePath is the path inside + of the container that the device will be + mapped to. + type: string + name: + description: name must match the name of a + persistentVolumeClaim in the pod + type: string + required: + - devicePath + - name + type: object + type: array + volumeMounts: + description: Pod volumes to mount into the container's + filesystem. Cannot be updated. + items: + description: VolumeMount describes a mounting + of a Volume within a container. + properties: + mountPath: + description: Path within the container at + which the volume should be mounted. Must + not contain ':'. + type: string + mountPropagation: + description: mountPropagation determines how + mounts are propagated from the host to container + and the other way around. When not set, + MountPropagationNone is used. This field + is beta in 1.10. + type: string + name: + description: This must match the Name of a + Volume. + type: string + readOnly: + description: Mounted read-only if true, read-write + otherwise (false or unspecified). Defaults + to false. + type: boolean + subPath: + description: Path within the volume from which + the container's volume should be mounted. + Defaults to "" (volume's root). + type: string + subPathExpr: + description: Expanded path within the volume + from which the container's volume should + be mounted. Behaves similarly to SubPath + but environment variable references $(VAR_NAME) + are expanded using the container's environment. + Defaults to "" (volume's root). SubPathExpr + and SubPath are mutually exclusive. + type: string + required: + - mountPath + - name + type: object + type: array + workingDir: + description: Container's working directory. If not + specified, the container runtime's default will + be used, which might be configured in the container + image. Cannot be updated. + type: string + required: + - name + type: object + type: array + nodeName: + description: NodeName is a request to schedule this pod + onto a specific node. If it is non-empty, the scheduler + simply schedules this pod onto that node, assuming that + it fits resource requirements. + type: string + nodeSelector: + additionalProperties: + type: string + description: 'NodeSelector is a selector which must be + true for the pod to fit on a node. Selector which must + match a node''s labels for the pod to be scheduled on + that node. More info: https://kubernetes.io/docs/concepts/configuration/assign-pod-node/' + type: object + overhead: + additionalProperties: + anyOf: + - type: integer + - type: string + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + description: 'Overhead represents the resource overhead + associated with running a pod for a given RuntimeClass. + This field will be autopopulated at admission time by + the RuntimeClass admission controller. If the RuntimeClass + admission controller is enabled, overhead must not be + set in Pod create requests. The RuntimeClass admission + controller will reject Pod create requests which have + the overhead already set. If RuntimeClass is configured + and selected in the PodSpec, Overhead will be set to + the value defined in the corresponding RuntimeClass, + otherwise it will remain unset and treated as zero. + More info: https://git.k8s.io/enhancements/keps/sig-node/20190226-pod-overhead.md + This field is alpha-level as of Kubernetes v1.16, and + is only honored by servers that enable the PodOverhead + feature.' + type: object + preemptionPolicy: + description: PreemptionPolicy is the Policy for preempting + pods with lower priority. One of Never, PreemptLowerPriority. + Defaults to PreemptLowerPriority if unset. This field + is alpha-level and is only honored by servers that enable + the NonPreemptingPriority feature. + type: string + priority: + description: The priority value. Various system components + use this field to find the priority of the pod. When + Priority Admission Controller is enabled, it prevents + users from setting this field. The admission controller + populates this field from PriorityClassName. The higher + the value, the higher the priority. + format: int32 + type: integer + priorityClassName: + description: If specified, indicates the pod's priority. + "system-node-critical" and "system-cluster-critical" + are two special keywords which indicate the highest + priorities with the former being the highest priority. + Any other name must be defined by creating a PriorityClass + object with that name. If not specified, the pod priority + will be default or zero if there is no default. + type: string + readinessGates: + description: 'If specified, all readiness gates will be + evaluated for pod readiness. A pod is ready when all + its containers are ready AND all conditions specified + in the readiness gates have status equal to "True" More + info: https://git.k8s.io/enhancements/keps/sig-network/0007-pod-ready%2B%2B.md' + items: + description: PodReadinessGate contains the reference + to a pod condition + properties: + conditionType: + description: ConditionType refers to a condition + in the pod's condition list with matching type. + type: string + required: + - conditionType + type: object + type: array + restartPolicy: + description: 'Restart policy for all containers within + the pod. One of Always, OnFailure, Never. Default to + Always. More info: https://kubernetes.io/docs/concepts/workloads/pods/pod-lifecycle/#restart-policy' + type: string + runtimeClassName: + description: 'RuntimeClassName refers to a RuntimeClass + object in the node.k8s.io group, which should be used + to run this pod. If no RuntimeClass resource matches + the named class, the pod will not be run. If unset or + empty, the "legacy" RuntimeClass will be used, which + is an implicit class with an empty definition that uses + the default runtime handler. More info: https://git.k8s.io/enhancements/keps/sig-node/runtime-class.md + This is a beta feature as of Kubernetes v1.14.' + type: string + schedulerName: + description: If specified, the pod will be dispatched + by specified scheduler. If not specified, the pod will + be dispatched by default scheduler. + type: string + securityContext: + description: 'SecurityContext holds pod-level security + attributes and common container settings. Optional: + Defaults to empty. See type description for default + values of each field.' + properties: + fsGroup: + description: "A special supplemental group that applies + to all containers in a pod. Some volume types allow + the Kubelet to change the ownership of that volume + to be owned by the pod: \n 1. The owning GID will + be the FSGroup 2. The setgid bit is set (new files + created in the volume will be owned by FSGroup) + 3. The permission bits are OR'd with rw-rw---- \n + If unset, the Kubelet will not modify the ownership + and permissions of any volume." + format: int64 + type: integer + fsGroupChangePolicy: + description: 'fsGroupChangePolicy defines behavior + of changing ownership and permission of the volume + before being exposed inside Pod. This field will + only apply to volume types which support fsGroup + based ownership(and permissions). It will have no + effect on ephemeral volume types such as: secret, + configmaps and emptydir. Valid values are "OnRootMismatch" + and "Always". If not specified defaults to "Always".' + type: string + runAsGroup: + description: The GID to run the entrypoint of the + container process. Uses runtime default if unset. + May also be set in SecurityContext. If set in both + SecurityContext and PodSecurityContext, the value + specified in SecurityContext takes precedence for + that container. + format: int64 + type: integer + runAsNonRoot: + description: Indicates that the container must run + as a non-root user. If true, the Kubelet will validate + the image at runtime to ensure that it does not + run as UID 0 (root) and fail to start the container + if it does. If unset or false, no such validation + will be performed. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + type: boolean + runAsUser: + description: The UID to run the entrypoint of the + container process. Defaults to user specified in + image metadata if unspecified. May also be set in + SecurityContext. If set in both SecurityContext + and PodSecurityContext, the value specified in SecurityContext + takes precedence for that container. + format: int64 + type: integer + seLinuxOptions: + description: The SELinux context to be applied to + all containers. If unspecified, the container runtime + will allocate a random SELinux context for each + container. May also be set in SecurityContext. If + set in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence + for that container. + properties: + level: + description: Level is SELinux level label that + applies to the container. + type: string + role: + description: Role is a SELinux role label that + applies to the container. + type: string + type: + description: Type is a SELinux type label that + applies to the container. + type: string + user: + description: User is a SELinux user label that + applies to the container. + type: string + type: object + supplementalGroups: + description: A list of groups applied to the first + process run in each container, in addition to the + container's primary GID. If unspecified, no groups + will be added to any container. + items: + format: int64 + type: integer + type: array + sysctls: + description: Sysctls hold a list of namespaced sysctls + used for the pod. Pods with unsupported sysctls + (by the container runtime) might fail to launch. + items: + description: Sysctl defines a kernel parameter to + be set + properties: + name: + description: Name of a property to set + type: string + value: + description: Value of a property to set + type: string + required: + - name + - value + type: object + type: array + windowsOptions: + description: The Windows specific settings applied + to all containers. If unspecified, the options within + a container's SecurityContext will be used. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes precedence. + properties: + gmsaCredentialSpec: + description: GMSACredentialSpec is where the GMSA + admission webhook (https://github.com/kubernetes-sigs/windows-gmsa) + inlines the contents of the GMSA credential + spec named by the GMSACredentialSpecName field. + type: string + gmsaCredentialSpecName: + description: GMSACredentialSpecName is the name + of the GMSA credential spec to use. + type: string + runAsUserName: + description: The UserName in Windows to run the + entrypoint of the container process. Defaults + to the user specified in image metadata if unspecified. + May also be set in PodSecurityContext. If set + in both SecurityContext and PodSecurityContext, + the value specified in SecurityContext takes + precedence. + type: string + type: object + type: object + serviceAccount: + description: 'DeprecatedServiceAccount is a depreciated + alias for ServiceAccountName. Deprecated: Use serviceAccountName + instead.' + type: string + serviceAccountName: + description: 'ServiceAccountName is the name of the ServiceAccount + to use to run this pod. More info: https://kubernetes.io/docs/tasks/configure-pod-container/configure-service-account/' + type: string + shareProcessNamespace: + description: 'Share a single process namespace between + all of the containers in a pod. When this is set containers + will be able to view and signal processes from other + containers in the same pod, and the first process in + each container will not be assigned PID 1. HostPID and + ShareProcessNamespace cannot both be set. Optional: + Default to false.' + type: boolean + subdomain: + description: If specified, the fully qualified Pod hostname + will be "...svc.". If not specified, the pod will not have a + domainname at all. + type: string + terminationGracePeriodSeconds: + description: Optional duration in seconds the pod needs + to terminate gracefully. May be decreased in delete + request. Value must be non-negative integer. The value + zero indicates delete immediately. If this value is + nil, the default grace period will be used instead. + The grace period is the duration in seconds after the + processes running in the pod are sent a termination + signal and the time when the processes are forcibly + halted with a kill signal. Set this value longer than + the expected cleanup time for your process. Defaults + to 30 seconds. + format: int64 + type: integer + tolerations: + description: If specified, the pod's tolerations. + items: + description: The pod this Toleration is attached to + tolerates any taint that matches the triple + using the matching operator . + properties: + effect: + description: Effect indicates the taint effect to + match. Empty means match all taint effects. When + specified, allowed values are NoSchedule, PreferNoSchedule + and NoExecute. + type: string + key: + description: Key is the taint key that the toleration + applies to. Empty means match all taint keys. + If the key is empty, operator must be Exists; + this combination means to match all values and + all keys. + type: string + operator: + description: Operator represents a key's relationship + to the value. Valid operators are Exists and Equal. + Defaults to Equal. Exists is equivalent to wildcard + for value, so that a pod can tolerate all taints + of a particular category. + type: string + tolerationSeconds: + description: TolerationSeconds represents the period + of time the toleration (which must be of effect + NoExecute, otherwise this field is ignored) tolerates + the taint. By default, it is not set, which means + tolerate the taint forever (do not evict). Zero + and negative values will be treated as 0 (evict + immediately) by the system. + format: int64 + type: integer + value: + description: Value is the taint value the toleration + matches to. If the operator is Exists, the value + should be empty, otherwise just a regular string. + type: string + type: object + type: array + topologySpreadConstraints: + description: TopologySpreadConstraints describes how a + group of pods ought to spread across topology domains. + Scheduler will schedule pods in a way which abides by + the constraints. This field is only honored by clusters + that enable the EvenPodsSpread feature. All topologySpreadConstraints + are ANDed. + items: + description: TopologySpreadConstraint specifies how + to spread matching pods among the given topology. + properties: + labelSelector: + description: LabelSelector is used to find matching + pods. Pods that match this label selector are + counted to determine the number of pods in their + corresponding topology domain. + properties: + matchExpressions: + description: matchExpressions is a list of label + selector requirements. The requirements are + ANDed. + items: + description: A label selector requirement + is a selector that contains values, a key, + and an operator that relates the key and + values. + properties: + key: + description: key is the label key that + the selector applies to. + type: string + operator: + description: operator represents a key's + relationship to a set of values. Valid + operators are In, NotIn, Exists and + DoesNotExist. + type: string + values: + description: values is an array of string + values. If the operator is In or NotIn, + the values array must be non-empty. + If the operator is Exists or DoesNotExist, + the values array must be empty. This + array is replaced during a strategic + merge patch. + items: + type: string + type: array + required: + - key + - operator + type: object + type: array + matchLabels: + additionalProperties: + type: string + description: matchLabels is a map of {key,value} + pairs. A single {key,value} in the matchLabels + map is equivalent to an element of matchExpressions, + whose key field is "key", the operator is + "In", and the values array contains only "value". + The requirements are ANDed. + type: object + type: object + maxSkew: + description: 'MaxSkew describes the degree to which + pods may be unevenly distributed. It''s the maximum + permitted difference between the number of matching + pods in any two topology domains of a given topology + type. For example, in a 3-zone cluster, MaxSkew + is set to 1, and pods with the same labelSelector + spread as 1/1/0: | zone1 | zone2 | zone3 | | P | P | | + - if MaxSkew is 1, incoming pod can only be scheduled + to zone3 to become 1/1/1; scheduling it onto zone1(zone2) + would make the ActualSkew(2-0) on zone1(zone2) + violate MaxSkew(1). - if MaxSkew is 2, incoming + pod can be scheduled onto any zone. It''s a required + field. Default value is 1 and 0 is not allowed.' + format: int32 + type: integer + topologyKey: + description: TopologyKey is the key of node labels. + Nodes that have a label with this key and identical + values are considered to be in the same topology. + We consider each as a "bucket", and + try to put balanced number of pods into each bucket. + It's a required field. + type: string + whenUnsatisfiable: + description: 'WhenUnsatisfiable indicates how to + deal with a pod if it doesn''t satisfy the spread + constraint. - DoNotSchedule (default) tells the + scheduler not to schedule it - ScheduleAnyway + tells the scheduler to still schedule it It''s + considered as "Unsatisfiable" if and only if placing + incoming pod on any topology violates "MaxSkew". + For example, in a 3-zone cluster, MaxSkew is set + to 1, and pods with the same labelSelector spread + as 3/1/1: | zone1 | zone2 | zone3 | | P P P | P | P | + If WhenUnsatisfiable is set to DoNotSchedule, + incoming pod can only be scheduled to zone2(zone3) + to become 3/2/1(3/1/2) as ActualSkew(2-1) on zone2(zone3) + satisfies MaxSkew(1). In other words, the cluster + can still be imbalanced, but scheduler won''t + make it *more* imbalanced. It''s a required field.' + type: string + required: + - maxSkew + - topologyKey + - whenUnsatisfiable + type: object + type: array + x-kubernetes-list-map-keys: + - topologyKey + - whenUnsatisfiable + x-kubernetes-list-type: map + volumes: + description: 'List of volumes that can be mounted by containers + belonging to the pod. More info: https://kubernetes.io/docs/concepts/storage/volumes' + items: + description: Volume represents a named volume in a pod + that may be accessed by any container in the pod. + properties: + awsElasticBlockStore: + description: 'AWSElasticBlockStore represents an + AWS Disk resource that is attached to a kubelet''s + host machine and then exposed to the pod. More + info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + properties: + fsType: + description: 'Filesystem type of the volume + that you want to mount. Tip: Ensure that the + filesystem type is supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that + you want to mount. If omitted, the default + is to mount by volume name. Examples: For + volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property + empty).' + format: int32 + type: integer + readOnly: + description: 'Specify "true" to force and set + the ReadOnly property in VolumeMounts to "true". + If omitted, the default is "false". More info: + https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: boolean + volumeID: + description: 'Unique ID of the persistent disk + resource in AWS (Amazon EBS volume). More + info: https://kubernetes.io/docs/concepts/storage/volumes#awselasticblockstore' + type: string + required: + - volumeID + type: object + azureDisk: + description: AzureDisk represents an Azure Data + Disk mount on the host and bind mount to the pod. + properties: + cachingMode: + description: 'Host Caching mode: None, Read + Only, Read Write.' + type: string + diskName: + description: The Name of the data disk in the + blob storage + type: string + diskURI: + description: The URI the data disk in the blob + storage + type: string + fsType: + description: Filesystem type to mount. Must + be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. + type: string + kind: + description: 'Expected values Shared: multiple + blob disks per storage account Dedicated: + single blob disk per storage account Managed: + azure managed data disk (only in managed availability + set). defaults to shared' + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. + type: boolean + required: + - diskName + - diskURI + type: object + azureFile: + description: AzureFile represents an Azure File + Service mount on the host and bind mount to the + pod. + properties: + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. + type: boolean + secretName: + description: the name of secret that contains + Azure Storage Account Name and Key + type: string + shareName: + description: Share Name + type: string + required: + - secretName + - shareName + type: object + cephfs: + description: CephFS represents a Ceph FS mount on + the host that shares a pod's lifetime + properties: + monitors: + description: 'Required: Monitors is a collection + of Ceph monitors More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + items: + type: string + type: array + path: + description: 'Optional: Used as the mounted + root, rather than the full Ceph tree, default + is /' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: boolean + secretFile: + description: 'Optional: SecretFile is the path + to key ring for User, default is /etc/ceph/user.secret + More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + secretRef: + description: 'Optional: SecretRef is reference + to the authentication secret for User, default + is empty. More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'Optional: User is the rados user + name, default is admin More info: https://examples.k8s.io/volumes/cephfs/README.md#how-to-use-it' + type: string + required: + - monitors + type: object + cinder: + description: 'Cinder represents a cinder volume + attached and mounted on kubelets host machine. + More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + properties: + fsType: + description: 'Filesystem type to mount. Must + be a filesystem type supported by the host + operating system. Examples: "ext4", "xfs", + "ntfs". Implicitly inferred to be "ext4" if + unspecified. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: boolean + secretRef: + description: 'Optional: points to a secret object + containing parameters used to connect to OpenStack.' + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeID: + description: 'volume id used to identify the + volume in cinder. More info: https://examples.k8s.io/mysql-cinder-pd/README.md' + type: string + required: + - volumeID + type: object + configMap: + description: ConfigMap represents a configMap that + should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on + created files by default. Must be a value + between 0 and 0777. Defaults to 0644. Directories + within the path are not affected by this setting. + This might be in conflict with other options + that affect the file mode, like fsGroup, and + the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value + pair in the Data field of the referenced ConfigMap + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the ConfigMap, the + volume setup will error unless it is marked + optional. Paths must be relative and may not + contain the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of the + file to map the key to. May not be an + absolute path. May not contain the path + element '..'. May not start with the + string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. More info: + https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap or + its keys must be defined + type: boolean + type: object + csi: + description: CSI (Container Storage Interface) represents + storage that is handled by an external CSI driver + (Alpha feature). + properties: + driver: + description: Driver is the name of the CSI driver + that handles this volume. Consult with your + admin for the correct name as registered in + the cluster. + type: string + fsType: + description: Filesystem type to mount. Ex. "ext4", + "xfs", "ntfs". If not provided, the empty + value is passed to the associated CSI driver + which will determine the default filesystem + to apply. + type: string + nodePublishSecretRef: + description: NodePublishSecretRef is a reference + to the secret object containing sensitive + information to pass to the CSI driver to complete + the CSI NodePublishVolume and NodeUnpublishVolume + calls. This field is optional, and may be + empty if no secret is required. If the secret + object contains more than one secret, all + secret references are passed. + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + readOnly: + description: Specifies a read-only configuration + for the volume. Defaults to false (read/write). + type: boolean + volumeAttributes: + additionalProperties: + type: string + description: VolumeAttributes stores driver-specific + properties that are passed to the CSI driver. + Consult your driver's documentation for supported + values. + type: object + required: + - driver + type: object + downwardAPI: + description: DownwardAPI represents downward API + about the pod that should populate this volume + properties: + defaultMode: + description: 'Optional: mode bits to use on + created files by default. Must be a value + between 0 and 0777. Defaults to 0644. Directories + within the path are not affected by this setting. + This might be in conflict with other options + that affect the file mode, like fsGroup, and + the result can be other mode bits set.' + format: int32 + type: integer + items: + description: Items is a list of downward API + volume file + items: + description: DownwardAPIVolumeFile represents + information to create the file containing + the pod field + properties: + fieldRef: + description: 'Required: Selects a field + of the pod: only annotations, labels, + name and namespace are supported.' + properties: + apiVersion: + description: Version of the schema + the FieldPath is written in terms + of, defaults to "v1". + type: string + fieldPath: + description: Path of the field to + select in the specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: 'Required: Path is the relative + path name of the file to be created. + Must not be absolute or contain the + ''..'' path. Must be utf-8 encoded. + The first item of the relative path + must not start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource of the + container: only resources limits and + requests (limits.cpu, limits.memory, + requests.cpu and requests.memory) are + currently supported.' + properties: + containerName: + description: 'Container name: required + for volumes, optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the output + format of the exposed resources, + defaults to "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: resource to + select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + emptyDir: + description: 'EmptyDir represents a temporary directory + that shares a pod''s lifetime. More info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + properties: + medium: + description: 'What type of storage medium should + back this directory. The default is "" which + means to use the node''s default medium. Must + be an empty string (default) or Memory. More + info: https://kubernetes.io/docs/concepts/storage/volumes#emptydir' + type: string + sizeLimit: + anyOf: + - type: integer + - type: string + description: 'Total amount of local storage + required for this EmptyDir volume. The size + limit is also applicable for memory medium. + The maximum usage on memory medium EmptyDir + would be the minimum value between the SizeLimit + specified here and the sum of memory limits + of all containers in a pod. The default is + nil which means that the limit is undefined. + More info: http://kubernetes.io/docs/user-guide/volumes#emptydir' + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + type: object + fc: + description: FC represents a Fibre Channel resource + that is attached to a kubelet's host machine and + then exposed to the pod. + properties: + fsType: + description: 'Filesystem type to mount. Must + be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + lun: + description: 'Optional: FC target lun number' + format: int32 + type: integer + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + targetWWNs: + description: 'Optional: FC target worldwide + names (WWNs)' + items: + type: string + type: array + wwids: + description: 'Optional: FC volume world wide + identifiers (wwids) Either wwids or combination + of targetWWNs and lun must be set, but not + both simultaneously.' + items: + type: string + type: array + type: object + flexVolume: + description: FlexVolume represents a generic volume + resource that is provisioned/attached using an + exec based plugin. + properties: + driver: + description: Driver is the name of the driver + to use for this volume. + type: string + fsType: + description: Filesystem type to mount. Must + be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs", "ntfs". + The default filesystem depends on FlexVolume + script. + type: string + options: + additionalProperties: + type: string + description: 'Optional: Extra command options + if any.' + type: object + readOnly: + description: 'Optional: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts.' + type: boolean + secretRef: + description: 'Optional: SecretRef is reference + to the secret object containing sensitive + information to pass to the plugin scripts. + This may be empty if no secret object is specified. + If the secret object contains more than one + secret, all secrets are passed to the plugin + scripts.' + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + required: + - driver + type: object + flocker: + description: Flocker represents a Flocker volume + attached to a kubelet's host machine. This depends + on the Flocker control service being running + properties: + datasetName: + description: Name of the dataset stored as metadata + -> name on the dataset for Flocker should + be considered as deprecated + type: string + datasetUUID: + description: UUID of the dataset. This is unique + identifier of a Flocker dataset + type: string + type: object + gcePersistentDisk: + description: 'GCEPersistentDisk represents a GCE + Disk resource that is attached to a kubelet''s + host machine and then exposed to the pod. More + info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + properties: + fsType: + description: 'Filesystem type of the volume + that you want to mount. Tip: Ensure that the + filesystem type is supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + partition: + description: 'The partition in the volume that + you want to mount. If omitted, the default + is to mount by volume name. Examples: For + volume /dev/sda1, you specify the partition + as "1". Similarly, the volume partition for + /dev/sda is "0" (or you can leave the property + empty). More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + format: int32 + type: integer + pdName: + description: 'Unique name of the PD resource + in GCE. Used to identify the disk in GCE. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + More info: https://kubernetes.io/docs/concepts/storage/volumes#gcepersistentdisk' + type: boolean + required: + - pdName + type: object + gitRepo: + description: 'GitRepo represents a git repository + at a particular revision. DEPRECATED: GitRepo + is deprecated. To provision a container with a + git repo, mount an EmptyDir into an InitContainer + that clones the repo using git, then mount the + EmptyDir into the Pod''s container.' + properties: + directory: + description: Target directory name. Must not + contain or start with '..'. If '.' is supplied, + the volume directory will be the git repository. Otherwise, + if specified, the volume will contain the + git repository in the subdirectory with the + given name. + type: string + repository: + description: Repository URL + type: string + revision: + description: Commit hash for the specified revision. + type: string + required: + - repository + type: object + glusterfs: + description: 'Glusterfs represents a Glusterfs mount + on the host that shares a pod''s lifetime. More + info: https://examples.k8s.io/volumes/glusterfs/README.md' + properties: + endpoints: + description: 'EndpointsName is the endpoint + name that details Glusterfs topology. More + info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + path: + description: 'Path is the Glusterfs volume path. + More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: string + readOnly: + description: 'ReadOnly here will force the Glusterfs + volume to be mounted with read-only permissions. + Defaults to false. More info: https://examples.k8s.io/volumes/glusterfs/README.md#create-a-pod' + type: boolean + required: + - endpoints + - path + type: object + hostPath: + description: 'HostPath represents a pre-existing + file or directory on the host machine that is + directly exposed to the container. This is generally + used for system agents or other privileged things + that are allowed to see the host machine. Most + containers will NOT need this. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath + --- TODO(jonesdl) We need to restrict who can + use host directory mounts and who can/can not + mount host directories as read/write.' + properties: + path: + description: 'Path of the directory on the host. + If the path is a symlink, it will follow the + link to the real path. More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + type: + description: 'Type for HostPath Volume Defaults + to "" More info: https://kubernetes.io/docs/concepts/storage/volumes#hostpath' + type: string + required: + - path + type: object + iscsi: + description: 'ISCSI represents an ISCSI Disk resource + that is attached to a kubelet''s host machine + and then exposed to the pod. More info: https://examples.k8s.io/volumes/iscsi/README.md' + properties: + chapAuthDiscovery: + description: whether support iSCSI Discovery + CHAP authentication + type: boolean + chapAuthSession: + description: whether support iSCSI Session CHAP + authentication + type: boolean + fsType: + description: 'Filesystem type of the volume + that you want to mount. Tip: Ensure that the + filesystem type is supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#iscsi + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + initiatorName: + description: Custom iSCSI Initiator Name. If + initiatorName is specified with iscsiInterface + simultaneously, new iSCSI interface : will be created for + the connection. + type: string + iqn: + description: Target iSCSI Qualified Name. + type: string + iscsiInterface: + description: iSCSI Interface Name that uses + an iSCSI transport. Defaults to 'default' + (tcp). + type: string + lun: + description: iSCSI Target Lun number. + format: int32 + type: integer + portals: + description: iSCSI Target Portal List. The portal + is either an IP or ip_addr:port if the port + is other than default (typically TCP ports + 860 and 3260). + items: + type: string + type: array + readOnly: + description: ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + type: boolean + secretRef: + description: CHAP Secret for iSCSI target and + initiator authentication + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + targetPortal: + description: iSCSI Target Portal. The Portal + is either an IP or ip_addr:port if the port + is other than default (typically TCP ports + 860 and 3260). + type: string + required: + - iqn + - lun + - targetPortal + type: object + name: + description: 'Volume''s name. Must be a DNS_LABEL + and unique within the pod. More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names' + type: string + nfs: + description: 'NFS represents an NFS mount on the + host that shares a pod''s lifetime More info: + https://kubernetes.io/docs/concepts/storage/volumes#nfs' + properties: + path: + description: 'Path that is exported by the NFS + server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + readOnly: + description: 'ReadOnly here will force the NFS + export to be mounted with read-only permissions. + Defaults to false. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: boolean + server: + description: 'Server is the hostname or IP address + of the NFS server. More info: https://kubernetes.io/docs/concepts/storage/volumes#nfs' + type: string + required: + - path + - server + type: object + persistentVolumeClaim: + description: 'PersistentVolumeClaimVolumeSource + represents a reference to a PersistentVolumeClaim + in the same namespace. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + properties: + claimName: + description: 'ClaimName is the name of a PersistentVolumeClaim + in the same namespace as the pod using this + volume. More info: https://kubernetes.io/docs/concepts/storage/persistent-volumes#persistentvolumeclaims' + type: string + readOnly: + description: Will force the ReadOnly setting + in VolumeMounts. Default false. + type: boolean + required: + - claimName + type: object + photonPersistentDisk: + description: PhotonPersistentDisk represents a PhotonController + persistent disk attached and mounted on kubelets + host machine + properties: + fsType: + description: Filesystem type to mount. Must + be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. + type: string + pdID: + description: ID that identifies Photon Controller + persistent disk + type: string + required: + - pdID + type: object + portworxVolume: + description: PortworxVolume represents a portworx + volume attached and mounted on kubelets host machine + properties: + fsType: + description: FSType represents the filesystem + type to mount Must be a filesystem type supported + by the host operating system. Ex. "ext4", + "xfs". Implicitly inferred to be "ext4" if + unspecified. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. + type: boolean + volumeID: + description: VolumeID uniquely identifies a + Portworx volume + type: string + required: + - volumeID + type: object + projected: + description: Items for all in one resources secrets, + configmaps, and downward API + properties: + defaultMode: + description: Mode bits to use on created files + by default. Must be a value between 0 and + 0777. Directories within the path are not + affected by this setting. This might be in + conflict with other options that affect the + file mode, like fsGroup, and the result can + be other mode bits set. + format: int32 + type: integer + sources: + description: list of volume projections + items: + description: Projection that may be projected + along with other supported volume types + properties: + configMap: + description: information about the configMap + data to project + properties: + items: + description: If unspecified, each + key-value pair in the Data field + of the referenced ConfigMap will + be projected into the volume as + a file whose name is the key and + content is the value. If specified, + the listed keys will be projected + into the specified paths, and unlisted + keys will not be present. If a key + is specified which is not present + in the ConfigMap, the volume setup + will error unless it is marked optional. + Paths must be relative and may not + contain the '..' path or start with + '..'. + items: + description: Maps a string key to + a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between 0 + and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path + of the file to map the key + to. May not be an absolute + path. May not contain the + path element '..'. May not + start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the ConfigMap + or its keys must be defined + type: boolean + type: object + downwardAPI: + description: information about the downwardAPI + data to project + properties: + items: + description: Items is a list of DownwardAPIVolume + file + items: + description: DownwardAPIVolumeFile + represents information to create + the file containing the pod field + properties: + fieldRef: + description: 'Required: Selects + a field of the pod: only annotations, + labels, name and namespace + are supported.' + properties: + apiVersion: + description: Version of + the schema the FieldPath + is written in terms of, + defaults to "v1". + type: string + fieldPath: + description: Path of the + field to select in the + specified API version. + type: string + required: + - fieldPath + type: object + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between 0 + and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: 'Required: Path + is the relative path name + of the file to be created. + Must not be absolute or contain + the ''..'' path. Must be utf-8 + encoded. The first item of + the relative path must not + start with ''..''' + type: string + resourceFieldRef: + description: 'Selects a resource + of the container: only resources + limits and requests (limits.cpu, + limits.memory, requests.cpu + and requests.memory) are currently + supported.' + properties: + containerName: + description: 'Container + name: required for volumes, + optional for env vars' + type: string + divisor: + anyOf: + - type: integer + - type: string + description: Specifies the + output format of the exposed + resources, defaults to + "1" + pattern: ^(\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))(([KMGTPE]i)|[numkMGTPE]|([eE](\+|-)?(([0-9]+(\.[0-9]*)?)|(\.[0-9]+))))?$ + x-kubernetes-int-or-string: true + resource: + description: 'Required: + resource to select' + type: string + required: + - resource + type: object + required: + - path + type: object + type: array + type: object + secret: + description: information about the secret + data to project + properties: + items: + description: If unspecified, each + key-value pair in the Data field + of the referenced Secret will be + projected into the volume as a file + whose name is the key and content + is the value. If specified, the + listed keys will be projected into + the specified paths, and unlisted + keys will not be present. If a key + is specified which is not present + in the Secret, the volume setup + will error unless it is marked optional. + Paths must be relative and may not + contain the '..' path or start with + '..'. + items: + description: Maps a string key to + a path within a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode + bits to use on this file, + must be a value between 0 + and 0777. If not specified, + the volume defaultMode will + be used. This might be in + conflict with other options + that affect the file mode, + like fsGroup, and the result + can be other mode bits set.' + format: int32 + type: integer + path: + description: The relative path + of the file to map the key + to. May not be an absolute + path. May not contain the + path element '..'. May not + start with the string '..'. + type: string + required: + - key + - path + type: object + type: array + name: + description: 'Name of the referent. + More info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + optional: + description: Specify whether the Secret + or its key must be defined + type: boolean + type: object + serviceAccountToken: + description: information about the serviceAccountToken + data to project + properties: + audience: + description: Audience is the intended + audience of the token. A recipient + of a token must identify itself + with an identifier specified in + the audience of the token, and otherwise + should reject the token. The audience + defaults to the identifier of the + apiserver. + type: string + expirationSeconds: + description: ExpirationSeconds is + the requested duration of validity + of the service account token. As + the token approaches expiration, + the kubelet volume plugin will proactively + rotate the service account token. + The kubelet will start trying to + rotate the token if the token is + older than 80 percent of its time + to live or if the token is older + than 24 hours.Defaults to 1 hour + and must be at least 10 minutes. + format: int64 + type: integer + path: + description: Path is the path relative + to the mount point of the file to + project the token into. + type: string + required: + - path + type: object + type: object + type: array + required: + - sources + type: object + quobyte: + description: Quobyte represents a Quobyte mount + on the host that shares a pod's lifetime + properties: + group: + description: Group to map volume access to Default + is no group + type: string + readOnly: + description: ReadOnly here will force the Quobyte + volume to be mounted with read-only permissions. + Defaults to false. + type: boolean + registry: + description: Registry represents a single or + multiple Quobyte Registry services specified + as a string as host:port pair (multiple entries + are separated with commas) which acts as the + central registry for volumes + type: string + tenant: + description: Tenant owning the given Quobyte + volume in the Backend Used with dynamically + provisioned Quobyte volumes, value is set + by the plugin + type: string + user: + description: User to map volume access to Defaults + to serivceaccount user + type: string + volume: + description: Volume is a string that references + an already created Quobyte volume by name. + type: string + required: + - registry + - volume + type: object + rbd: + description: 'RBD represents a Rados Block Device + mount on the host that shares a pod''s lifetime. + More info: https://examples.k8s.io/volumes/rbd/README.md' + properties: + fsType: + description: 'Filesystem type of the volume + that you want to mount. Tip: Ensure that the + filesystem type is supported by the host operating + system. Examples: "ext4", "xfs", "ntfs". Implicitly + inferred to be "ext4" if unspecified. More + info: https://kubernetes.io/docs/concepts/storage/volumes#rbd + TODO: how do we prevent errors in the filesystem + from compromising the machine' + type: string + image: + description: 'The rados image name. More info: + https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + keyring: + description: 'Keyring is the path to key ring + for RBDUser. Default is /etc/ceph/keyring. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + monitors: + description: 'A collection of Ceph monitors. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + items: + type: string + type: array + pool: + description: 'The rados pool name. Default is + rbd. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + readOnly: + description: 'ReadOnly here will force the ReadOnly + setting in VolumeMounts. Defaults to false. + More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: boolean + secretRef: + description: 'SecretRef is name of the authentication + secret for RBDUser. If provided overrides + keyring. Default is nil. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + user: + description: 'The rados user name. Default is + admin. More info: https://examples.k8s.io/volumes/rbd/README.md#how-to-use-it' + type: string + required: + - image + - monitors + type: object + scaleIO: + description: ScaleIO represents a ScaleIO persistent + volume attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must + be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs", "ntfs". + Default is "xfs". + type: string + gateway: + description: The host address of the ScaleIO + API Gateway. + type: string + protectionDomain: + description: The name of the ScaleIO Protection + Domain for the configured storage. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. + type: boolean + secretRef: + description: SecretRef references to the secret + for ScaleIO user and other sensitive information. + If this is not provided, Login operation will + fail. + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + sslEnabled: + description: Flag to enable/disable SSL communication + with Gateway, default false + type: boolean + storageMode: + description: Indicates whether the storage for + a volume should be ThickProvisioned or ThinProvisioned. + Default is ThinProvisioned. + type: string + storagePool: + description: The ScaleIO Storage Pool associated + with the protection domain. + type: string + system: + description: The name of the storage system + as configured in ScaleIO. + type: string + volumeName: + description: The name of a volume already created + in the ScaleIO system that is associated with + this volume source. + type: string + required: + - gateway + - secretRef + - system + type: object + secret: + description: 'Secret represents a secret that should + populate this volume. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + properties: + defaultMode: + description: 'Optional: mode bits to use on + created files by default. Must be a value + between 0 and 0777. Defaults to 0644. Directories + within the path are not affected by this setting. + This might be in conflict with other options + that affect the file mode, like fsGroup, and + the result can be other mode bits set.' + format: int32 + type: integer + items: + description: If unspecified, each key-value + pair in the Data field of the referenced Secret + will be projected into the volume as a file + whose name is the key and content is the value. + If specified, the listed keys will be projected + into the specified paths, and unlisted keys + will not be present. If a key is specified + which is not present in the Secret, the volume + setup will error unless it is marked optional. + Paths must be relative and may not contain + the '..' path or start with '..'. + items: + description: Maps a string key to a path within + a volume. + properties: + key: + description: The key to project. + type: string + mode: + description: 'Optional: mode bits to use + on this file, must be a value between + 0 and 0777. If not specified, the volume + defaultMode will be used. This might + be in conflict with other options that + affect the file mode, like fsGroup, + and the result can be other mode bits + set.' + format: int32 + type: integer + path: + description: The relative path of the + file to map the key to. May not be an + absolute path. May not contain the path + element '..'. May not start with the + string '..'. + type: string + required: + - key + - path + type: object + type: array + optional: + description: Specify whether the Secret or its + keys must be defined + type: boolean + secretName: + description: 'Name of the secret in the pod''s + namespace to use. More info: https://kubernetes.io/docs/concepts/storage/volumes#secret' + type: string + type: object + storageos: + description: StorageOS represents a StorageOS volume + attached and mounted on Kubernetes nodes. + properties: + fsType: + description: Filesystem type to mount. Must + be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. + type: string + readOnly: + description: Defaults to false (read/write). + ReadOnly here will force the ReadOnly setting + in VolumeMounts. + type: boolean + secretRef: + description: SecretRef specifies the secret + to use for obtaining the StorageOS API credentials. If + not specified, default values will be attempted. + properties: + name: + description: 'Name of the referent. More + info: https://kubernetes.io/docs/concepts/overview/working-with-objects/names/#names + TODO: Add other useful fields. apiVersion, + kind, uid?' + type: string + type: object + volumeName: + description: VolumeName is the human-readable + name of the StorageOS volume. Volume names + are only unique within a namespace. + type: string + volumeNamespace: + description: VolumeNamespace specifies the scope + of the volume within StorageOS. If no namespace + is specified then the Pod's namespace will + be used. This allows the Kubernetes name + scoping to be mirrored within StorageOS for + tighter integration. Set VolumeName to any + name to override the default behaviour. Set + to "default" if you are not using namespaces + within StorageOS. Namespaces that do not pre-exist + within StorageOS will be created. + type: string + type: object + vsphereVolume: + description: VsphereVolume represents a vSphere + volume attached and mounted on kubelets host machine + properties: + fsType: + description: Filesystem type to mount. Must + be a filesystem type supported by the host + operating system. Ex. "ext4", "xfs", "ntfs". + Implicitly inferred to be "ext4" if unspecified. + type: string + storagePolicyID: + description: Storage Policy Based Management + (SPBM) profile ID associated with the StoragePolicyName. + type: string + storagePolicyName: + description: Storage Policy Based Management + (SPBM) profile name. + type: string + volumePath: + description: Path that identifies vSphere volume + vmdk + type: string + required: + - volumePath + type: object + required: + - name + type: object + type: array + required: + - containers + type: object + type: object + type: object + powDifficultySeconds: + default: 0 + description: The quantity of seconds of the proof of work + type: integer + replicas: + default: 1 + description: The desired quantity of replicas if horizontal pod autoscaler + is disabled + format: int32 + type: integer + required: + - healthcheck + type: object + status: + description: ChallengeStatus defines the observed state of Challenge + properties: + health: + default: disabled + description: Shows healthcheck returns + type: string + status: + default: up-to-date + description: 'Important: Run "operator-sdk generate k8s" to regenerate + code after modifying this file Add custom validation using kubebuilder + tags: https://book-v1.book.kubebuilder.io/beyond_basics/generating_crd.html + Says if the challenge is up to date or being updated' + type: string + type: object + type: object + served: true + storage: true + subresources: + status: {} diff --git a/vrp/kctf/resources/operator.yaml b/vrp/kctf/resources/operator.yaml new file mode 100644 index 000000000..9cb7a02e3 --- /dev/null +++ b/vrp/kctf/resources/operator.yaml @@ -0,0 +1,40 @@ +apiVersion: apps/v1 +kind: Deployment +metadata: + name: kctf-operator + namespace: kctf-system +spec: + replicas: 1 + selector: + matchLabels: + name: kctf-operator + template: + metadata: + labels: + name: kctf-operator + spec: + serviceAccountName: kctf-operator + containers: + - name: kctf-operator + image: gcr.io/kctf-docker/kctf-operator@sha256:d9cebd766fbdbe898add623a2b3dc1c2574096d64d3b8d88bfd04a75378aea63 + command: + - kctf-operator + imagePullPolicy: Always + env: + - name: WATCH_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: OPERATOR_NAME + value: "kctf-operator" + readinessProbe: + exec: + command: + - cat + - /tmp/initialized + initialDelaySeconds: 5 + periodSeconds: 5 diff --git a/vrp/kctf/resources/rbac.yaml b/vrp/kctf/resources/rbac.yaml new file mode 100644 index 000000000..cd20d6290 --- /dev/null +++ b/vrp/kctf/resources/rbac.yaml @@ -0,0 +1,35 @@ +apiVersion: v1 +kind: ServiceAccount +metadata: + name: kctf-operator + namespace: kctf-system + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRoleBinding +metadata: + name: kctf-operator +subjects: +- kind: ServiceAccount + name: kctf-operator + namespace: kctf-system +roleRef: + kind: ClusterRole + name: kctf-operator + apiGroup: rbac.authorization.k8s.io + +--- + +apiVersion: rbac.authorization.k8s.io/v1 +kind: ClusterRole +metadata: + creationTimestamp: null + name: kctf-operator +rules: +- apiGroups: + - "*" + resources: + - "*" + verbs: + - "*" diff --git a/vrp/level1/challenge.yaml b/vrp/level1/challenge.yaml new file mode 100644 index 000000000..5c22be498 --- /dev/null +++ b/vrp/level1/challenge.yaml @@ -0,0 +1,26 @@ +apiVersion: kctf.dev/v1 +kind: Challenge +metadata: + name: kctf +spec: + deployed: true + powDifficultySeconds: 1 + network: + public: true + healthcheck: + enabled: true + podTemplate: + template: + spec: + containers: + - name: challenge + volumeMounts: + - mountPath: /flag + name: flag + readOnly: true + volumes: + - name: flag + secret: + defaultMode: 0555 + secretName: kctf-flag + optional: true diff --git a/vrp/level1/challenge/Dockerfile b/vrp/level1/challenge/Dockerfile new file mode 100644 index 000000000..262078bb2 --- /dev/null +++ b/vrp/level1/challenge/Dockerfile @@ -0,0 +1,30 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM ubuntu:20.04 as chroot + +RUN /usr/sbin/useradd --no-create-home -u 1000 user + +COPY chal /home/user/ + +FROM gcr.io/kctf-docker/challenge@sha256:e550af5df266cb89a26ace1ba5dcc685981f01d1cb61e45a898cce0c9753de7a + +COPY --from=chroot / /chroot + +COPY nsjail.cfg /home/user/ + +CMD kctf_setup && \ + kctf_drop_privs \ + socat \ + TCP-LISTEN:1337,reuseaddr,fork \ + EXEC:"kctf_pow nsjail --config /home/user/nsjail.cfg -- /home/user/chal" diff --git a/vrp/level1/challenge/chal b/vrp/level1/challenge/chal new file mode 100755 index 000000000..200ffe0ab --- /dev/null +++ b/vrp/level1/challenge/chal @@ -0,0 +1,3 @@ +#!/bin/bash + +exec /bin/bash diff --git a/vrp/level1/challenge/nsjail.cfg b/vrp/level1/challenge/nsjail.cfg new file mode 100644 index 000000000..4b86fdbb3 --- /dev/null +++ b/vrp/level1/challenge/nsjail.cfg @@ -0,0 +1,50 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# See options available at https://github.com/google/nsjail/blob/master/config.proto + +name: "default-nsjail-configuration" +description: "Default nsjail configuration for pwnable-style CTF task." + +mode: ONCE +clone_newnet: false +uidmap {inside_id: "1000"} +gidmap {inside_id: "1000"} +rlimit_as_type: HARD +rlimit_nofile_type: HARD +rlimit_nproc_type: HARD +rlimit_cpu: 30 +max_cpus: 1 +mount: [ + { + src: "/chroot" + dst: "/" + is_bind: true + }, + { + src: "/etc/resolv.conf" + dst: "/etc/resolv.conf" + is_bind: true + }, + { + dst: "/tmp" + fstype: "tmpfs" + rw: true + }, + { + dst: "/proc" + fstype: "proc" + rw: true + } +] diff --git a/vrp/level1/healthcheck/Dockerfile b/vrp/level1/healthcheck/Dockerfile new file mode 100644 index 000000000..da6a11676 --- /dev/null +++ b/vrp/level1/healthcheck/Dockerfile @@ -0,0 +1,21 @@ +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +FROM gcr.io/kctf-docker/healthcheck@sha256:06c6f051583b84d8dc4d77962256b7d1f1f247f405972e0649c821837b66c894 + +COPY healthz.py /home/user/ +COPY doit.py /home/user/ +COPY run.sh /home/user/ +COPY env /home/user/ + +CMD kctf_drop_privs /home/user/run.sh & /home/user/healthz.py diff --git a/vrp/level1/healthcheck/doit.py b/vrp/level1/healthcheck/doit.py new file mode 100755 index 000000000..63e5a83a9 --- /dev/null +++ b/vrp/level1/healthcheck/doit.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import pwnlib + +def handle_pow(r): + print(r.recvuntil(b'python3 ')) + print(r.recvuntil(b' solve ')) + challenge = r.recvline().decode('ascii').strip() + p = pwnlib.tubes.process.process(['kctf_bypass_pow', challenge]) + solution = p.readall().strip() + r.sendline(solution) + print(r.recvuntil(b'Correct\n')) + +r = pwnlib.tubes.remote.remote('127.0.0.1', 1337) +print(r.recvuntil('== proof-of-work: ')) +if r.recvline().startswith(b'enabled'): + handle_pow(r) + +r.sendline(b"echo $'Hello \\x57orld'") +print(r.recvuntil(b'Hello World')) + +exit(0) diff --git a/vrp/level1/healthcheck/env b/vrp/level1/healthcheck/env new file mode 100644 index 000000000..405750dc3 --- /dev/null +++ b/vrp/level1/healthcheck/env @@ -0,0 +1,2 @@ +TIMEOUT=20 +PERIOD=30 diff --git a/vrp/level1/healthcheck/healthz.py b/vrp/level1/healthcheck/healthz.py new file mode 100755 index 000000000..62cf01986 --- /dev/null +++ b/vrp/level1/healthcheck/healthz.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +import http.server + +class HealthzHandler(http.server.BaseHTTPRequestHandler): + def do_GET(self): + if self.path != '/healthz': + self.send_response(404) + self.send_header("Content-length", "0") + self.end_headers() + return + + content = b'err' + try: + with open('/tmp/healthz', 'rb') as fd: + content = fd.read().strip() + except: + pass + self.send_response(200 if content == b'ok' else 400) + self.send_header("Content-type", "text/plain") + self.send_header("Content-length", str(len(content))) + self.end_headers() + self.wfile.write(content) + +httpd = http.server.HTTPServer(('', 45281), HealthzHandler) +httpd.serve_forever() diff --git a/vrp/level1/healthcheck/run.sh b/vrp/level1/healthcheck/run.sh new file mode 100755 index 000000000..f4ed2f496 --- /dev/null +++ b/vrp/level1/healthcheck/run.sh @@ -0,0 +1,33 @@ +#!/bin/bash +# Copyright 2020 Google LLC +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +set -Eeuo pipefail + +TIMEOUT=20 +PERIOD=30 + +export TERM=linux +export TERMINFO=/etc/terminfo + +while true; do + source /home/user/env + echo -n "[$(date)] " + if timeout "${TIMEOUT}" /home/user/doit.py; then + echo 'ok' | tee /tmp/healthz + else + echo -n "$? " + echo 'err' | tee /tmp/healthz + fi + sleep "${PERIOD}" +done