make the webhook generic

This commit is contained in:
wiaamm 2025-06-29 20:36:15 +03:00
parent 882dc4c187
commit a712ff8442

View File

@ -15,6 +15,7 @@ app = Flask(__name__)
AGENT_IMAGE = os.getenv('AGENT_IMAGE', 'ghcr.io/openappsec/agent')
AGENT_TAG = os.getenv('AGENT_TAG', 'latest')
AGENT_CPU = os.getenv('AGENT_CPU', '200m')
AGENT_KIND = os.getenv('AGENT_KIND', 'istio')
INIT_CONTAINER_IMAGE = os.getenv('INIT_CONTAINER_IMAGE', 'ghcr.io/openappsec/openappsec-envoy-filters')
INIT_CONTAINER_TAG = os.getenv('INIT_CONTAINER_TAG', 'latest')
ISTIOD_PORT = os.getenv('ISTIOD_PORT', '15014')
@ -23,6 +24,10 @@ FULL_INIT_CONTAINER_IMAGE = f"{INIT_CONTAINER_IMAGE}:{INIT_CONTAINER_TAG}"
config.load_incluster_config()
def is_istio_agent():
"""Check if the current agent kind is Istio"""
return AGENT_KIND.lower() == "istio"
def configure_logging():
# Read the DEBUG_LEVEL from environment variables, defaulting to WARNING
DEBUG_LEVEL = os.getenv('DEBUG_LEVEL', 'WARNING').upper()
@ -58,16 +63,23 @@ def get_sidecar_container():
token = os.getenv("TOKEN")
custom_fog_enabled = os.getenv("CUSTOM_FOG_ENABLED") == "true" # Check if it's set to "true"
fog_address = os.getenv("FOG_ADDRESS")
registered_server = os.getenv("REGISTERED_SERVER")
appsec_proxy = os.getenv("APPSEC_PROXY")
config_map_ref = os.getenv("CONFIG_MAP_REF")
secret_ref = os.getenv("SECRET_REF")
persistence_enabled = os.getenv("APPSEC_PERSISTENCE_ENABLED", "false").lower() == "true"
# Prepare the volumeMounts list
volume_mounts = [
{"name": "envoy-attachment-shared", "mountPath": "/envoy/attachment/shared/"},
{"name": "advanced-model", "mountPath": "/advanced-model"}
]
# Prepare the volumeMounts list based on AGENT_KIND
if is_istio_agent():
volume_mounts = [
{"name": "envoy-attachment-shared", "mountPath": "/envoy/attachment/shared/"},
{"name": "advanced-model", "mountPath": "/advanced-model"}
]
else:
# For non-istio agents, only mount advanced-model
volume_mounts = [
{"name": "advanced-model", "mountPath": "/advanced-model"}
]
if persistence_enabled:
volume_mounts.extend([
@ -101,7 +113,7 @@ def get_sidecar_container():
# Base environment variables
env = [
{"name": "registered_server", "value": "ISTIO Server"}
{"name": "registered_server", "value": registered_server}
]
# Add optional environment variables if they are set
@ -226,19 +238,33 @@ def get_volume_definition():
persistence_enabled = os.getenv("APPSEC_PERSISTENCE_ENABLED", "false").lower() == "true"
volume_def = [
{
"name": "envoy-attachment-shared",
"emptyDir": {}
},
{
"name": "advanced-model",
"configMap": {
"name": "advanced-model-config",
"optional": True
# Base volumes - only add envoy-attachment-shared for Istio
# wiaam instead of this add the advanced-model volume all the time and the env
if is_istio_agent():
volume_def = [
{
"name": "envoy-attachment-shared",
"emptyDir": {}
},
{
"name": "advanced-model",
"configMap": {
"name": "advanced-model-config",
"optional": True
}
}
}
]
]
else:
# For non-istio agents, only add advanced-model volume
volume_def = [
{
"name": "advanced-model",
"configMap": {
"name": "advanced-model-config",
"optional": True
}
}
]
if persistence_enabled:
volume_def.extend([
@ -559,8 +585,10 @@ def mutate():
app.logger.debug("Current containers in the pod: %s", json.dumps(containers, indent=2))
sidecar_exists = any(container['name'] == 'open-appsec-nano-agent' for container in containers)
init_container_exist = any(init_container['name'] == 'prepare-attachment' for init_container in init_containers)
volume_exist = any(volume['name'] == 'envoy-attachment-shared' for volume in volumes)
# Only check for envoy-attachment-shared volume if agent kind is Istio
volume_exist = any(volume['name'] == 'envoy-attachment-shared' for volume in volumes) if is_istio_agent() else False
app.logger.debug("Does sidecar 'open-appsec-nano-agent' exist? %s", sidecar_exists)
app.logger.debug("Agent kind: %s", AGENT_KIND)
# Determine if we should remove the injected data
REMOVE_WAF = os.getenv('REMOVE_INJECTED_DATA', 'false').lower() == 'true'
@ -574,53 +602,61 @@ def mutate():
CONFIG_PORT_VALUE = os.getenv('CONFIG_PORT')
CONCURRENCY_NUMBER_VALUE = os.getenv('CONCURRENCY_NUMBER')
if REMOVE_WAF:
if DEPLOY_FILTER and SELECTOR_LABEL_NAME and SELECTOR_LABEL_VALUE:
remove_envoy_filter_by_selector(namespace, SELECTOR_LABEL_NAME, SELECTOR_LABEL_VALUE)
app.logger.debug("Removing injected sidecar and associated resources.")
# Remove ld library path env variable
if ISTIO_CONTAINER_NAME:
if CONCURRENCY_NUMBER_VALUE:
remove_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONCURRENCY_NUMBER', patches)
if CONFIG_PORT_VALUE:
remove_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONFIG_PORT', patches)
if CONCURRENCY_CALC_VALUE:
remove_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONCURRENCY_CALC', patches)
if LIBRARY_PATH_VALUE:
remove_env_variable(containers, ISTIO_CONTAINER_NAME, 'LD_LIBRARY_PATH', patches)
# Only handle Istio-specific removal if AGENT_KIND is "istio"
if is_istio_agent():
app.logger.debug("AGENT_KIND is istio, removing Istio-specific components.")
if 'shareProcessNamespace' in obj.get('spec', {}):
patches.append({
"op": "remove",
"path": "/spec/shareProcessNamespace"
})
app.logger.debug("Removed shareProcessNamespace patch")
if DEPLOY_FILTER and SELECTOR_LABEL_NAME and SELECTOR_LABEL_VALUE:
remove_envoy_filter_by_selector(namespace, SELECTOR_LABEL_NAME, SELECTOR_LABEL_VALUE)
# Remove ld library path env variable
if ISTIO_CONTAINER_NAME:
if CONCURRENCY_NUMBER_VALUE:
remove_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONCURRENCY_NUMBER', patches)
if CONFIG_PORT_VALUE:
remove_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONFIG_PORT', patches)
if CONCURRENCY_CALC_VALUE:
remove_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONCURRENCY_CALC', patches)
if LIBRARY_PATH_VALUE:
remove_env_variable(containers, ISTIO_CONTAINER_NAME, 'LD_LIBRARY_PATH', patches)
if 'shareProcessNamespace' in obj.get('spec', {}):
patches.append({
"op": "remove",
"path": "/spec/shareProcessNamespace"
})
app.logger.debug("Removed shareProcessNamespace patch")
else:
app.logger.debug("shareProcessNamespace not found; no patch to remove it")
# Remove the init container if it exists (Istio only)
if init_container_exist:
for idx, init_container in enumerate(init_containers):
if init_container['name'] == 'prepare-attachment':
patches.append({
"op": "remove",
"path": f"/spec/initContainers/{idx}"
})
app.logger.debug(f"Removed init container patch: {patches[-1]}")
break # Stop once we find and remove the target container
else:
app.logger.debug("shareProcessNamespace not found; no patch to remove it")
app.logger.debug(f"AGENT_KIND is {AGENT_KIND}, skipping Istio-specific removal.")
# Remove the init container if it exists
if init_container_exist:
for idx, init_container in enumerate(init_containers):
if init_container['name'] == 'prepare-attachment':
patches.append({
"op": "remove",
"path": f"/spec/initContainers/{idx}"
})
app.logger.debug(f"Removed init container patch: {patches[-1]}")
break # Stop once we find and remove the target container
# Remove the sidecar container if it exists
# Remove the sidecar container if it exists (common for all agent kinds)
if sidecar_exists:
for idx, container in enumerate(containers):
volume_mounts = container.get('volumeMounts', [])
for idx_v, volume_mount in enumerate(volume_mounts):
if volume_mount['name'] == 'envoy-attachment-shared':
patches.append({
"op": "remove",
"path": f"/spec/containers/{idx}/volumeMounts/{idx_v}"
})
app.logger.debug(f"Removed volumeMount: {patches[-1]}")
# Only remove envoy-attachment-shared volume mount for Istio
if is_istio_agent():
for idx_v, volume_mount in enumerate(volume_mounts):
if volume_mount['name'] == 'envoy-attachment-shared':
patches.append({
"op": "remove",
"path": f"/spec/containers/{idx}/volumeMounts/{idx_v}"
})
app.logger.debug(f"Removed volumeMount: {patches[-1]}")
if container['name'] == 'open-appsec-nano-agent':
patches.append({
"op": "remove",
@ -628,16 +664,17 @@ def mutate():
})
app.logger.debug(f"Removed sidecar container patch: {patches[-1]}")
# Remove the volume if it exists
# Remove volumes based on agent kind
if volume_exist:
for idx, volume in enumerate(volumes):
if volume['name'] == 'envoy-attachment-shared':
# For Istio, remove envoy-attachment-shared volume
if is_istio_agent() and volume['name'] == 'envoy-attachment-shared':
patches.append({
"op": "remove",
"path": f"/spec/volumes/{idx}"
})
app.logger.debug(f"Removed volume patch: {patches[-1]}")
break # Stop once we find and remove the target container
break # Stop once we find and remove the target volume
else:
app.logger.debug("Before if: Sidecar 'open-appsec-nano-agent' does not exist. Preparing to add it.")
@ -645,33 +682,34 @@ def mutate():
# Define the sidecar container
sidecar = get_sidecar_container()
# Define the init container()
init_container = get_init_container()
# Define the volume mount for istio-proxy
volume_mount = get_volume_mount()
# Define the volume
volume_def = get_volume_definition()
if ISTIO_CONTAINER_NAME:
add_env_if_not_exist(containers, ISTIO_CONTAINER_NAME, patches)
add_env_variable_value_from(containers, ISTIO_CONTAINER_NAME, 'OPENAPPSEC_UID', None, patches, value_from={"fieldRef": {"fieldPath": "metadata.uid"}})
if LIBRARY_PATH_VALUE:
add_env_variable(containers, ISTIO_CONTAINER_NAME, 'LD_LIBRARY_PATH', LIBRARY_PATH_VALUE, patches)
if CONCURRENCY_CALC_VALUE:
add_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONCURRENCY_CALC', CONCURRENCY_CALC_VALUE, patches)
if CONFIG_PORT_VALUE:
add_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONFIG_PORT', CONFIG_PORT_VALUE, patches)
if CONCURRENCY_NUMBER_VALUE:
add_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONCURRENCY_NUMBER', CONCURRENCY_NUMBER_VALUE, patches)
else:
app.logger.debug("ISTIO_CONTAINER_NAME skipping environment variable addition")
# Only handle Istio-specific components if AGENT_KIND is "istio"
if is_istio_agent():
app.logger.debug("AGENT_KIND is istio, adding Istio-specific components.")
# Add the sidecar container
if not sidecar_exists:
# Define the init container()
init_container = get_init_container()
# Add shareProcessNamespace if not already set
# Define the volume mount for istio-proxy
volume_mount = get_volume_mount()
if ISTIO_CONTAINER_NAME:
add_env_if_not_exist(containers, ISTIO_CONTAINER_NAME, patches)
add_env_variable_value_from(containers, ISTIO_CONTAINER_NAME, 'OPENAPPSEC_UID', None, patches, value_from={"fieldRef": {"fieldPath": "metadata.uid"}})
if LIBRARY_PATH_VALUE:
add_env_variable(containers, ISTIO_CONTAINER_NAME, 'LD_LIBRARY_PATH', LIBRARY_PATH_VALUE, patches)
if CONCURRENCY_CALC_VALUE:
add_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONCURRENCY_CALC', CONCURRENCY_CALC_VALUE, patches)
if CONFIG_PORT_VALUE:
add_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONFIG_PORT', CONFIG_PORT_VALUE, patches)
if CONCURRENCY_NUMBER_VALUE:
add_env_variable(containers, ISTIO_CONTAINER_NAME, 'CONCURRENCY_NUMBER', CONCURRENCY_NUMBER_VALUE, patches)
else:
app.logger.debug("ISTIO_CONTAINER_NAME skipping environment variable addition")
# Add shareProcessNamespace for Istio
patches.append({
"op": "add",
"path": "/spec/shareProcessNamespace",
@ -679,13 +717,6 @@ def mutate():
})
app.logger.debug("Added shareProcessNamespace patch")
patches.append({
"op": "add",
"path": "/spec/containers/-",
"value": sidecar
})
app.logger.debug("Added sidecar container patch: %s", patches[-1])
# Add the volume mount to istio-proxy container (assumes istio-proxy is first container)
patches.append({
"op": "add",
@ -694,6 +725,37 @@ def mutate():
})
app.logger.debug("Added volume mount patch to istio-proxy: %s", patches[-1])
# Add init container for Istio
if not init_container_exist:
# Add the initContainer to the pod spec in the deployment
if 'initContainers' in obj['spec']:
obj['spec']['initContainers'].append(init_container)
else:
obj['spec']['initContainers'] = [init_container]
patches.append({
"op": "add",
"path": "/spec/initContainers",
"value": obj['spec']['initContainers']
})
# Deploy EnvoyFilter for Istio
if DEPLOY_FILTER and SELECTOR_LABEL_NAME and SELECTOR_LABEL_VALUE:
RELEASE_NAME = os.getenv('RELEASE_NAME', 'openappsec-waf-injected')
envoy_filter_name = RELEASE_NAME + "-waf-filter"
create_or_update_envoy_filter(envoy_filter_name, namespace, SELECTOR_LABEL_NAME, SELECTOR_LABEL_VALUE)
else:
app.logger.debug(f"AGENT_KIND is {AGENT_KIND}, skipping Istio-specific components.")
# Add the sidecar container (common for all agent kinds)
if not sidecar_exists:
patches.append({
"op": "add",
"path": "/spec/containers/-",
"value": sidecar
})
app.logger.debug("Added sidecar container patch: %s", patches[-1])
# Add the new volume definition
for volume in volume_def:
patches.append({
@ -702,11 +764,6 @@ def mutate():
"value": volume
})
app.logger.debug("Added volume definition patch: %s", patches[-1])
if DEPLOY_FILTER and SELECTOR_LABEL_NAME and SELECTOR_LABEL_VALUE:
RELEASE_NAME = os.getenv('RELEASE_NAME', 'openappsec-waf-injected')
envoy_filter_name = RELEASE_NAME + "-waf-filter"
create_or_update_envoy_filter(envoy_filter_name, namespace, SELECTOR_LABEL_NAME, SELECTOR_LABEL_VALUE)
else:
app.logger.debug("Before else: Sidecar 'open-appsec-nano-agent' already exists. Checking for image updates.")
@ -725,35 +782,24 @@ def mutate():
app.logger.debug(f"Updated sidecar image patch: {patches[-1]}")
break # Sidecar found and handled
if not init_container_exist:
# Add the initContainer to the pod spec in the deployment
if 'initContainers' in obj['spec']:
obj['spec']['initContainers'].append(init_container)
else:
obj['spec']['initContainers'] = [init_container]
patches.append({
"op": "add",
"path": "/spec/initContainers",
"value": obj['spec']['initContainers']
})
else:
# Handle init container updates for Istio only
if is_istio_agent() and init_container_exist:
app.logger.debug("Before else: init-container 'prepare-attachment' already exists. Checking for image updates.")
# Optionally, update the sidecar image and tag if necessary
for idx, container in enumerate(containers):
# Optionally, update the init container image and tag if necessary
for idx, container in enumerate(init_containers):
if container['name'] == 'prepare-attachment':
current_image = container.get('image', '')
app.logger.debug("Current init container image: %s", current_image)
app.logger.debug("Desired init container image: %s", FULL_AGENT_IMAGE)
if current_image != FULL_AGENT_IMAGE:
app.logger.debug("Desired init container image: %s", FULL_INIT_CONTAINER_IMAGE)
if current_image != FULL_INIT_CONTAINER_IMAGE:
patches.append({
"op": "replace",
"path": f"/spec/containers/{idx}/image",
"value": FULL_AGENT_IMAGE
"path": f"/spec/initContainers/{idx}/image",
"value": FULL_INIT_CONTAINER_IMAGE
})
app.logger.debug(f"Updated sidecar image patch: {patches[-1]}")
break # Sidecar found and handled
app.logger.debug(f"Updated init container image patch: {patches[-1]}")
break # Init container found and handled
app.logger.info("Total patches: %s", json.dumps(patches, indent=2))