diff --git a/docker/openappsec-waf-webhook/webhook_server.py b/docker/openappsec-waf-webhook/webhook_server.py index f1b5449..917de5b 100755 --- a/docker/openappsec-waf-webhook/webhook_server.py +++ b/docker/openappsec-waf-webhook/webhook_server.py @@ -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))