mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-23 19:56:01 +00:00
Merge pull request #78543 from cheftako/kas-np3
Get network-proxy working with GCE.
This commit is contained in:
commit
4c315aa8d9
@ -432,6 +432,7 @@ function kube::release::package_kube_manifests_tarball() {
|
||||
cp "${src_dir}/etcd.manifest" "${dst_dir}"
|
||||
cp "${src_dir}/kube-scheduler.manifest" "${dst_dir}"
|
||||
cp "${src_dir}/kube-apiserver.manifest" "${dst_dir}"
|
||||
cp "${src_dir}/konnectivity-server.yaml" "${dst_dir}"
|
||||
cp "${src_dir}/abac-authz-policy.jsonl" "${dst_dir}"
|
||||
cp "${src_dir}/kube-controller-manager.manifest" "${dst_dir}"
|
||||
cp "${src_dir}/kube-addon-manager.yaml" "${dst_dir}"
|
||||
|
66
cluster/gce/addons/konnectivity-agent/daemonset.yaml
Normal file
66
cluster/gce/addons/konnectivity-agent/daemonset.yaml
Normal file
@ -0,0 +1,66 @@
|
||||
---
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
labels:
|
||||
addonmanager.kubernetes.io/mode: Reconcile
|
||||
k8s-app: konnectivity-agent
|
||||
namespace: kube-system
|
||||
name: konnectivity-agent
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: konnectivity-agent
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: konnectivity-agent
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ''
|
||||
spec:
|
||||
priorityClassName: system-cluster-critical
|
||||
tolerations:
|
||||
- key: "CriticalAddonsOnly"
|
||||
operator: "Exists"
|
||||
hostNetwork: true
|
||||
volumes:
|
||||
- name: pki
|
||||
hostPath:
|
||||
path: /etc/srv/kubernetes/pki/konnectivity-agent
|
||||
containers:
|
||||
- image: gcr.io/google-containers/proxy-agent:v0.0.3
|
||||
name: konnectivity-agent
|
||||
command: ["/proxy-agent"]
|
||||
args: [
|
||||
"--logtostderr=true",
|
||||
"--ca-cert=/etc/srv/kubernetes/pki/konnectivity-agent/ca.crt",
|
||||
"--agent-cert=/etc/srv/kubernetes/pki/konnectivity-agent/client.crt",
|
||||
"--agent-key=/etc/srv/kubernetes/pki/konnectivity-agent/client.key",
|
||||
"--proxy-server-host=__APISERVER_IP__",
|
||||
"--proxy-server-port=8132"
|
||||
]
|
||||
env:
|
||||
- name: POD_NAME
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: POD_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
resources:
|
||||
limits:
|
||||
cpu: 50m
|
||||
memory: 30Mi
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
host: 127.0.0.1
|
||||
port: 8093
|
||||
path: /healthz
|
||||
initialDelaySeconds: 15
|
||||
timeoutSeconds: 15
|
||||
volumeMounts:
|
||||
- name: pki
|
||||
mountPath: /etc/srv/kubernetes/pki/konnectivity-agent
|
@ -480,3 +480,6 @@ WINDOWS_NODE_TAINTS="${WINDOWS_NODE_TAINTS:-node.kubernetes.io/os=win1809:NoSche
|
||||
|
||||
# Whether to set up a private GCE cluster, i.e. a cluster where nodes have only private IPs.
|
||||
GCE_PRIVATE_CLUSTER="${KUBE_GCE_PRIVATE_CLUSTER:-false}"
|
||||
|
||||
# Optional: Create apiserver konnectivity server and agent.
|
||||
ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE="${KUBE_ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}"
|
||||
|
@ -495,6 +495,16 @@ function create-node-pki {
|
||||
KUBELET_KEY_PATH="${pki_dir}/kubelet.key"
|
||||
write-pki-data "${KUBELET_KEY}" "${KUBELET_KEY_PATH}"
|
||||
fi
|
||||
|
||||
if [[ "${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
|
||||
mkdir -p "${pki_dir}/konnectivity-agent"
|
||||
KONNECTIVITY_AGENT_CA_CERT_PATH="${pki_dir}/konnectivity-agent/ca.crt"
|
||||
KONNECTIVITY_AGENT_CLIENT_KEY_PATH="${pki_dir}/konnectivity-agent/client.key"
|
||||
KONNECTIVITY_AGENT_CLIENT_CERT_PATH="${pki_dir}/konnectivity-agent/client.crt"
|
||||
write-pki-data "${KONNECTIVITY_AGENT_CA_CERT}" "${KONNECTIVITY_AGENT_CA_CERT_PATH}"
|
||||
write-pki-data "${KONNECTIVITY_AGENT_CLIENT_KEY}" "${KONNECTIVITY_AGENT_CLIENT_KEY_PATH}"
|
||||
write-pki-data "${KONNECTIVITY_AGENT_CLIENT_CERT}" "${KONNECTIVITY_AGENT_CLIENT_CERT_PATH}"
|
||||
fi
|
||||
}
|
||||
|
||||
function create-master-pki {
|
||||
@ -558,6 +568,42 @@ function create-master-pki {
|
||||
PROXY_CLIENT_CERT_PATH="${pki_dir}/proxy_client.crt"
|
||||
write-pki-data "${PROXY_CLIENT_CERT}" "${PROXY_CLIENT_CERT_PATH}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "${KONNECTIVITY_SERVER_CA_CERT:-}" ]]; then
|
||||
mkdir -p "${pki_dir}"/konnectivity-server
|
||||
#KONNECTIVITY_SERVER_CA_KEY_PATH="${pki_dir}/konnectivity-server/ca.key"
|
||||
#write-pki-data "${KONNECTIVITY_SERVER_CA_KEY}" "${KONNECTIVITY_SERVER_CA_KEY_PATH}"
|
||||
|
||||
KONNECTIVITY_SERVER_CA_CERT_PATH="${pki_dir}/konnectivity-server/ca.crt"
|
||||
write-pki-data "${KONNECTIVITY_SERVER_CA_CERT}" "${KONNECTIVITY_SERVER_CA_CERT_PATH}"
|
||||
|
||||
KONNECTIVITY_SERVER_KEY_PATH="${pki_dir}/konnectivity-server/server.key"
|
||||
write-pki-data "${KONNECTIVITY_SERVER_KEY}" "${KONNECTIVITY_SERVER_KEY_PATH}"
|
||||
|
||||
KONNECTIVITY_SERVER_CERT_PATH="${pki_dir}/konnectivity-server/server.crt"
|
||||
write-pki-data "${KONNECTIVITY_SERVER_CERT}" "${KONNECTIVITY_SERVER_CERT_PATH}"
|
||||
|
||||
KONNECTIVITY_SERVER_CLIENT_KEY_PATH="${pki_dir}/konnectivity-server/client.key"
|
||||
write-pki-data "${KONNECTIVITY_SERVER_CLIENT_KEY}" "${KONNECTIVITY_SERVER_CLIENT_KEY_PATH}"
|
||||
|
||||
KONNECTIVITY_SERVER_CLIENT_CERT_PATH="${pki_dir}/konnectivity-server/client.crt"
|
||||
write-pki-data "${KONNECTIVITY_SERVER_CLIENT_CERT}" "${KONNECTIVITY_SERVER_CLIENT_CERT_PATH}"
|
||||
fi
|
||||
|
||||
if [[ ! -z "${KONNECTIVITY_AGENT_CA_CERT:-}" ]]; then
|
||||
mkdir -p "${pki_dir}"/konnectivity-agent
|
||||
KONNECTIVITY_AGENT_CA_KEY_PATH="${pki_dir}/konnectivity-agent/ca.key"
|
||||
write-pki-data "${KONNECTIVITY_AGENT_CA_KEY}" "${KONNECTIVITY_AGENT_CA_KEY_PATH}"
|
||||
|
||||
KONNECTIVITY_AGENT_CA_CERT_PATH="${pki_dir}/konnectivity-agent/ca.crt"
|
||||
write-pki-data "${KONNECTIVITY_AGENT_CA_CERT}" "${KONNECTIVITY_AGENT_CA_CERT_PATH}"
|
||||
|
||||
KONNECTIVITY_AGENT_KEY_PATH="${pki_dir}/konnectivity-agent/server.key"
|
||||
write-pki-data "${KONNECTIVITY_AGENT_KEY}" "${KONNECTIVITY_AGENT_KEY_PATH}"
|
||||
|
||||
KONNECTIVITY_AGENT_CERT_PATH="${pki_dir}/konnectivity-agent/server.crt"
|
||||
write-pki-data "${KONNECTIVITY_AGENT_CERT}" "${KONNECTIVITY_AGENT_CERT_PATH}"
|
||||
fi
|
||||
}
|
||||
|
||||
# After the first boot and on upgrade, these files exist on the master-pd
|
||||
@ -757,6 +803,27 @@ contexts:
|
||||
name: webhook
|
||||
EOF
|
||||
fi
|
||||
if [[ "${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
|
||||
cat <<EOF >/etc/srv/kubernetes/egress_selector_configuration.yaml
|
||||
apiVersion: apiserver.k8s.io/v1alpha1
|
||||
kind: EgressSelectorConfiguration
|
||||
egressSelections:
|
||||
- name: cluster
|
||||
connection:
|
||||
type: http-connect
|
||||
httpConnect:
|
||||
url: https://127.0.0.1:8131
|
||||
caBundle: /etc/srv/kubernetes/pki/konnectivity-server/ca.crt
|
||||
clientKey: /etc/srv/kubernetes/pki/konnectivity-server/client.key
|
||||
clientCert: /etc/srv/kubernetes/pki/konnectivity-server/client.crt
|
||||
- name: master
|
||||
connection:
|
||||
type: direct
|
||||
- name: etcd
|
||||
connection:
|
||||
type: direct
|
||||
EOF
|
||||
fi
|
||||
|
||||
if [[ -n "${WEBHOOK_GKE_EXEC_AUTH:-}" ]]; then
|
||||
if [[ -z "${EXEC_AUTH_PLUGIN_URL:-}" ]]; then
|
||||
@ -1214,6 +1281,10 @@ function create-master-etcd-apiserver-auth {
|
||||
fi
|
||||
}
|
||||
|
||||
function create-master-konnectivity-server-apiserver-auth {
|
||||
echo TODO: implement create-master-konnectivity-server-apiserver-auth
|
||||
}
|
||||
|
||||
function assemble-docker-flags {
|
||||
echo "Assemble docker command line flags"
|
||||
local docker_opts="-p /var/run/docker.pid --iptables=false --ip-masq=false"
|
||||
@ -1562,6 +1633,49 @@ function start-etcd-servers {
|
||||
prepare-etcd-manifest "-events" "4002" "2381" "100m" "etcd-events.manifest"
|
||||
}
|
||||
|
||||
# Replaces the variables in the konnectivity-server manifest file with the real values, and then
|
||||
# copy the file to the manifest dir
|
||||
# $1: value for variable "server_port"
|
||||
# $2: value for variable "agent_port"
|
||||
# $3: value for bariable "admin_port"
|
||||
function prepare-konnectivity-server-manifest {
|
||||
local -r temp_file="/tmp/konnectivity-server.yaml"
|
||||
params=()
|
||||
cp "${KUBE_HOME}/kube-manifests/kubernetes/gci-trusty/konnectivity-server.yaml" "${temp_file}"
|
||||
params+=("--log-file=/var/log/konnectivity-server.log")
|
||||
params+=("--logtostderr=false")
|
||||
params+=("--log-file-max-size=0")
|
||||
params+=("--server-ca-cert=${KONNECTIVITY_SERVER_CA_CERT_PATH}")
|
||||
params+=("--server-cert=${KONNECTIVITY_SERVER_CERT_PATH}")
|
||||
params+=("--server-key=${KONNECTIVITY_SERVER_KEY_PATH}")
|
||||
params+=("--cluster-ca-cert=${KONNECTIVITY_AGENT_CA_CERT_PATH}")
|
||||
params+=("--cluster-cert=${KONNECTIVITY_AGENT_CERT_PATH}")
|
||||
params+=("--cluster-key=${KONNECTIVITY_AGENT_KEY_PATH}")
|
||||
params+=("--mode=http-connect")
|
||||
params+=("--server-port=$1")
|
||||
params+=("--agent-port=$2")
|
||||
params+=("--admin-port=$3")
|
||||
konnectivity_args=""
|
||||
for param in "${params[@]}"; do
|
||||
konnectivity_args+=", \"${param}\""
|
||||
done
|
||||
sed -i -e "s@{{ *konnectivity_args *}}@${konnectivity_args}@g" "${temp_file}"
|
||||
sed -i -e "s@{{ *server_port *}}@$1@g" "${temp_file}"
|
||||
sed -i -e "s@{{ *agent_port *}}@$2@g" "${temp_file}"
|
||||
sed -i -e "s@{{ *admin_port *}}@$3@g" "${temp_file}"
|
||||
sed -i -e "s@{{ *liveness_probe_initial_delay *}}@30@g" "${temp_file}"
|
||||
mv "${temp_file}" /etc/kubernetes/manifests
|
||||
}
|
||||
|
||||
# Starts konnectivity server pod.
|
||||
# More specifically, it prepares dirs and files, sets the variable value
|
||||
# in the manifests, and copies them to /etc/kubernetes/manifests.
|
||||
function start-konnectivity-server {
|
||||
echo "Start konnectivity server pods"
|
||||
prepare-log-file /var/log/konnectivity-server.log
|
||||
prepare-konnectivity-server-manifest "8131" "8132" "8133"
|
||||
}
|
||||
|
||||
# Calculates the following variables based on env variables, which will be used
|
||||
# by the manifests of several kube-master components.
|
||||
# CLOUD_CONFIG_OPT
|
||||
@ -1905,6 +2019,15 @@ function start-kube-apiserver {
|
||||
authorization_mode="Node,${authorization_mode}"
|
||||
params+=" --authorization-mode=${authorization_mode}"
|
||||
|
||||
local csc_config_mount=""
|
||||
local csc_config_volume=""
|
||||
if [[ "${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
|
||||
# Create the EgressSelectorConfiguration yaml file to control the Egress Selector.
|
||||
csc_config_mount="{\"name\": \"cscconfigmount\",\"mountPath\": \"/etc/srv/kubernetes/egress_selector_configuration.yaml\", \"readOnly\": false},"
|
||||
csc_config_volume="{\"name\": \"cscconfigmount\",\"hostPath\": {\"path\": \"/etc/srv/kubernetes/egress_selector_configuration.yaml\", \"type\": \"FileOrCreate\"}},"
|
||||
params+=" --egress-selector-config-file=/etc/srv/kubernetes/egress_selector_configuration.yaml"
|
||||
fi
|
||||
|
||||
local container_env=""
|
||||
if [[ -n "${ENABLE_CACHE_MUTATION_DETECTOR:-}" ]]; then
|
||||
container_env+="{\"name\": \"KUBE_CACHE_MUTATION_DETECTOR\", \"value\": \"${ENABLE_CACHE_MUTATION_DETECTOR}\"}"
|
||||
@ -1943,6 +2066,8 @@ function start-kube-apiserver {
|
||||
sed -i -e "s@{{webhook_authn_config_volume}}@${webhook_authn_config_volume}@g" "${src_file}"
|
||||
sed -i -e "s@{{webhook_config_mount}}@${webhook_config_mount}@g" "${src_file}"
|
||||
sed -i -e "s@{{webhook_config_volume}}@${webhook_config_volume}@g" "${src_file}"
|
||||
sed -i -e "s@{{csc_config_mount}}@${csc_config_mount}@g" "${src_file}"
|
||||
sed -i -e "s@{{csc_config_volume}}@${csc_config_volume}@g" "${src_file}"
|
||||
sed -i -e "s@{{audit_policy_config_mount}}@${audit_policy_config_mount}@g" "${src_file}"
|
||||
sed -i -e "s@{{audit_policy_config_volume}}@${audit_policy_config_volume}@g" "${src_file}"
|
||||
sed -i -e "s@{{audit_webhook_config_mount}}@${audit_webhook_config_mount}@g" "${src_file}"
|
||||
@ -2675,6 +2800,11 @@ EOF
|
||||
setup-addon-manifests "addons" "node-termination-handler"
|
||||
setup-node-termination-handler-manifest
|
||||
fi
|
||||
# Setting up the konnectivity-agent daemonset
|
||||
if [[ "${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
|
||||
setup-addon-manifests "addons" "konnectivity-agent"
|
||||
setup-konnectivity-agent-manifest
|
||||
fi
|
||||
if [[ "${ENABLE_CLUSTER_DNS:-}" == "true" ]]; then
|
||||
# Create a new directory for the DNS addon and prepend a "0" on the name.
|
||||
# Prepending "0" to the directory ensures that add-on manager
|
||||
@ -2773,6 +2903,11 @@ function setup-node-termination-handler-manifest {
|
||||
fi
|
||||
}
|
||||
|
||||
function setup-konnectivity-agent-manifest {
|
||||
local -r manifest="/etc/kubernetes/addons/konnectivity-agent/daemonset.yaml"
|
||||
sed -i "s|__APISERVER_IP__|${KUBERNETES_MASTER_NAME}|g" "${manifest}"
|
||||
}
|
||||
|
||||
# Setups manifests for ingress controller and gce-specific policies for service controller.
|
||||
function start-lb-controller {
|
||||
setup-addon-manifests "addons" "loadbalancing"
|
||||
@ -3055,6 +3190,9 @@ function main() {
|
||||
create-master-pki
|
||||
create-master-auth
|
||||
ensure-master-bootstrap-kubectl-auth
|
||||
if [[ "${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
|
||||
create-master-konnectivity-server-apiserver-auth
|
||||
fi
|
||||
create-master-kubelet-auth
|
||||
create-master-etcd-auth
|
||||
create-master-etcd-apiserver-auth
|
||||
@ -3088,6 +3226,9 @@ function main() {
|
||||
start-etcd-empty-dir-cleanup-pod
|
||||
fi
|
||||
start-kube-apiserver
|
||||
if [[ "${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
|
||||
start-konnectivity-server
|
||||
fi
|
||||
start-kube-controller-manager
|
||||
start-kube-scheduler
|
||||
wait-till-apiserver-ready
|
||||
|
@ -18,6 +18,7 @@ filegroup(
|
||||
"etcd.manifest",
|
||||
"etcd-empty-dir-cleanup.yaml",
|
||||
"glbc.manifest",
|
||||
"konnectivity-server.yaml",
|
||||
"kube-addon-manager.yaml",
|
||||
"kube-apiserver.manifest",
|
||||
"kube-controller-manager.manifest",
|
||||
|
58
cluster/gce/manifests/konnectivity-server.yaml
Normal file
58
cluster/gce/manifests/konnectivity-server.yaml
Normal file
@ -0,0 +1,58 @@
|
||||
apiVersion: v1
|
||||
kind: Pod
|
||||
metadata:
|
||||
name: konnectivity-server
|
||||
namespace: kube-system
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ''
|
||||
seccomp.security.alpha.kubernetes.io/pod: 'docker/default'
|
||||
component: konnectivity-server
|
||||
spec:
|
||||
hostNetwork: true
|
||||
containers:
|
||||
- name: konnectivity-server-container
|
||||
image: gcr.io/google-containers/proxy-server:v0.0.3
|
||||
resources:
|
||||
requests:
|
||||
cpu: 40m
|
||||
command: [ "/proxy-server"{{ konnectivity_args }} ]
|
||||
livenessProbe:
|
||||
httpGet:
|
||||
scheme: HTTP
|
||||
host: 127.0.0.1
|
||||
port: {{ admin_port }}
|
||||
path: /healthz
|
||||
initialDelaySeconds: {{ liveness_probe_initial_delay }}
|
||||
timeoutSeconds: 60
|
||||
ports:
|
||||
- name: serverport
|
||||
containerPort: {{ server_port }}
|
||||
hostPort: {{ server_port }}
|
||||
- name: agentport
|
||||
containerPort: {{ agent_port }}
|
||||
hostPort: {{ agent_port }}
|
||||
- name: adminport
|
||||
containerPort: {{ admin_port }}
|
||||
hostPort: {{ admin_port }}
|
||||
volumeMounts:
|
||||
- name: varlogkonnectivityserver
|
||||
mountPath: /var/log/konnectivity-server.log
|
||||
readOnly: false
|
||||
- name: pkiserver
|
||||
mountPath: /etc/srv/kubernetes/pki/konnectivity-server
|
||||
readOnly: true
|
||||
- name: pkiagent
|
||||
mountPath: /etc/srv/kubernetes/pki/konnectivity-agent
|
||||
readOnly: true
|
||||
volumes:
|
||||
- name: varlogkonnectivityserver
|
||||
hostPath:
|
||||
path: /var/log/konnectivity-server.log
|
||||
type: FileOrCreate
|
||||
- name: pkiserver
|
||||
hostPath:
|
||||
path: /etc/srv/kubernetes/pki/konnectivity-server
|
||||
- name: pkiagent
|
||||
hostPath:
|
||||
path: /etc/srv/kubernetes/pki/konnectivity-agent
|
||||
|
@ -63,6 +63,7 @@
|
||||
{{additional_cloud_config_mount}}
|
||||
{{webhook_config_mount}}
|
||||
{{webhook_authn_config_mount}}
|
||||
{{csc_config_mount}}
|
||||
{{audit_policy_config_mount}}
|
||||
{{audit_webhook_config_mount}}
|
||||
{{webhook_exec_auth_plugin_mount}}
|
||||
@ -103,6 +104,7 @@
|
||||
{{additional_cloud_config_volume}}
|
||||
{{webhook_config_volume}}
|
||||
{{webhook_authn_config_volume}}
|
||||
{{csc_config_volume}}
|
||||
{{audit_policy_config_volume}}
|
||||
{{audit_webhook_config_volume}}
|
||||
{{webhook_exec_auth_plugin_volume}}
|
||||
|
@ -1066,6 +1066,18 @@ ETCD_APISERVER_SERVER_KEY: $(yaml-quote ${ETCD_APISERVER_SERVER_KEY_BASE64:-})
|
||||
ETCD_APISERVER_SERVER_CERT: $(yaml-quote ${ETCD_APISERVER_SERVER_CERT_BASE64:-})
|
||||
ETCD_APISERVER_CLIENT_KEY: $(yaml-quote ${ETCD_APISERVER_CLIENT_KEY_BASE64:-})
|
||||
ETCD_APISERVER_CLIENT_CERT: $(yaml-quote ${ETCD_APISERVER_CLIENT_CERT_BASE64:-})
|
||||
KONNECTIVITY_SERVER_CA_KEY: $(yaml-quote ${KONNECTIVITY_SERVER_CA_KEY_BASE64:-})
|
||||
KONNECTIVITY_SERVER_CA_CERT: $(yaml-quote ${KONNECTIVITY_SERVER_CA_CERT_BASE64:-})
|
||||
KONNECTIVITY_SERVER_CERT: $(yaml-quote ${KONNECTIVITY_SERVER_CERT_BASE64:-})
|
||||
KONNECTIVITY_SERVER_KEY: $(yaml-quote ${KONNECTIVITY_SERVER_KEY_BASE64:-})
|
||||
KONNECTIVITY_SERVER_CLIENT_CERT: $(yaml-quote ${KONNECTIVITY_SERVER_CLIENT_CERT_BASE64:-})
|
||||
KONNECTIVITY_SERVER_CLIENT_KEY: $(yaml-quote ${KONNECTIVITY_SERVER_CLIENT_KEY_BASE64:-})
|
||||
KONNECTIVITY_AGENT_CA_KEY: $(yaml-quote ${KONNECTIVITY_AGENT_CA_KEY_BASE64:-})
|
||||
KONNECTIVITY_AGENT_CA_CERT: $(yaml-quote ${KONNECTIVITY_AGENT_CA_CERT_BASE64:-})
|
||||
KONNECTIVITY_AGENT_CERT: $(yaml-quote ${KONNECTIVITY_AGENT_CERT_BASE64:-})
|
||||
KONNECTIVITY_AGENT_KEY: $(yaml-quote ${KONNECTIVITY_AGENT_KEY_BASE64:-})
|
||||
KONNECTIVITY_AGENT_CLIENT_CERT: $(yaml-quote ${KONNECTIVITY_AGENT_CLIENT_CERT_BASE64:-})
|
||||
KONNECTIVITY_AGENT_CLIENT_KEY: $(yaml-quote ${KONNECTIVITY_AGENT_CLIENT_KEY_BASE64:-})
|
||||
EOF
|
||||
}
|
||||
|
||||
@ -1206,6 +1218,13 @@ EOF
|
||||
[[ "${master}" == "false" && "${NODE_OS_DISTRIBUTION}" == "cos" ]]; then
|
||||
cat >>$file <<EOF
|
||||
REMOUNT_VOLUME_PLUGIN_DIR: $(yaml-quote ${REMOUNT_VOLUME_PLUGIN_DIR:-true})
|
||||
EOF
|
||||
fi
|
||||
if [[ "${master}" == "false" ]]; then
|
||||
cat >>$file <<EOF
|
||||
KONNECTIVITY_AGENT_CA_CERT: $(yaml-quote ${KONNECTIVITY_AGENT_CA_CERT_BASE64:-})
|
||||
KONNECTIVITY_AGENT_CLIENT_KEY: $(yaml-quote ${KONNECTIVITY_AGENT_CLIENT_KEY_BASE64:-})
|
||||
KONNECTIVITY_AGENT_CLIENT_CERT: $(yaml-quote ${KONNECTIVITY_AGENT_CLIENT_CERT_BASE64:-})
|
||||
EOF
|
||||
fi
|
||||
if [ -n "${KUBE_APISERVER_REQUEST_TIMEOUT:-}" ]; then
|
||||
@ -1462,6 +1481,11 @@ EOF
|
||||
if [ -n "${MAX_PODS_PER_NODE:-}" ]; then
|
||||
cat >>$file <<EOF
|
||||
MAX_PODS_PER_NODE: $(yaml-quote ${MAX_PODS_PER_NODE})
|
||||
EOF
|
||||
fi
|
||||
if [[ "${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
|
||||
cat >>$file <<EOF
|
||||
ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE: $(yaml-quote ${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE})
|
||||
EOF
|
||||
fi
|
||||
}
|
||||
@ -1549,6 +1573,7 @@ function create-certs {
|
||||
setup-easyrsa
|
||||
PRIMARY_CN="${primary_cn}" SANS="${sans}" generate-certs
|
||||
AGGREGATOR_PRIMARY_CN="${primary_cn}" AGGREGATOR_SANS="${sans}" generate-aggregator-certs
|
||||
KONNECTIVITY_SERVER_PRIMARY_CN="${primary_cn}" KONNECTIVITY_SERVER_SANS="${sans}" generate-konnectivity-server-certs
|
||||
|
||||
# By default, linux wraps base64 output every 76 cols, so we use 'tr -d' to remove whitespaces.
|
||||
# Note 'base64 -w0' doesn't work on Mac OS X, which has different flags.
|
||||
@ -1570,6 +1595,22 @@ function create-certs {
|
||||
REQUESTHEADER_CA_CERT_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/ca.crt" | base64 | tr -d '\r\n')
|
||||
PROXY_CLIENT_CERT_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/issued/proxy-client.crt" | base64 | tr -d '\r\n')
|
||||
PROXY_CLIENT_KEY_BASE64=$(cat "${AGGREGATOR_CERT_DIR}/pki/private/proxy-client.key" | base64 | tr -d '\r\n')
|
||||
|
||||
# Setting up the Kubernetes API Server Konnectivity Server auth.
|
||||
# This includes certs for both API Server to Konnectivity Server and
|
||||
# Konnectivity Agent to Konnectivity Server.
|
||||
KONNECTIVITY_SERVER_CA_KEY_BASE64=$(cat "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/ca.key" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_SERVER_CA_CERT_BASE64=$(cat "${KONNECTIVITY_SERVER_CERT_DIR}/pki/ca.crt" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_SERVER_CERT_BASE64=$(cat "${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/server.crt" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_SERVER_KEY_BASE64=$(cat "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/server.key" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_SERVER_CLIENT_CERT_BASE64=$(cat "${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/client.crt" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_SERVER_CLIENT_KEY_BASE64=$(cat "${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/client.key" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_AGENT_CA_KEY_BASE64=$(cat "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/ca.key" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_AGENT_CA_CERT_BASE64=$(cat "${KONNECTIVITY_AGENT_CERT_DIR}/pki/ca.crt" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_AGENT_CERT_BASE64=$(cat "${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/server.crt" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_AGENT_KEY_BASE64=$(cat "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/server.key" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_AGENT_CLIENT_CERT_BASE64=$(cat "${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/client.crt" | base64 | tr -d '\r\n')
|
||||
KONNECTIVITY_AGENT_CLIENT_KEY_BASE64=$(cat "${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/client.key" | base64 | tr -d '\r\n')
|
||||
}
|
||||
|
||||
# Set up easy-rsa directory structure.
|
||||
@ -1590,9 +1631,15 @@ function setup-easyrsa {
|
||||
mkdir easy-rsa-master/kubelet
|
||||
cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/kubelet
|
||||
mkdir easy-rsa-master/aggregator
|
||||
cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/aggregator) &>${cert_create_debug_output} || true
|
||||
cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/aggregator
|
||||
mkdir easy-rsa-master/konnectivity-server
|
||||
cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/konnectivity-server
|
||||
mkdir easy-rsa-master/konnectivity-agent
|
||||
cp -r easy-rsa-master/easyrsa3/* easy-rsa-master/konnectivity-agent) &>${cert_create_debug_output} || true
|
||||
CERT_DIR="${KUBE_TEMP}/easy-rsa-master/easyrsa3"
|
||||
AGGREGATOR_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/aggregator"
|
||||
KONNECTIVITY_SERVER_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/konnectivity-server"
|
||||
KONNECTIVITY_AGENT_CERT_DIR="${KUBE_TEMP}/easy-rsa-master/konnectivity-agent"
|
||||
if [ ! -x "${CERT_DIR}/easyrsa" -o ! -x "${AGGREGATOR_CERT_DIR}/easyrsa" ]; then
|
||||
# TODO(roberthbailey,porridge): add better error handling here,
|
||||
# see https://github.com/kubernetes/kubernetes/issues/55229
|
||||
@ -1726,7 +1773,92 @@ function generate-aggregator-certs {
|
||||
fi
|
||||
}
|
||||
|
||||
# Runs the easy RSA commands to generate server side certificate files
|
||||
# for the konnectivity server. This includes both server side to both
|
||||
# konnectivity-server and konnectivity-agent.
|
||||
# The generated files are in ${KONNECTIVITY_SERVER_CERT_DIR} and
|
||||
# ${KONNECTIVITY_AGENT_CERT_DIR}
|
||||
#
|
||||
# Assumed vars
|
||||
# KUBE_TEMP
|
||||
# KONNECTIVITY_SERVER_CERT_DIR
|
||||
# KONNECTIVITY_SERVER_PRIMARY_CN: Primary canonical name
|
||||
# KONNECTIVITY_SERVER_SANS: Subject alternate names
|
||||
#
|
||||
function generate-konnectivity-server-certs {
|
||||
local -r cert_create_debug_output=$(mktemp "${KUBE_TEMP}/cert_create_debug_output.XXX")
|
||||
# Note: This was heavily cribbed from make-ca-cert.sh
|
||||
(set -x
|
||||
# Make the client <-> konnectivity server side certificates.
|
||||
cd "${KUBE_TEMP}/easy-rsa-master/konnectivity-server"
|
||||
./easyrsa init-pki
|
||||
# this puts the cert into pki/ca.crt and the key into pki/private/ca.key
|
||||
./easyrsa --batch "--req-cn=${KONNECTIVITY_SERVER_PRIMARY_CN}@$(date +%s)" build-ca nopass
|
||||
./easyrsa --subject-alt-name="IP:127.0.0.1,${KONNECTIVITY_SERVER_SANS}" build-server-full server nopass
|
||||
./easyrsa build-client-full client nopass
|
||||
|
||||
kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl"
|
||||
|
||||
# make the config for the signer
|
||||
echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","client auth"]}}}' > "ca-config.json"
|
||||
# create the konnectivity server cert with the correct groups
|
||||
echo '{"CN":"konnectivity-server","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare konnectivity-server
|
||||
rm -f "konnectivity-server.csr"
|
||||
|
||||
# Make the agent <-> konnectivity server side certificates.
|
||||
cd "${KUBE_TEMP}/easy-rsa-master/konnectivity-agent"
|
||||
./easyrsa init-pki
|
||||
# this puts the cert into pki/ca.crt and the key into pki/private/ca.key
|
||||
./easyrsa --batch "--req-cn=${KONNECTIVITY_SERVER_PRIMARY_CN}@$(date +%s)" build-ca nopass
|
||||
./easyrsa --subject-alt-name="${KONNECTIVITY_SERVER_SANS}" build-server-full server nopass
|
||||
./easyrsa build-client-full client nopass
|
||||
|
||||
kube::util::ensure-cfssl "${KUBE_TEMP}/cfssl"
|
||||
|
||||
# make the config for the signer
|
||||
echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","agent auth"]}}}' > "ca-config.json"
|
||||
# create the konnectivity server cert with the correct groups
|
||||
echo '{"CN":"koonectivity-server","hosts":[""],"key":{"algo":"rsa","size":2048}}' | "${CFSSL_BIN}" gencert -ca=pki/ca.crt -ca-key=pki/private/ca.key -config=ca-config.json - | "${CFSSLJSON_BIN}" -bare konnectivity-agent
|
||||
rm -f "konnectivity-agent.csr"
|
||||
|
||||
echo `ls ${KONNECTIVITY_SERVER_CERT_DIR}/pki/`
|
||||
echo `ls ${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/`
|
||||
echo `ls ${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/`
|
||||
echo `ls ${KONNECTIVITY_AGENT_CERT_DIR}/pki/`
|
||||
echo `ls ${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/`
|
||||
echo `ls ${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/`
|
||||
echo "completed main certificate section") &>${cert_create_debug_output} || true
|
||||
|
||||
local output_file_missing=0
|
||||
local output_file
|
||||
for output_file in \
|
||||
"${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/ca.key" \
|
||||
"${KONNECTIVITY_SERVER_CERT_DIR}/pki/ca.crt" \
|
||||
"${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/server.crt" \
|
||||
"${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/server.key" \
|
||||
"${KONNECTIVITY_SERVER_CERT_DIR}/pki/issued/client.crt" \
|
||||
"${KONNECTIVITY_SERVER_CERT_DIR}/pki/private/client.key" \
|
||||
"${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/ca.key" \
|
||||
"${KONNECTIVITY_AGENT_CERT_DIR}/pki/ca.crt" \
|
||||
"${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/server.crt" \
|
||||
"${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/server.key" \
|
||||
"${KONNECTIVITY_AGENT_CERT_DIR}/pki/issued/client.crt" \
|
||||
"${KONNECTIVITY_AGENT_CERT_DIR}/pki/private/client.key"
|
||||
do
|
||||
if [[ ! -s "${output_file}" ]]; then
|
||||
echo "Expected file ${output_file} not created" >&2
|
||||
output_file_missing=1
|
||||
fi
|
||||
done
|
||||
if (( $output_file_missing )); then
|
||||
# TODO(roberthbailey,porridge): add better error handling here,
|
||||
# see https://github.com/kubernetes/kubernetes/issues/55229
|
||||
cat "${cert_create_debug_output}" >&2
|
||||
echo "=== Failed to generate konnectivity-server certificates: Aborting ===" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
# Using provided master env, extracts value from provided key.
|
||||
#
|
||||
# Args:
|
||||
@ -1766,6 +1898,16 @@ function parse-master-env() {
|
||||
ETCD_APISERVER_SERVER_CERT_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_SERVER_CERT")
|
||||
ETCD_APISERVER_CLIENT_KEY_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_CLIENT_KEY")
|
||||
ETCD_APISERVER_CLIENT_CERT_BASE64=$(get-env-val "${master_env}" "ETCD_APISERVER_CLIENT_CERT")
|
||||
KONNECTIVITY_SERVER_CA_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CA_KEY")
|
||||
KONNECTIVITY_SERVER_CA_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CA_CERT")
|
||||
KONNECTIVITY_SERVER_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CERT")
|
||||
KONNECTIVITY_SERVER_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_KEY")
|
||||
KONNECTIVITY_SERVER_CLIENT_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CLIENT_CERT")
|
||||
KONNECTIVITY_SERVER_CLIENT_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_SERVER_CLIENT_KEY")
|
||||
KONNECTIVITY_AGENT_CA_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_AGENT_CA_KEY")
|
||||
KONNECTIVITY_AGENT_CA_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_AGENT_CA_CERT")
|
||||
KONNECTIVITY_AGENT_CERT_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_AGENT_CERT")
|
||||
KONNECTIVITY_AGENT_KEY_BASE64=$(get-env-val "${master_env}" "KONNECTIVITY_AGENT_KEY")
|
||||
}
|
||||
|
||||
# Update or verify required gcloud components are installed
|
||||
@ -2505,6 +2647,15 @@ function create-master() {
|
||||
--target-tags "${MASTER_TAG}" \
|
||||
--allow tcp:443 &
|
||||
|
||||
echo "Configuring firewall for apiserver konnectivity server"
|
||||
if [[ "${ENABLE_EGRESS_VIA_KONNECTIVITY_SERVICE:-false}" == "true" ]]; then
|
||||
gcloud compute firewall-rules create "${MASTER_NAME}-konnectivity-server" \
|
||||
--project "${NETWORK_PROJECT}" \
|
||||
--network "${NETWORK}" \
|
||||
--target-tags "${MASTER_TAG}" \
|
||||
--allow tcp:8132 &
|
||||
fi
|
||||
|
||||
# We have to make sure the disk is created before creating the master VM, so
|
||||
# run this in the foreground.
|
||||
gcloud compute disks create "${MASTER_NAME}-pd" \
|
||||
@ -3197,7 +3348,7 @@ function kube-down() {
|
||||
# If there are no more remaining master replicas, we should delete all remaining network resources.
|
||||
if [[ "${REMAINING_MASTER_COUNT}" -eq 0 ]]; then
|
||||
# Delete firewall rule for the master, etcd servers, and nodes.
|
||||
delete-firewall-rules "${MASTER_NAME}-https" "${MASTER_NAME}-etcd" "${NODE_TAG}-all"
|
||||
delete-firewall-rules "${MASTER_NAME}-https" "${MASTER_NAME}-etcd" "${NODE_TAG}-all" "${MASTER_NAME}-konnectivity-server"
|
||||
# Delete the master's reserved IP
|
||||
if gcloud compute addresses describe "${MASTER_NAME}-ip" --region "${REGION}" --project "${PROJECT}" &>/dev/null; then
|
||||
gcloud compute addresses delete \
|
||||
|
@ -48,6 +48,7 @@ type ServerRunOptions struct {
|
||||
Authorization *kubeoptions.BuiltInAuthorizationOptions
|
||||
CloudProvider *kubeoptions.CloudProviderOptions
|
||||
APIEnablement *genericoptions.APIEnablementOptions
|
||||
EgressSelector *genericoptions.EgressSelectorOptions
|
||||
|
||||
AllowPrivileged bool
|
||||
EnableLogsHandler bool
|
||||
@ -87,6 +88,7 @@ func NewServerRunOptions() *ServerRunOptions {
|
||||
Authorization: kubeoptions.NewBuiltInAuthorizationOptions(),
|
||||
CloudProvider: kubeoptions.NewCloudProviderOptions(),
|
||||
APIEnablement: genericoptions.NewAPIEnablementOptions(),
|
||||
EgressSelector: genericoptions.NewEgressSelectorOptions(),
|
||||
|
||||
EnableLogsHandler: true,
|
||||
EventTTL: 1 * time.Hour,
|
||||
@ -134,6 +136,7 @@ func (s *ServerRunOptions) Flags() (fss cliflag.NamedFlagSets) {
|
||||
s.Authorization.AddFlags(fss.FlagSet("authorization"))
|
||||
s.CloudProvider.AddFlags(fss.FlagSet("cloud provider"))
|
||||
s.APIEnablement.AddFlags(fss.FlagSet("api enablement"))
|
||||
s.EgressSelector.AddFlags(fss.FlagSet("egress selector"))
|
||||
s.Admission.AddFlags(fss.FlagSet("admission"))
|
||||
|
||||
// Note: the weird ""+ in below lines seems to be the only way to get gofmt to
|
||||
|
@ -94,6 +94,7 @@ func TestAddFlags(t *testing.T) {
|
||||
"--cloud-provider=azure",
|
||||
"--cors-allowed-origins=10.10.10.100,10.10.10.200",
|
||||
"--contention-profiling=true",
|
||||
"--egress-selector-config-file=/var/run/kubernetes/egress-selector/connectivity.yaml",
|
||||
"--enable-aggregator-routing=true",
|
||||
"--enable-logs-handler=false",
|
||||
"--endpoint-reconciler-type=" + string(reconcilers.LeaseEndpointReconcilerType),
|
||||
@ -293,6 +294,9 @@ func TestAddFlags(t *testing.T) {
|
||||
APIEnablement: &apiserveroptions.APIEnablementOptions{
|
||||
RuntimeConfig: cliflag.ConfigurationMap{},
|
||||
},
|
||||
EgressSelector: &apiserveroptions.EgressSelectorOptions{
|
||||
ConfigFile: "/var/run/kubernetes/egress-selector/connectivity.yaml",
|
||||
},
|
||||
EnableLogsHandler: false,
|
||||
EnableAggregatorRouting: true,
|
||||
ProxyClientKeyFile: "/var/run/kubernetes/proxy.key",
|
||||
|
@ -362,6 +362,10 @@ func CreateKubeAPIServerConfig(
|
||||
// Use the nodeTunneler's dialer to connect to the kubelet
|
||||
config.ExtraConfig.KubeletClientConfig.Dial = nodeTunneler.Dial
|
||||
}
|
||||
if config.GenericConfig.EgressSelector != nil {
|
||||
// Use the config.GenericConfig.EgressSelector lookup to find the dialer to connect to the kubelet
|
||||
config.ExtraConfig.KubeletClientConfig.Lookup = config.GenericConfig.EgressSelector.Lookup
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
@ -402,6 +406,9 @@ func buildGenericConfig(
|
||||
if lastErr = s.APIEnablement.ApplyTo(genericConfig, master.DefaultAPIResourceConfigSource(), legacyscheme.Scheme); lastErr != nil {
|
||||
return
|
||||
}
|
||||
if lastErr = s.EgressSelector.ApplyTo(genericConfig); lastErr != nil {
|
||||
return
|
||||
}
|
||||
|
||||
genericConfig.OpenAPIConfig = genericapiserver.DefaultOpenAPIConfig(generatedopenapi.GetOpenAPIDefinitions, openapinamer.NewDefinitionNamer(legacyscheme.Scheme, extensionsapiserver.Scheme, aggregatorscheme.Scheme))
|
||||
genericConfig.OpenAPIConfig.Info.Title = "Kubernetes"
|
||||
|
@ -16,6 +16,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/rest:go_default_library",
|
||||
"//staging/src/k8s.io/client-go/transport:go_default_library",
|
||||
],
|
||||
|
@ -18,6 +18,7 @@ package client
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"time"
|
||||
@ -26,6 +27,7 @@ import (
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/types"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
restclient "k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/transport"
|
||||
nodeutil "k8s.io/kubernetes/pkg/util/node"
|
||||
@ -56,6 +58,9 @@ type KubeletClientConfig struct {
|
||||
|
||||
// Dial is a custom dialer used for the client
|
||||
Dial utilnet.DialFunc
|
||||
|
||||
// Lookup will give us a dialer if the egress selector is configured for it
|
||||
Lookup server.EgressSelectorLookup
|
||||
}
|
||||
|
||||
// ConnectionInfo provides the information needed to connect to a kubelet
|
||||
@ -79,9 +84,20 @@ func MakeTransport(config *KubeletClientConfig) (http.RoundTripper, error) {
|
||||
}
|
||||
|
||||
rt := http.DefaultTransport
|
||||
if config.Dial != nil || tlsConfig != nil {
|
||||
dialer := config.Dial
|
||||
if dialer == nil && config.Lookup != nil {
|
||||
// Assuming EgressSelector if SSHTunnel is not turned on.
|
||||
// We will not get a dialer if egress selector is disabled.
|
||||
networkContext := server.NetworkContext{EgressSelectionName: server.Cluster}
|
||||
dialer, err = config.Lookup(networkContext)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to get context dialer for 'cluster': got %v", err)
|
||||
}
|
||||
}
|
||||
if dialer != nil || tlsConfig != nil {
|
||||
// If SSH Tunnel is turned on
|
||||
rt = utilnet.SetOldTransportDefaults(&http.Transport{
|
||||
DialContext: config.Dial,
|
||||
DialContext: dialer,
|
||||
TLSClientConfig: tlsConfig,
|
||||
})
|
||||
}
|
||||
|
@ -215,6 +215,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss=
|
||||
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.4 h1:S0tLZ3VOKl2Te0hpq8+ke0eSJPfCnNTPiDlsfwi1/NE=
|
||||
|
1
staging/src/k8s.io/apiserver/go.sum
generated
1
staging/src/k8s.io/apiserver/go.sum
generated
@ -156,6 +156,7 @@ github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss=
|
||||
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg=
|
||||
|
@ -45,6 +45,7 @@ var (
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&AdmissionConfiguration{},
|
||||
&EgressSelectorConfiguration{},
|
||||
)
|
||||
return nil
|
||||
}
|
||||
|
@ -48,3 +48,52 @@ type AdmissionPluginConfiguration struct {
|
||||
// +optional
|
||||
Configuration *runtime.Unknown
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// EgressSelectorConfiguration provides versioned configuration for egress selector clients.
|
||||
type EgressSelectorConfiguration struct {
|
||||
metav1.TypeMeta
|
||||
|
||||
// EgressSelections contains a list of egress selection client configurations
|
||||
EgressSelections []EgressSelection
|
||||
}
|
||||
|
||||
// EgressSelection provides the configuration for a single egress selection client.
|
||||
type EgressSelection struct {
|
||||
// Name is the name of the egress selection.
|
||||
// Currently supported values are "Master", "Etcd" and "Cluster"
|
||||
Name string
|
||||
|
||||
// Connection is the exact information used to configure the egress selection
|
||||
Connection Connection
|
||||
}
|
||||
|
||||
// Connection provides the configuration for a single egress selection client.
|
||||
type Connection struct {
|
||||
// Type is the type of connection used to connect from client to konnectivity server.
|
||||
// Currently supported values are "http-connect" and "direct".
|
||||
Type string
|
||||
|
||||
// httpConnect is the config needed to use http-connect to the konnectivity server.
|
||||
// +optional
|
||||
HTTPConnect *HTTPConnectConfig
|
||||
}
|
||||
|
||||
type HTTPConnectConfig struct {
|
||||
// URL is the location of the konnectivity server to connect to.
|
||||
// As an example it might be "https://127.0.0.1:8131"
|
||||
URL string
|
||||
|
||||
// CABundle is the file location of the CA to be used to determine trust with the konnectivity server.
|
||||
// +optional
|
||||
CABundle string
|
||||
|
||||
// ClientKey is the file location of the client key to be used in mtls handshakes with the konnectivity server.
|
||||
// +optional
|
||||
ClientKey string
|
||||
|
||||
// ClientCert is the file location of the client certificate to be used in mtls handshakes with the konnectivity server.
|
||||
// +optional
|
||||
ClientCert string
|
||||
}
|
||||
|
@ -46,6 +46,7 @@ func init() {
|
||||
func addKnownTypes(scheme *runtime.Scheme) error {
|
||||
scheme.AddKnownTypes(SchemeGroupVersion,
|
||||
&AdmissionConfiguration{},
|
||||
&EgressSelectorConfiguration{},
|
||||
)
|
||||
metav1.AddToGroupVersion(scheme, SchemeGroupVersion)
|
||||
return nil
|
||||
|
@ -48,3 +48,63 @@ type AdmissionPluginConfiguration struct {
|
||||
// +optional
|
||||
Configuration *runtime.Unknown `json:"configuration"`
|
||||
}
|
||||
|
||||
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
|
||||
|
||||
// EgressSelectorConfiguration provides versioned configuration for egress selector clients.
|
||||
type EgressSelectorConfiguration struct {
|
||||
metav1.TypeMeta `json:",inline"`
|
||||
|
||||
// connectionServices contains a list of egress selection client configurations
|
||||
EgressSelections []EgressSelection `json:"egressSelections"`
|
||||
}
|
||||
|
||||
// EgressSelection provides the configuration for a single egress selection client.
|
||||
type EgressSelection struct {
|
||||
// name is the name of the egress selection.
|
||||
// Currently supported values are "Master", "Etcd" and "Cluster"
|
||||
Name string `json:"name"`
|
||||
|
||||
// connection is the exact information used to configure the egress selection
|
||||
Connection Connection `json:"connection"`
|
||||
}
|
||||
|
||||
// Connection provides the configuration for a single egress selection client.
|
||||
type Connection struct {
|
||||
// type is the type of connection used to connect from client to network/konnectivity server.
|
||||
// Currently supported values are "http-connect" and "direct".
|
||||
Type string `json:"type"`
|
||||
|
||||
// httpConnect is the config needed to use http-connect to the konnectivity server.
|
||||
// Absence when the type is "http-connect" will cause an error
|
||||
// Presence when the type is "direct" will also cause an error
|
||||
// +optional
|
||||
HTTPConnect *HTTPConnectConfig `json:"httpConnect,omitempty"`
|
||||
}
|
||||
|
||||
type HTTPConnectConfig struct {
|
||||
// url is the location of the proxy server to connect to.
|
||||
// As an example it might be "https://127.0.0.1:8131"
|
||||
URL string `json:"url"`
|
||||
|
||||
// caBundle is the file location of the CA to be used to determine trust with the konnectivity server.
|
||||
// Must be absent/empty http-connect using the plain http
|
||||
// Must be configured for http-connect using the https protocol
|
||||
// Misconfiguration will cause an error
|
||||
// +optional
|
||||
CABundle string `json:"caBundle,omitempty"`
|
||||
|
||||
// clientKey is the file location of the client key to be used in mtls handshakes with the konnectivity server.
|
||||
// Must be absent/empty http-connect using the plain http
|
||||
// Must be configured for http-connect using the https protocol
|
||||
// Misconfiguration will cause an error
|
||||
// +optional
|
||||
ClientKey string `json:"clientKey,omitempty"`
|
||||
|
||||
// clientCert is the file location of the client certificate to be used in mtls handshakes with the konnectivity server.
|
||||
// Must be absent/empty http-connect using the plain http
|
||||
// Must be configured for http-connect using the https protocol
|
||||
// Misconfiguration will cause an error
|
||||
// +optional
|
||||
ClientCert string `json:"clientCert,omitempty"`
|
||||
}
|
||||
|
@ -55,6 +55,46 @@ func RegisterConversions(s *runtime.Scheme) error {
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*Connection)(nil), (*apiserver.Connection)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_Connection_To_apiserver_Connection(a.(*Connection), b.(*apiserver.Connection), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.Connection)(nil), (*Connection)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_Connection_To_v1alpha1_Connection(a.(*apiserver.Connection), b.(*Connection), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*EgressSelection)(nil), (*apiserver.EgressSelection)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_EgressSelection_To_apiserver_EgressSelection(a.(*EgressSelection), b.(*apiserver.EgressSelection), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.EgressSelection)(nil), (*EgressSelection)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_EgressSelection_To_v1alpha1_EgressSelection(a.(*apiserver.EgressSelection), b.(*EgressSelection), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*EgressSelectorConfiguration)(nil), (*apiserver.EgressSelectorConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_EgressSelectorConfiguration_To_apiserver_EgressSelectorConfiguration(a.(*EgressSelectorConfiguration), b.(*apiserver.EgressSelectorConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.EgressSelectorConfiguration)(nil), (*EgressSelectorConfiguration)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_EgressSelectorConfiguration_To_v1alpha1_EgressSelectorConfiguration(a.(*apiserver.EgressSelectorConfiguration), b.(*EgressSelectorConfiguration), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*HTTPConnectConfig)(nil), (*apiserver.HTTPConnectConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_v1alpha1_HTTPConnectConfig_To_apiserver_HTTPConnectConfig(a.(*HTTPConnectConfig), b.(*apiserver.HTTPConnectConfig), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.AddGeneratedConversionFunc((*apiserver.HTTPConnectConfig)(nil), (*HTTPConnectConfig)(nil), func(a, b interface{}, scope conversion.Scope) error {
|
||||
return Convert_apiserver_HTTPConnectConfig_To_v1alpha1_HTTPConnectConfig(a.(*apiserver.HTTPConnectConfig), b.(*HTTPConnectConfig), scope)
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -101,3 +141,97 @@ func autoConvert_apiserver_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPlu
|
||||
func Convert_apiserver_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginConfiguration(in *apiserver.AdmissionPluginConfiguration, out *AdmissionPluginConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_AdmissionPluginConfiguration_To_v1alpha1_AdmissionPluginConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_Connection_To_apiserver_Connection(in *Connection, out *apiserver.Connection, s conversion.Scope) error {
|
||||
out.Type = in.Type
|
||||
out.HTTPConnect = (*apiserver.HTTPConnectConfig)(unsafe.Pointer(in.HTTPConnect))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_Connection_To_apiserver_Connection is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_Connection_To_apiserver_Connection(in *Connection, out *apiserver.Connection, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_Connection_To_apiserver_Connection(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_Connection_To_v1alpha1_Connection(in *apiserver.Connection, out *Connection, s conversion.Scope) error {
|
||||
out.Type = in.Type
|
||||
out.HTTPConnect = (*HTTPConnectConfig)(unsafe.Pointer(in.HTTPConnect))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_Connection_To_v1alpha1_Connection is an autogenerated conversion function.
|
||||
func Convert_apiserver_Connection_To_v1alpha1_Connection(in *apiserver.Connection, out *Connection, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_Connection_To_v1alpha1_Connection(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_EgressSelection_To_apiserver_EgressSelection(in *EgressSelection, out *apiserver.EgressSelection, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
if err := Convert_v1alpha1_Connection_To_apiserver_Connection(&in.Connection, &out.Connection, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_EgressSelection_To_apiserver_EgressSelection is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_EgressSelection_To_apiserver_EgressSelection(in *EgressSelection, out *apiserver.EgressSelection, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_EgressSelection_To_apiserver_EgressSelection(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_EgressSelection_To_v1alpha1_EgressSelection(in *apiserver.EgressSelection, out *EgressSelection, s conversion.Scope) error {
|
||||
out.Name = in.Name
|
||||
if err := Convert_apiserver_Connection_To_v1alpha1_Connection(&in.Connection, &out.Connection, s); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_EgressSelection_To_v1alpha1_EgressSelection is an autogenerated conversion function.
|
||||
func Convert_apiserver_EgressSelection_To_v1alpha1_EgressSelection(in *apiserver.EgressSelection, out *EgressSelection, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_EgressSelection_To_v1alpha1_EgressSelection(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_EgressSelectorConfiguration_To_apiserver_EgressSelectorConfiguration(in *EgressSelectorConfiguration, out *apiserver.EgressSelectorConfiguration, s conversion.Scope) error {
|
||||
out.EgressSelections = *(*[]apiserver.EgressSelection)(unsafe.Pointer(&in.EgressSelections))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_EgressSelectorConfiguration_To_apiserver_EgressSelectorConfiguration is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_EgressSelectorConfiguration_To_apiserver_EgressSelectorConfiguration(in *EgressSelectorConfiguration, out *apiserver.EgressSelectorConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_EgressSelectorConfiguration_To_apiserver_EgressSelectorConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_EgressSelectorConfiguration_To_v1alpha1_EgressSelectorConfiguration(in *apiserver.EgressSelectorConfiguration, out *EgressSelectorConfiguration, s conversion.Scope) error {
|
||||
out.EgressSelections = *(*[]EgressSelection)(unsafe.Pointer(&in.EgressSelections))
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_EgressSelectorConfiguration_To_v1alpha1_EgressSelectorConfiguration is an autogenerated conversion function.
|
||||
func Convert_apiserver_EgressSelectorConfiguration_To_v1alpha1_EgressSelectorConfiguration(in *apiserver.EgressSelectorConfiguration, out *EgressSelectorConfiguration, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_EgressSelectorConfiguration_To_v1alpha1_EgressSelectorConfiguration(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_v1alpha1_HTTPConnectConfig_To_apiserver_HTTPConnectConfig(in *HTTPConnectConfig, out *apiserver.HTTPConnectConfig, s conversion.Scope) error {
|
||||
out.URL = in.URL
|
||||
out.CABundle = in.CABundle
|
||||
out.ClientKey = in.ClientKey
|
||||
out.ClientCert = in.ClientCert
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_v1alpha1_HTTPConnectConfig_To_apiserver_HTTPConnectConfig is an autogenerated conversion function.
|
||||
func Convert_v1alpha1_HTTPConnectConfig_To_apiserver_HTTPConnectConfig(in *HTTPConnectConfig, out *apiserver.HTTPConnectConfig, s conversion.Scope) error {
|
||||
return autoConvert_v1alpha1_HTTPConnectConfig_To_apiserver_HTTPConnectConfig(in, out, s)
|
||||
}
|
||||
|
||||
func autoConvert_apiserver_HTTPConnectConfig_To_v1alpha1_HTTPConnectConfig(in *apiserver.HTTPConnectConfig, out *HTTPConnectConfig, s conversion.Scope) error {
|
||||
out.URL = in.URL
|
||||
out.CABundle = in.CABundle
|
||||
out.ClientKey = in.ClientKey
|
||||
out.ClientCert = in.ClientCert
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert_apiserver_HTTPConnectConfig_To_v1alpha1_HTTPConnectConfig is an autogenerated conversion function.
|
||||
func Convert_apiserver_HTTPConnectConfig_To_v1alpha1_HTTPConnectConfig(in *apiserver.HTTPConnectConfig, out *HTTPConnectConfig, s conversion.Scope) error {
|
||||
return autoConvert_apiserver_HTTPConnectConfig_To_v1alpha1_HTTPConnectConfig(in, out, s)
|
||||
}
|
||||
|
@ -76,3 +76,89 @@ func (in *AdmissionPluginConfiguration) DeepCopy() *AdmissionPluginConfiguration
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Connection) DeepCopyInto(out *Connection) {
|
||||
*out = *in
|
||||
if in.HTTPConnect != nil {
|
||||
in, out := &in.HTTPConnect, &out.HTTPConnect
|
||||
*out = new(HTTPConnectConfig)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Connection.
|
||||
func (in *Connection) DeepCopy() *Connection {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Connection)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EgressSelection) DeepCopyInto(out *EgressSelection) {
|
||||
*out = *in
|
||||
in.Connection.DeepCopyInto(&out.Connection)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressSelection.
|
||||
func (in *EgressSelection) DeepCopy() *EgressSelection {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(EgressSelection)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EgressSelectorConfiguration) DeepCopyInto(out *EgressSelectorConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.EgressSelections != nil {
|
||||
in, out := &in.EgressSelections, &out.EgressSelections
|
||||
*out = make([]EgressSelection, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressSelectorConfiguration.
|
||||
func (in *EgressSelectorConfiguration) DeepCopy() *EgressSelectorConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(EgressSelectorConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *EgressSelectorConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPConnectConfig) DeepCopyInto(out *HTTPConnectConfig) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPConnectConfig.
|
||||
func (in *HTTPConnectConfig) DeepCopy() *HTTPConnectConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPConnectConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
@ -76,3 +76,89 @@ func (in *AdmissionPluginConfiguration) DeepCopy() *AdmissionPluginConfiguration
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *Connection) DeepCopyInto(out *Connection) {
|
||||
*out = *in
|
||||
if in.HTTPConnect != nil {
|
||||
in, out := &in.HTTPConnect, &out.HTTPConnect
|
||||
*out = new(HTTPConnectConfig)
|
||||
**out = **in
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Connection.
|
||||
func (in *Connection) DeepCopy() *Connection {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(Connection)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EgressSelection) DeepCopyInto(out *EgressSelection) {
|
||||
*out = *in
|
||||
in.Connection.DeepCopyInto(&out.Connection)
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressSelection.
|
||||
func (in *EgressSelection) DeepCopy() *EgressSelection {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(EgressSelection)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *EgressSelectorConfiguration) DeepCopyInto(out *EgressSelectorConfiguration) {
|
||||
*out = *in
|
||||
out.TypeMeta = in.TypeMeta
|
||||
if in.EgressSelections != nil {
|
||||
in, out := &in.EgressSelections, &out.EgressSelections
|
||||
*out = make([]EgressSelection, len(*in))
|
||||
for i := range *in {
|
||||
(*in)[i].DeepCopyInto(&(*out)[i])
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new EgressSelectorConfiguration.
|
||||
func (in *EgressSelectorConfiguration) DeepCopy() *EgressSelectorConfiguration {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(EgressSelectorConfiguration)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
||||
// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object.
|
||||
func (in *EgressSelectorConfiguration) DeepCopyObject() runtime.Object {
|
||||
if c := in.DeepCopy(); c != nil {
|
||||
return c
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil.
|
||||
func (in *HTTPConnectConfig) DeepCopyInto(out *HTTPConnectConfig) {
|
||||
*out = *in
|
||||
return
|
||||
}
|
||||
|
||||
// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new HTTPConnectConfig.
|
||||
func (in *HTTPConnectConfig) DeepCopy() *HTTPConnectConfig {
|
||||
if in == nil {
|
||||
return nil
|
||||
}
|
||||
out := new(HTTPConnectConfig)
|
||||
in.DeepCopyInto(out)
|
||||
return out
|
||||
}
|
||||
|
@ -62,6 +62,7 @@ func (s *LocationStreamer) InputStream(ctx context.Context, apiVersion, acceptHe
|
||||
if transport == nil {
|
||||
transport = http.DefaultTransport
|
||||
}
|
||||
|
||||
client := &http.Client{
|
||||
Transport: transport,
|
||||
CheckRedirect: s.RedirectChecker,
|
||||
|
@ -11,6 +11,7 @@ go_test(
|
||||
srcs = [
|
||||
"config_selfclient_test.go",
|
||||
"config_test.go",
|
||||
"egress_selector_test.go",
|
||||
"genericapiserver_test.go",
|
||||
"healthz_test.go",
|
||||
],
|
||||
@ -21,9 +22,11 @@ go_test(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/version:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/apiserver:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/example:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/example/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/authorization/authorizer:go_default_library",
|
||||
@ -49,6 +52,7 @@ go_library(
|
||||
"config_selfclient.go",
|
||||
"deprecated_insecure_serving.go",
|
||||
"doc.go",
|
||||
"egress_selector.go",
|
||||
"genericapiserver.go",
|
||||
"handler.go",
|
||||
"healthz.go",
|
||||
@ -70,6 +74,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/clock:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/errors:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/sets:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation:go_default_library",
|
||||
@ -79,6 +84,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/namespace/lifecycle:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/mutating:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/admission/plugin/webhook/validating:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/apiserver:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/apiserver/install:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/audit:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/audit/policy:go_default_library",
|
||||
@ -133,6 +139,7 @@ filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [
|
||||
":package-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/egressselector:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/filters:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/healthz:all-srcs",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/httplog:all-srcs",
|
||||
|
@ -29,7 +29,7 @@ import (
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
jsonpatch "github.com/evanphx/json-patch"
|
||||
"github.com/evanphx/json-patch"
|
||||
"github.com/go-openapi/spec"
|
||||
"github.com/pborman/uuid"
|
||||
|
||||
@ -94,6 +94,11 @@ type Config struct {
|
||||
// This is required for proper functioning of the PostStartHooks on a GenericAPIServer
|
||||
// TODO: move into SecureServing(WithLoopback) as soon as insecure serving is gone
|
||||
LoopbackClientConfig *restclient.Config
|
||||
|
||||
// EgressSelector provides a lookup mechanism for dialing outbound connections.
|
||||
// It does so based on a EgressSelectorConfiguration which was read at startup.
|
||||
EgressSelector *EgressSelector
|
||||
|
||||
// RuleResolver is required to get the list of rules that apply to a given user
|
||||
// in a given namespace
|
||||
RuleResolver authorizer.RuleResolver
|
||||
|
192
staging/src/k8s.io/apiserver/pkg/server/egress_selector.go
Normal file
192
staging/src/k8s.io/apiserver/pkg/server/egress_selector.go
Normal file
@ -0,0 +1,192 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/klog"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
)
|
||||
|
||||
var directDialer utilnet.DialFunc = http.DefaultTransport.(*http.Transport).DialContext
|
||||
|
||||
type EgressSelector struct {
|
||||
egressToDialer map[EgressType]utilnet.DialFunc
|
||||
}
|
||||
|
||||
// EgressType is an indicator of which egress selection should be used for sending traffic.
|
||||
// See https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/20190226-network-proxy.md#network-context
|
||||
type EgressType int
|
||||
|
||||
const (
|
||||
// Master is the EgressType for traffic intended to go to the control plane.
|
||||
Master EgressType = iota
|
||||
// Etcd is the EgressType for traffic intended to go to Kubernetes persistence store.
|
||||
Etcd
|
||||
// Cluster is the EgressType for traffic intended to go to the system being managed by Kubernetes.
|
||||
Cluster
|
||||
)
|
||||
|
||||
// NetworkContext is the struct used by Kubernetes API Server to indicate where it intends traffic to be sent.
|
||||
type NetworkContext struct {
|
||||
// EgressSelectionName is the unique name of the
|
||||
// EgressSelectorConfiguration which determines
|
||||
// the network we route the traffic to.
|
||||
EgressSelectionName EgressType
|
||||
}
|
||||
|
||||
// EgressSelectorLookup is the interface to get the dialer function for the network context.
|
||||
type EgressSelectorLookup func(networkContext NetworkContext) (utilnet.DialFunc, error)
|
||||
|
||||
func (s EgressType) String() string {
|
||||
switch s {
|
||||
case Master:
|
||||
return "master"
|
||||
case Etcd:
|
||||
return "etcd"
|
||||
case Cluster:
|
||||
return "cluster"
|
||||
default:
|
||||
return "invalid"
|
||||
}
|
||||
}
|
||||
|
||||
func lookupServiceName(name string) (EgressType, error) {
|
||||
switch strings.ToLower(name) {
|
||||
case "master":
|
||||
return Master, nil
|
||||
case "etcd":
|
||||
return Etcd, nil
|
||||
case "cluster":
|
||||
return Cluster, nil
|
||||
}
|
||||
return -1, fmt.Errorf("unrecognized service name %s", name)
|
||||
}
|
||||
|
||||
func createConnectDialer(connectConfig *apiserver.HTTPConnectConfig) (utilnet.DialFunc, error) {
|
||||
clientCert := connectConfig.ClientCert
|
||||
clientKey := connectConfig.ClientKey
|
||||
caCert := connectConfig.CABundle
|
||||
proxyURL, err := url.Parse(connectConfig.URL)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid proxy server url %q: %v", connectConfig.URL, err)
|
||||
}
|
||||
proxyAddress := proxyURL.Host
|
||||
|
||||
clientCerts, err := tls.LoadX509KeyPair(clientCert, clientKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read key pair %s & %s, got %v", clientCert, clientKey, err)
|
||||
}
|
||||
certPool := x509.NewCertPool()
|
||||
certBytes, err := ioutil.ReadFile(caCert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read cert file %s, got %v", caCert, err)
|
||||
}
|
||||
ok := certPool.AppendCertsFromPEM(certBytes)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("failed to append CA cert to the cert pool")
|
||||
}
|
||||
contextDialer := func(ctx context.Context, network, addr string) (net.Conn, error) {
|
||||
klog.V(4).Infof("Sending request to %q.", addr)
|
||||
proxyConn, err := tls.Dial("tcp", proxyAddress,
|
||||
&tls.Config{
|
||||
Certificates: []tls.Certificate{clientCerts},
|
||||
RootCAs: certPool,
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("dialing proxy %q failed: %v", proxyAddress, err)
|
||||
}
|
||||
fmt.Fprintf(proxyConn, "CONNECT %s HTTP/1.1\r\nHost: %s\r\n\r\n", addr, "127.0.0.1")
|
||||
br := bufio.NewReader(proxyConn)
|
||||
res, err := http.ReadResponse(br, nil)
|
||||
if err != nil {
|
||||
proxyConn.Close()
|
||||
return nil, fmt.Errorf("reading HTTP response from CONNECT to %s via proxy %s failed: %v",
|
||||
addr, proxyAddress, err)
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
proxyConn.Close()
|
||||
return nil, fmt.Errorf("proxy error from %s while dialing %s, code %d: %v",
|
||||
proxyAddress, addr, res.StatusCode, res.Status)
|
||||
}
|
||||
|
||||
// It's safe to discard the bufio.Reader here and return the
|
||||
// original TCP conn directly because we only use this for
|
||||
// TLS, and in TLS the client speaks first, so we know there's
|
||||
// no unbuffered data. But we can double-check.
|
||||
if br.Buffered() > 0 {
|
||||
proxyConn.Close()
|
||||
return nil, fmt.Errorf("unexpected %d bytes of buffered data from CONNECT proxy %q",
|
||||
br.Buffered(), proxyAddress)
|
||||
}
|
||||
klog.V(4).Infof("About to proxy request to %s over %s.", addr, proxyAddress)
|
||||
return proxyConn, nil
|
||||
}
|
||||
return contextDialer, nil
|
||||
}
|
||||
|
||||
// NewEgressSelector configures lookup mechanism for Lookup.
|
||||
// It does so based on a EgressSelectorConfiguration which was read at startup.
|
||||
func NewEgressSelector(config *apiserver.EgressSelectorConfiguration) (*EgressSelector, error) {
|
||||
if config == nil || config.EgressSelections == nil {
|
||||
// No Connection Services configured, leaving the serviceMap empty, will return default dialer.
|
||||
return nil, nil
|
||||
}
|
||||
cs := &EgressSelector{
|
||||
egressToDialer: make(map[EgressType]utilnet.DialFunc),
|
||||
}
|
||||
for _, service := range config.EgressSelections {
|
||||
name, err := lookupServiceName(service.Name)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
switch service.Connection.Type {
|
||||
case "http-connect":
|
||||
contextDialer, err := createConnectDialer(service.Connection.HTTPConnect)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to create http-connect dialer: %v", err)
|
||||
}
|
||||
cs.egressToDialer[name] = contextDialer
|
||||
case "direct":
|
||||
cs.egressToDialer[name] = directDialer
|
||||
default:
|
||||
return nil, fmt.Errorf("unrecognized service connection type %q", service.Connection.Type)
|
||||
}
|
||||
}
|
||||
return cs, nil
|
||||
}
|
||||
|
||||
// Lookup gets the dialer function for the network context.
|
||||
// This is configured for the Kubernetes API Server at startup.
|
||||
func (cs *EgressSelector) Lookup(networkContext NetworkContext) (utilnet.DialFunc, error) {
|
||||
if cs.egressToDialer == nil {
|
||||
// The round trip wrapper will over-ride the dialContext method appropriately
|
||||
return nil, nil
|
||||
}
|
||||
return cs.egressToDialer[networkContext.EgressSelectionName], nil
|
||||
}
|
183
staging/src/k8s.io/apiserver/pkg/server/egress_selector_test.go
Normal file
183
staging/src/k8s.io/apiserver/pkg/server/egress_selector_test.go
Normal file
@ -0,0 +1,183 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
*/
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||
)
|
||||
|
||||
type fakeEgressSelection struct {
|
||||
directDialerCalled bool
|
||||
}
|
||||
|
||||
func TestEgressSelector(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
input *apiserver.EgressSelectorConfiguration
|
||||
services []struct {
|
||||
egressType EgressType
|
||||
validateDialer func(dialer utilnet.DialFunc, s *fakeEgressSelection) (bool, error)
|
||||
lookupError *string
|
||||
dialerError *string
|
||||
}
|
||||
expectedError *string
|
||||
}{
|
||||
{
|
||||
name: "direct",
|
||||
input: &apiserver.EgressSelectorConfiguration{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "",
|
||||
APIVersion: "",
|
||||
},
|
||||
EgressSelections: []apiserver.EgressSelection{
|
||||
{
|
||||
Name: "cluster",
|
||||
Connection: apiserver.Connection{
|
||||
Type: "direct",
|
||||
HTTPConnect: &apiserver.HTTPConnectConfig{
|
||||
URL: "",
|
||||
CABundle: "",
|
||||
ClientKey: "",
|
||||
ClientCert: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "master",
|
||||
Connection: apiserver.Connection{
|
||||
Type: "direct",
|
||||
HTTPConnect: &apiserver.HTTPConnectConfig{
|
||||
URL: "",
|
||||
CABundle: "",
|
||||
ClientKey: "",
|
||||
ClientCert: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "etcd",
|
||||
Connection: apiserver.Connection{
|
||||
Type: "direct",
|
||||
HTTPConnect: &apiserver.HTTPConnectConfig{
|
||||
URL: "",
|
||||
CABundle: "",
|
||||
ClientKey: "",
|
||||
ClientCert: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
services: []struct {
|
||||
egressType EgressType
|
||||
validateDialer func(dialer utilnet.DialFunc, s *fakeEgressSelection) (bool, error)
|
||||
lookupError *string
|
||||
dialerError *string
|
||||
}{
|
||||
{
|
||||
Cluster,
|
||||
validateDirectDialer,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
Master,
|
||||
validateDirectDialer,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
{
|
||||
Etcd,
|
||||
validateDirectDialer,
|
||||
nil,
|
||||
nil,
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
// Setup the various pieces such as the fake dialer prior to initializing the egress selector.
|
||||
// Go doesn't allow function pointer comparison, nor does its reflect package
|
||||
// So overriding the default dialer to detect if it is returned.
|
||||
fake := &fakeEgressSelection{}
|
||||
directDialer = fake.fakeDirectDialer
|
||||
cs, err := NewEgressSelector(tc.input)
|
||||
if err == nil && tc.expectedError != nil {
|
||||
t.Errorf("calling NewEgressSelector expected error: %s, did not get it", *tc.expectedError)
|
||||
}
|
||||
if err != nil && tc.expectedError == nil {
|
||||
t.Errorf("unexpected error calling NewEgressSelector got: %#v", err)
|
||||
}
|
||||
if err != nil && tc.expectedError != nil && err.Error() != *tc.expectedError {
|
||||
t.Errorf("calling NewEgressSelector expected error: %s, got %#v", *tc.expectedError, err)
|
||||
}
|
||||
|
||||
for _, service := range tc.services {
|
||||
networkContext := NetworkContext{EgressSelectionName: service.egressType}
|
||||
dialer, lookupErr := cs.Lookup(networkContext)
|
||||
if lookupErr == nil && service.lookupError != nil {
|
||||
t.Errorf("calling Lookup expected error: %s, did not get it", *service.lookupError)
|
||||
}
|
||||
if lookupErr != nil && service.lookupError == nil {
|
||||
t.Errorf("unexpected error calling Lookup got: %#v", lookupErr)
|
||||
}
|
||||
if lookupErr != nil && service.lookupError != nil && lookupErr.Error() != *service.lookupError {
|
||||
t.Errorf("calling Lookup expected error: %s, got %#v", *service.lookupError, lookupErr)
|
||||
}
|
||||
fake.directDialerCalled = false
|
||||
ok, dialerErr := service.validateDialer(dialer, fake)
|
||||
if dialerErr == nil && service.dialerError != nil {
|
||||
t.Errorf("calling Lookup expected error: %s, did not get it", *service.dialerError)
|
||||
}
|
||||
if dialerErr != nil && service.dialerError == nil {
|
||||
t.Errorf("unexpected error calling Lookup got: %#v", dialerErr)
|
||||
}
|
||||
if dialerErr != nil && service.dialerError != nil && dialerErr.Error() != *service.dialerError {
|
||||
t.Errorf("calling Lookup expected error: %s, got %#v", *service.dialerError, dialerErr)
|
||||
}
|
||||
if !ok {
|
||||
t.Errorf("Could not validate dialer for service %q", service.egressType)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (s *fakeEgressSelection) fakeDirectDialer(ctx context.Context, network, address string) (net.Conn, error) {
|
||||
s.directDialerCalled = true
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func validateDirectDialer(dialer utilnet.DialFunc, s *fakeEgressSelection) (bool, error) {
|
||||
conn, err := dialer(context.Background(), "tcp", "127.0.0.1:8080")
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if conn != nil {
|
||||
return false, nil
|
||||
}
|
||||
return s.directDialerCalled, nil
|
||||
}
|
42
staging/src/k8s.io/apiserver/pkg/server/egressselector/BUILD
Normal file
42
staging/src/k8s.io/apiserver/pkg/server/egressselector/BUILD
Normal file
@ -0,0 +1,42 @@
|
||||
load("@io_bazel_rules_go//go:def.bzl", "go_library", "go_test")
|
||||
|
||||
go_library(
|
||||
name = "go_default_library",
|
||||
srcs = ["config.go"],
|
||||
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/pkg/server/egressselector",
|
||||
importpath = "k8s.io/apiserver/pkg/server/egressselector",
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/runtime:go_default_library",
|
||||
"//staging/src/k8s.io/apimachinery/pkg/util/validation/field:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/apiserver:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/apiserver/install:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/apiserver/v1alpha1:go_default_library",
|
||||
"//vendor/k8s.io/utils/path:go_default_library",
|
||||
"//vendor/sigs.k8s.io/yaml:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "package-srcs",
|
||||
srcs = glob(["**"]),
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:private"],
|
||||
)
|
||||
|
||||
filegroup(
|
||||
name = "all-srcs",
|
||||
srcs = [":package-srcs"],
|
||||
tags = ["automanaged"],
|
||||
visibility = ["//visibility:public"],
|
||||
)
|
||||
|
||||
go_test(
|
||||
name = "go_default_test",
|
||||
srcs = ["config_test.go"],
|
||||
embed = [":go_default_library"],
|
||||
deps = [
|
||||
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/apis/apiserver:go_default_library",
|
||||
],
|
||||
)
|
179
staging/src/k8s.io/apiserver/pkg/server/egressselector/config.go
Normal file
179
staging/src/k8s.io/apiserver/pkg/server/egressselector/config.go
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
*/
|
||||
|
||||
package egressselector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strings"
|
||||
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/util/validation/field"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver/install"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver/v1alpha1"
|
||||
"k8s.io/utils/path"
|
||||
"sigs.k8s.io/yaml"
|
||||
)
|
||||
|
||||
var cfgScheme = runtime.NewScheme()
|
||||
|
||||
func init() {
|
||||
install.Install(cfgScheme)
|
||||
}
|
||||
|
||||
// ReadEgressSelectorConfiguration reads the egress selector configuration at the specified path.
|
||||
// It returns the loaded egress selector configuration if the input file aligns with the required syntax.
|
||||
// If it does not align with the provided syntax, it returns a default configuration which should function as a no-op.
|
||||
// It does this by returning a nil configuration, which preserves backward compatibility.
|
||||
// This works because prior to this there was no egress selector configuration.
|
||||
// It returns an error if the file did not exist.
|
||||
func ReadEgressSelectorConfiguration(configFilePath string) (*apiserver.EgressSelectorConfiguration, error) {
|
||||
if configFilePath == "" {
|
||||
return nil, nil
|
||||
}
|
||||
// a file was provided, so we just read it.
|
||||
data, err := ioutil.ReadFile(configFilePath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read egress selector configuration from %q [%v]", configFilePath, err)
|
||||
}
|
||||
var decodedConfig v1alpha1.EgressSelectorConfiguration
|
||||
err = yaml.Unmarshal(data, &decodedConfig)
|
||||
if err != nil {
|
||||
// we got an error where the decode wasn't related to a missing type
|
||||
return nil, err
|
||||
}
|
||||
if decodedConfig.Kind != "EgressSelectorConfiguration" {
|
||||
return nil, fmt.Errorf("invalid service configuration object %q", decodedConfig.Kind)
|
||||
}
|
||||
config, err := cfgScheme.ConvertToVersion(&decodedConfig, apiserver.SchemeGroupVersion)
|
||||
if err != nil {
|
||||
// we got an error where the decode wasn't related to a missing type
|
||||
return nil, err
|
||||
}
|
||||
if internalConfig, ok := config.(*apiserver.EgressSelectorConfiguration); ok {
|
||||
return internalConfig, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unable to convert %T to *apiserver.EgressSelectorConfiguration", config)
|
||||
}
|
||||
|
||||
// ValidateEgressSelectorConfiguration checks the apiserver.EgressSelectorConfiguration for
|
||||
// common configuration errors. It will return error for problems such as configuring mtls/cert
|
||||
// settings for protocol which do not support security. It will also try to catch errors such as
|
||||
// incorrect file paths. It will return nil if it does not find anything wrong.
|
||||
func ValidateEgressSelectorConfiguration(config *apiserver.EgressSelectorConfiguration) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if config == nil {
|
||||
return allErrs // Treating a nil configuration as valid
|
||||
}
|
||||
for _, service := range config.EgressSelections {
|
||||
base := field.NewPath("service", "connection")
|
||||
switch service.Connection.Type {
|
||||
case "direct":
|
||||
allErrs = append(allErrs, validateDirectConnection(service.Connection, base)...)
|
||||
case "http-connect":
|
||||
allErrs = append(allErrs, validateHTTPConnection(service.Connection, base)...)
|
||||
default:
|
||||
allErrs = append(allErrs, field.NotSupported(
|
||||
base.Child("type"),
|
||||
service.Connection.Type,
|
||||
[]string{"direct", "http-connect"}))
|
||||
}
|
||||
}
|
||||
|
||||
return allErrs
|
||||
}
|
||||
|
||||
func validateDirectConnection(connection apiserver.Connection, fldPath *field.Path) field.ErrorList {
|
||||
if connection.HTTPConnect != nil {
|
||||
return field.ErrorList{field.Invalid(
|
||||
fldPath.Child("httpConnect"),
|
||||
"direct",
|
||||
"httpConnect config should be absent for direct connect"),
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateHTTPConnection(connection apiserver.Connection, fldPath *field.Path) field.ErrorList {
|
||||
allErrs := field.ErrorList{}
|
||||
if connection.HTTPConnect == nil {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect"),
|
||||
"nil",
|
||||
"httpConnect config should be present for http-connect"))
|
||||
} else if strings.HasPrefix(connection.HTTPConnect.URL, "https://") {
|
||||
if connection.HTTPConnect.CABundle == "" {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "caBundle"),
|
||||
"nil",
|
||||
"http-connect via https requires caBundle"))
|
||||
} else if exists, err := path.Exists(path.CheckFollowSymlink, connection.HTTPConnect.CABundle); exists == false || err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "caBundle"),
|
||||
connection.HTTPConnect.CABundle,
|
||||
"http-connect ca bundle does not exist"))
|
||||
}
|
||||
if connection.HTTPConnect.ClientCert == "" {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "clientCert"),
|
||||
"nil",
|
||||
"http-connect via https requires clientCert"))
|
||||
} else if exists, err := path.Exists(path.CheckFollowSymlink, connection.HTTPConnect.ClientCert); exists == false || err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "clientCert"),
|
||||
connection.HTTPConnect.ClientCert,
|
||||
"http-connect client cert does not exist"))
|
||||
}
|
||||
if connection.HTTPConnect.ClientKey == "" {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "clientKey"),
|
||||
"nil",
|
||||
"http-connect via https requires clientKey"))
|
||||
} else if exists, err := path.Exists(path.CheckFollowSymlink, connection.HTTPConnect.ClientKey); exists == false || err != nil {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "clientKey"),
|
||||
connection.HTTPConnect.ClientKey,
|
||||
"http-connect client key does not exist"))
|
||||
}
|
||||
} else if strings.HasPrefix(connection.HTTPConnect.URL, "http://") {
|
||||
if connection.HTTPConnect.CABundle != "" {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "caBundle"),
|
||||
connection.HTTPConnect.CABundle,
|
||||
"http-connect via http does not support caBundle"))
|
||||
}
|
||||
if connection.HTTPConnect.ClientCert != "" {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "clientCert"),
|
||||
connection.HTTPConnect.ClientCert,
|
||||
"http-connect via http does not support clientCert"))
|
||||
}
|
||||
if connection.HTTPConnect.ClientKey != "" {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "clientKey"),
|
||||
connection.HTTPConnect.ClientKey,
|
||||
"http-connect via http does not support clientKey"))
|
||||
}
|
||||
} else {
|
||||
allErrs = append(allErrs, field.Invalid(
|
||||
fldPath.Child("httpConnect", "url"),
|
||||
connection.HTTPConnect.URL,
|
||||
"supported connection protocols are http:// and https://"))
|
||||
}
|
||||
return allErrs
|
||||
}
|
@ -0,0 +1,214 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
*/
|
||||
|
||||
package egressselector
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apiserver/pkg/apis/apiserver"
|
||||
)
|
||||
|
||||
func strptr(s string) *string {
|
||||
return &s
|
||||
}
|
||||
|
||||
func TestReadEgressSelectorConfiguration(t *testing.T) {
|
||||
testcases := []struct {
|
||||
name string
|
||||
contents string
|
||||
createFile bool
|
||||
expectedResult *apiserver.EgressSelectorConfiguration
|
||||
expectedError *string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
createFile: true,
|
||||
contents: ``,
|
||||
expectedResult: nil,
|
||||
expectedError: strptr("invalid service configuration object \"\""),
|
||||
},
|
||||
{
|
||||
name: "absent",
|
||||
createFile: false,
|
||||
contents: ``,
|
||||
expectedResult: nil,
|
||||
expectedError: strptr("unable to read egress selector configuration from \"test-egress-selector-config-absent\" [open test-egress-selector-config-absent: no such file or directory]"),
|
||||
},
|
||||
{
|
||||
name: "v1alpha1",
|
||||
createFile: true,
|
||||
contents: `
|
||||
apiVersion: apiserver.k8s.io/v1alpha1
|
||||
kind: EgressSelectorConfiguration
|
||||
egressSelections:
|
||||
- name: "cluster"
|
||||
connection:
|
||||
type: "http-connect"
|
||||
httpConnect:
|
||||
url: "https://127.0.0.1:8131"
|
||||
caBundle: "/etc/srv/kubernetes/pki/konnectivity-server/ca.crt"
|
||||
clientKey: "/etc/srv/kubernetes/pki/konnectivity-server/client.key"
|
||||
clientCert: "/etc/srv/kubernetes/pki/konnectivity-server/client.crt"
|
||||
- name: "master"
|
||||
connection:
|
||||
type: "http-connect"
|
||||
httpConnect:
|
||||
url: "https://127.0.0.1:8132"
|
||||
caBundle: "/etc/srv/kubernetes/pki/konnectivity-server-master/ca.crt"
|
||||
clientKey: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.key"
|
||||
clientCert: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.crt"
|
||||
- name: "etcd"
|
||||
connection:
|
||||
type: "direct"
|
||||
`,
|
||||
expectedResult: &apiserver.EgressSelectorConfiguration{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
Kind: "",
|
||||
APIVersion: "",
|
||||
},
|
||||
EgressSelections: []apiserver.EgressSelection{
|
||||
{
|
||||
Name: "cluster",
|
||||
Connection: apiserver.Connection{
|
||||
Type: "http-connect",
|
||||
HTTPConnect: &apiserver.HTTPConnectConfig{
|
||||
URL: "https://127.0.0.1:8131",
|
||||
CABundle: "/etc/srv/kubernetes/pki/konnectivity-server/ca.crt",
|
||||
ClientKey: "/etc/srv/kubernetes/pki/konnectivity-server/client.key",
|
||||
ClientCert: "/etc/srv/kubernetes/pki/konnectivity-server/client.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "master",
|
||||
Connection: apiserver.Connection{
|
||||
Type: "http-connect",
|
||||
HTTPConnect: &apiserver.HTTPConnectConfig{
|
||||
URL: "https://127.0.0.1:8132",
|
||||
CABundle: "/etc/srv/kubernetes/pki/konnectivity-server-master/ca.crt",
|
||||
ClientKey: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.key",
|
||||
ClientCert: "/etc/srv/kubernetes/pki/konnectivity-server-master/client.crt",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "etcd",
|
||||
Connection: apiserver.Connection{
|
||||
Type: "direct",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: nil,
|
||||
},
|
||||
{
|
||||
name: "wrong_type",
|
||||
createFile: true,
|
||||
contents: `
|
||||
apiVersion: apps/v1
|
||||
kind: DaemonSet
|
||||
metadata:
|
||||
labels:
|
||||
addonmanager.kubernetes.io/mode: Reconcile
|
||||
k8s-app: konnectivity-agent
|
||||
namespace: kube-system
|
||||
name: proxy-agent
|
||||
spec:
|
||||
selector:
|
||||
matchLabels:
|
||||
k8s-app: konnectivity-agent
|
||||
updateStrategy:
|
||||
type: RollingUpdate
|
||||
template:
|
||||
metadata:
|
||||
labels:
|
||||
k8s-app: proxy-agent
|
||||
annotations:
|
||||
scheduler.alpha.kubernetes.io/critical-pod: ''
|
||||
spec:
|
||||
priorityClassName: system-cluster-critical
|
||||
# Necessary to reboot node
|
||||
hostPID: true
|
||||
volumes:
|
||||
- name: pki
|
||||
hostPath:
|
||||
path: /etc/srv/kubernetes/pki/konnectivity-agent
|
||||
containers:
|
||||
- image: gcr.io/google-containers/proxy-agent:v0.0.3
|
||||
name: proxy-agent
|
||||
command: ["/proxy-agent"]
|
||||
args: ["--caCert=/etc/srv/kubernetes/pki/proxy-agent/ca.crt", "--agentCert=/etc/srv/kubernetes/pki/proxy-agent/client.crt", "--agentKey=/etc/srv/kubernetes/pki/proxy-agent/client.key", "--proxyServerHost=127.0.0.1", "--proxyServerPort=8132"]
|
||||
securityContext:
|
||||
capabilities:
|
||||
add: ["SYS_BOOT"]
|
||||
env:
|
||||
- name: wrong-type
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.name
|
||||
- name: kube-system
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
resources:
|
||||
limits:
|
||||
cpu: 50m
|
||||
memory: 30Mi
|
||||
volumeMounts:
|
||||
- name: pki
|
||||
mountPath: /etc/srv/kubernetes/pki/konnectivity-agent
|
||||
`,
|
||||
expectedResult: nil,
|
||||
expectedError: strptr("invalid service configuration object \"DaemonSet\""),
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range testcases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
proxyConfig := fmt.Sprintf("test-egress-selector-config-%s", tc.name)
|
||||
if tc.createFile {
|
||||
f, err := ioutil.TempFile("", proxyConfig)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer os.Remove(f.Name())
|
||||
if err := ioutil.WriteFile(f.Name(), []byte(tc.contents), os.FileMode(0755)); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
proxyConfig = f.Name()
|
||||
}
|
||||
config, err := ReadEgressSelectorConfiguration(proxyConfig)
|
||||
if err == nil && tc.expectedError != nil {
|
||||
t.Errorf("calling ReadEgressSelectorConfiguration expected error: %s, did not get it", *tc.expectedError)
|
||||
}
|
||||
if err != nil && tc.expectedError == nil {
|
||||
t.Errorf("unexpected error calling ReadEgressSelectorConfiguration got: %#v", err)
|
||||
}
|
||||
if err != nil && tc.expectedError != nil && err.Error() != *tc.expectedError {
|
||||
t.Errorf("calling ReadEgressSelectorConfiguration expected error: %s, got %#v", *tc.expectedError, err)
|
||||
}
|
||||
if !reflect.DeepEqual(config, tc.expectedResult) {
|
||||
t.Errorf("problem with configuration returned from ReadEgressSelectorConfiguration expected: %#v, got: %#v", tc.expectedResult, config)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -11,6 +11,7 @@ go_library(
|
||||
"coreapi.go",
|
||||
"deprecated_insecure_serving.go",
|
||||
"doc.go",
|
||||
"egress_selector.go",
|
||||
"etcd.go",
|
||||
"events.go",
|
||||
"feature.go",
|
||||
@ -56,6 +57,7 @@ go_library(
|
||||
"//staging/src/k8s.io/apiserver/pkg/registry/generic:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/registry/generic/registry:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/egressselector:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/healthz:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/options/encryptionconfig:go_default_library",
|
||||
"//staging/src/k8s.io/apiserver/pkg/server/resourceconfig:go_default_library",
|
||||
@ -83,6 +85,7 @@ go_library(
|
||||
"//vendor/gopkg.in/natefinch/lumberjack.v2:go_default_library",
|
||||
"//vendor/k8s.io/klog:go_default_library",
|
||||
"//vendor/k8s.io/kube-openapi/pkg/common:go_default_library",
|
||||
"//vendor/k8s.io/utils/path:go_default_library",
|
||||
],
|
||||
)
|
||||
|
||||
|
@ -0,0 +1,92 @@
|
||||
/*
|
||||
Copyright 2019 The Kubernetes Authors.
|
||||
|
||||
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
|
||||
|
||||
http://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.
|
||||
*/
|
||||
|
||||
package options
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/spf13/pflag"
|
||||
"k8s.io/utils/path"
|
||||
|
||||
"k8s.io/apiserver/pkg/server"
|
||||
"k8s.io/apiserver/pkg/server/egressselector"
|
||||
)
|
||||
|
||||
// EgressSelectorOptions holds the api server egress selector options.
|
||||
// See https://github.com/kubernetes/enhancements/blob/master/keps/sig-api-machinery/20190226-network-proxy.md
|
||||
type EgressSelectorOptions struct {
|
||||
// ConfigFile is the file path with api-server egress selector configuration.
|
||||
ConfigFile string
|
||||
}
|
||||
|
||||
// NewEgressSelectorOptions creates a new instance of EgressSelectorOptions
|
||||
//
|
||||
// The option is to point to a configuration file for egress/konnectivity.
|
||||
// This determines which types of requests use egress/konnectivity and how they use it.
|
||||
// If empty the API Server will attempt to connect directly using the network.
|
||||
func NewEgressSelectorOptions() *EgressSelectorOptions {
|
||||
return &EgressSelectorOptions{}
|
||||
}
|
||||
|
||||
// AddFlags adds flags related to admission for a specific APIServer to the specified FlagSet
|
||||
func (o *EgressSelectorOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
if o == nil {
|
||||
return
|
||||
}
|
||||
|
||||
fs.StringVar(&o.ConfigFile, "egress-selector-config-file", o.ConfigFile,
|
||||
"File with apiserver egress selector configuration.")
|
||||
}
|
||||
|
||||
// ApplyTo adds the egress selector settings to the server configuration.
|
||||
// In case egress selector settings were not provided by a cluster-admin
|
||||
// they will be prepared from the recommended/default/no-op values.
|
||||
func (o *EgressSelectorOptions) ApplyTo(c *server.Config) error {
|
||||
if o == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
npConfig, err := egressselector.ReadEgressSelectorConfiguration(o.ConfigFile)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read egress selector config: %v", err)
|
||||
}
|
||||
errs := egressselector.ValidateEgressSelectorConfiguration(npConfig)
|
||||
if len(errs) > 0 {
|
||||
return fmt.Errorf("failed to validate egress selector configuration: %v", errs.ToAggregate())
|
||||
}
|
||||
|
||||
cs, err := server.NewEgressSelector(npConfig)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to setup egress selector with config %#v: %v", npConfig, err)
|
||||
}
|
||||
c.EgressSelector = cs
|
||||
return nil
|
||||
}
|
||||
|
||||
// Validate verifies flags passed to EgressSelectorOptions.
|
||||
func (o *EgressSelectorOptions) Validate() []error {
|
||||
if o == nil || o.ConfigFile == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
errs := []error{}
|
||||
|
||||
if exists, err := path.Exists(path.CheckFollowSymlink, o.ConfigFile); exists == false || err != nil {
|
||||
errs = append(errs, fmt.Errorf("egress-selector-config-file %s does not exist", o.ConfigFile))
|
||||
}
|
||||
|
||||
return errs
|
||||
}
|
@ -44,6 +44,8 @@ type RecommendedOptions struct {
|
||||
// ProcessInfo is used to identify events created by the server.
|
||||
ProcessInfo *ProcessInfo
|
||||
Webhook *WebhookOptions
|
||||
// API Server Egress Selector is used to control outbound traffic from the API Server
|
||||
EgressSelector *EgressSelectorOptions
|
||||
}
|
||||
|
||||
func NewRecommendedOptions(prefix string, codec runtime.Codec, processInfo *ProcessInfo) *RecommendedOptions {
|
||||
@ -67,6 +69,7 @@ func NewRecommendedOptions(prefix string, codec runtime.Codec, processInfo *Proc
|
||||
Admission: NewAdmissionOptions(),
|
||||
ProcessInfo: processInfo,
|
||||
Webhook: NewWebhookOptions(),
|
||||
EgressSelector: NewEgressSelectorOptions(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -79,6 +82,7 @@ func (o *RecommendedOptions) AddFlags(fs *pflag.FlagSet) {
|
||||
o.Features.AddFlags(fs)
|
||||
o.CoreAPI.AddFlags(fs)
|
||||
o.Admission.AddFlags(fs)
|
||||
o.EgressSelector.AddFlags(fs)
|
||||
}
|
||||
|
||||
// ApplyTo adds RecommendedOptions to the server configuration.
|
||||
@ -110,6 +114,9 @@ func (o *RecommendedOptions) ApplyTo(config *server.RecommendedConfig) error {
|
||||
} else if err := o.Admission.ApplyTo(&config.Config, config.SharedInformerFactory, config.ClientConfig, initializers...); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := o.EgressSelector.ApplyTo(&config.Config); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
@ -124,6 +131,7 @@ func (o *RecommendedOptions) Validate() []error {
|
||||
errors = append(errors, o.Features.Validate()...)
|
||||
errors = append(errors, o.CoreAPI.Validate()...)
|
||||
errors = append(errors, o.Admission.Validate()...)
|
||||
errors = append(errors, o.EgressSelector.Validate()...)
|
||||
|
||||
return errors
|
||||
}
|
||||
|
1
staging/src/k8s.io/kube-aggregator/go.sum
generated
1
staging/src/k8s.io/kube-aggregator/go.sum
generated
@ -171,6 +171,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss=
|
||||
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.4 h1:S0tLZ3VOKl2Te0hpq8+ke0eSJPfCnNTPiDlsfwi1/NE=
|
||||
|
1
staging/src/k8s.io/sample-apiserver/go.sum
generated
1
staging/src/k8s.io/sample-apiserver/go.sum
generated
@ -168,6 +168,7 @@ github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6Mwd
|
||||
github.com/soheilhy/cmux v0.1.3 h1:09wy7WZk4AqO03yH85Ex1X+Uo3vDsil3Fa9AgF8Emss=
|
||||
github.com/soheilhy/cmux v0.1.3/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.2.2 h1:5jhuqJyZCZf2JRofRvN/nIFgIWNzPa3/Vz8mYylgbWc=
|
||||
github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v0.0.4 h1:S0tLZ3VOKl2Te0hpq8+ke0eSJPfCnNTPiDlsfwi1/NE=
|
||||
|
1
vendor/modules.txt
vendored
1
vendor/modules.txt
vendored
@ -1257,6 +1257,7 @@ k8s.io/apiserver/pkg/registry/generic/testing
|
||||
k8s.io/apiserver/pkg/registry/rest
|
||||
k8s.io/apiserver/pkg/registry/rest/resttest
|
||||
k8s.io/apiserver/pkg/server
|
||||
k8s.io/apiserver/pkg/server/egressselector
|
||||
k8s.io/apiserver/pkg/server/filters
|
||||
k8s.io/apiserver/pkg/server/healthz
|
||||
k8s.io/apiserver/pkg/server/httplog
|
||||
|
Loading…
Reference in New Issue
Block a user