From 646ebc51b8ae36f5e43c1dd84fae09da4c391cce Mon Sep 17 00:00:00 2001 From: cwadhwani-splunk Date: Wed, 15 Oct 2025 14:04:14 +0000 Subject: [PATCH 1/4] fix: Handled manage_secrets from docker entrypoint --- Dockerfile | 2 + docker_compose/.env | 3 + docker_compose/docker-compose.yaml | 4 + docker_compose/manage_secrets.py | 349 ------------------ docs/dockercompose/7-snmpv3-secrets.md | 126 ++++--- entrypoint.sh | 6 +- integration_tests/.env | 3 + integration_tests/automatic_setup_compose.sh | 21 +- .../sample_v3_values/secrets.json | 18 + .../sample_v3_values/snmpv3/test/authKey | 0 .../sample_v3_values/snmpv3/test/authKeyType | 0 .../sample_v3_values/snmpv3/test/authProtocol | 0 .../sample_v3_values/snmpv3/test/privKey | 0 .../sample_v3_values/snmpv3/test/privKeyType | 0 .../sample_v3_values/snmpv3/test/privProtocol | 0 .../sample_v3_values/snmpv3/test/userName | 0 integration_tests/splunk_test_utils.py | 21 +- manage_secrets.py | 58 +++ 18 files changed, 171 insertions(+), 440 deletions(-) delete mode 100644 docker_compose/manage_secrets.py create mode 100644 integration_tests/sample_v3_values/secrets.json delete mode 100644 integration_tests/sample_v3_values/snmpv3/test/authKey delete mode 100644 integration_tests/sample_v3_values/snmpv3/test/authKeyType delete mode 100644 integration_tests/sample_v3_values/snmpv3/test/authProtocol delete mode 100644 integration_tests/sample_v3_values/snmpv3/test/privKey delete mode 100644 integration_tests/sample_v3_values/snmpv3/test/privKeyType delete mode 100644 integration_tests/sample_v3_values/snmpv3/test/privProtocol delete mode 100644 integration_tests/sample_v3_values/snmpv3/test/userName create mode 100644 manage_secrets.py diff --git a/Dockerfile b/Dockerfile index 9ca6c431e..eb87ad4ac 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,8 @@ RUN poetry config virtualenvs.in-project true ;\ FROM base AS final RUN mkdir /.pysnmp && chown 10001:10001 /.pysnmp +COPY manage_secrets.py /app/secrets/ +RUN chown 10001:10001 /app/secrets/ RUN chown 10001:10001 /tmp USER 10001:10001 COPY --from=builder /app/.venv /app/.venv diff --git a/docker_compose/.env b/docker_compose/.env index 60258c279..29402b1a4 100644 --- a/docker_compose/.env +++ b/docker_compose/.env @@ -103,3 +103,6 @@ SCHEDULER_LOG_LEVEL=INFO FLOWER_PORT=80 #Secrets +SECRET_FOLDER_PATH= +ENABLE_TRAPS_SECRETS=false +ENABLE_WORKER_POLLER_SECRETS=false \ No newline at end of file diff --git a/docker_compose/docker-compose.yaml b/docker_compose/docker-compose.yaml index 8a180acff..85f90af0c 100644 --- a/docker_compose/docker-compose.yaml +++ b/docker_compose/docker-compose.yaml @@ -147,6 +147,7 @@ services: LOG_LEVEL: ${TRAP_LOG_LEVEL:-INFO} SNMP_V3_SECURITY_ENGINE_ID: ${SNMP_V3_SECURITY_ENGINE_ID:-80003a8c04} DISABLE_MONGO_DEBUG_LOGGING: ${TRAP_DISABLE_MONGO_DEBUG_LOGGING:-true} + ENABLE_TRAPS_SECRETS: ${ENABLE_TRAPS_SECRETS:-false} image: ${SC4SNMP_IMAGE}:${SC4SNMP_TAG:-latest} ports: - mode: host @@ -157,6 +158,7 @@ services: - ${TRAPS_CONFIG_FILE_ABSOLUTE_PATH}:/app/config/config.yaml:ro - traps-pysnmp-cache-volume:/.pysnmp/:rw - traps-tmp:/tmp/:rw + - ${SECRET_FOLDER_PATH}:/app/secrets/tmp:ro worker-poller: <<: [*dns_and_networks, *dependency_and_restart_policy] image: ${SC4SNMP_IMAGE}:${SC4SNMP_TAG:-latest} @@ -178,10 +180,12 @@ services: *pysnmp_debug, *ipv6] WORKER_CONCURRENCY: ${WORKER_POLLER_CONCURRENCY:-2} PREFETCH_COUNT: ${PREFETCH_POLLER_COUNT:-1} + ENABLE_WORKER_POLLER_SECRETS: ${ENABLE_WORKER_POLLER_SECRETS:-false} volumes: - ${SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH}:/app/config/config.yaml:ro - worker-poller-pysnmp-cache-volume:/.pysnmp/:rw - worker-poller-tmp:/tmp/:rw + - ${SECRET_FOLDER_PATH}:/app/secrets/tmp:ro worker-sender: <<: [*dns_and_networks, *dependency_and_restart_policy] image: ${SC4SNMP_IMAGE}:${SC4SNMP_TAG:-latest} diff --git a/docker_compose/manage_secrets.py b/docker_compose/manage_secrets.py deleted file mode 100644 index 079d8890f..000000000 --- a/docker_compose/manage_secrets.py +++ /dev/null @@ -1,349 +0,0 @@ -import argparse -import os -from typing import Union - -import ruamel.yaml - -SERVICE_SECRETS = ["worker-poller", "traps"] -DOCKER_COMPOSE = "docker-compose.yaml" - - -def human_bool(flag: Union[str, bool], default: bool = False) -> bool: - if flag is None: - return False - if isinstance(flag, bool): - return flag - if flag.lower() in [ - "true", - "1", - "t", - "y", - "yes", - ]: - return True - elif flag.lower() in [ - "false", - "0", - "f", - "n", - "no", - ]: - return False - else: - return default - - -def remove_variables_from_env(file_path: str, variables_to_remove: list): - """ - Function to remove variables from .env file - @param file_path: path to .env file - @param variables_to_remove: names of variables to remove - """ - try: - with open(file_path) as env_file: - lines = env_file.readlines() - - with open(file_path, "w") as env_file: - for line in lines: - key = line.split("=")[0].strip() - if key not in variables_to_remove: - env_file.write(line) - - print("Variables removed successfully from .env file.") - except Exception as e: - print(f"Error: {e}") - - -def create_secrets( - variables: dict, - path_to_compose_files: str, - secret_name: str, - make_change_in_worker_poller: bool, - make_change_in_traps: bool, -): - """ - Function to create secrets in .env and docker-compose.yaml files - @param variables: dictionary mapping variable names to their values - @param path_to_compose_files: absolute path to directory with .env and docker-compose.yaml files - @param secret_name: name of the secret - @param make_change_in_worker_poller: flag indicating whether to add secrets to worker poller service - @param make_change_in_traps: flag indicating whether to add secrets to traps service - """ - for k, v in variables.items(): - if k != "contextEngineId" and not v: - raise ValueError(f"Value {k} is not set") - - # list for storing secrets configuration which should be added to docker-compose-secrets.yaml - new_secrets, new_secrets_in_workers = store_secrets(secret_name, variables) - - try: - # Load docker-compose-secrets.yaml to a dictionary and update "secrets" section. If the same secret - # has been already configured, stop processing further. - yaml = ruamel.yaml.YAML() - with open(os.path.join(path_to_compose_files, DOCKER_COMPOSE)) as file: - yaml_file = yaml.load(file) - if yaml_file["secrets"] is None or "secrets" not in yaml_file: - yaml_file["secrets"] = {} - for new_secret in new_secrets: - if new_secret["secret_name"] in yaml_file["secrets"]: - print(f"Secret {secret_name} already configured. New secret not added.") - return - yaml_file["secrets"][new_secret["secret_name"]] = new_secret[ - "secret_config" - ] - secrets_ready = True - - if make_change_in_worker_poller: - yaml_file, worker_poller_ready = load_compose_worker_poller( - new_secrets_in_workers, yaml_file - ) - else: - worker_poller_ready = True - - if make_change_in_traps: - yaml_file, traps_ready = load_compose_traps( - new_secrets_in_workers, yaml_file - ) - else: - traps_ready = True - - save_to_compose_files( - path_to_compose_files, - secret_name, - yaml_file, - secrets_ready, - traps_ready, - variables, - worker_poller_ready, - ) - except Exception as e: - print(f"Problem with adding secrets. Error: {e}") - - -def save_to_compose_files( - path_to_compose_files, - secret_name, - yaml_file, - secrets_ready, - traps_ready, - variables, - worker_poller_ready, -): - if secrets_ready and worker_poller_ready and traps_ready: - # If all three files were loaded into dictionary and updated successfully, - # save the latest configuration to files. - with open(os.path.join(path_to_compose_files, ".env"), "a") as file: - for k, v in variables.items(): - if v: - file.write(f"\n{secret_name}_{k}={v}") - - yaml = ruamel.yaml.YAML() - with open(os.path.join(path_to_compose_files, DOCKER_COMPOSE), "w") as file: - yaml.dump(yaml_file, file) - - -def load_compose_traps(new_secrets_in_workers, yaml_file): - # If the secret should be added to traps, load docker-compose-traps.yaml to a dictionary and - # update "secrets" section. - try: - if "secrets" not in yaml_file["services"]["traps"]: - yaml_file["services"]["traps"]["secrets"] = [] - yaml_file["services"]["traps"]["secrets"].extend(new_secrets_in_workers) - traps_ready = True - except Exception as e: - print(f"Problem with editing traps. Secret not added. Error {e}") - yaml_file = {} - traps_ready = False - return yaml_file, traps_ready - - -def load_compose_worker_poller(new_secrets_in_workers, yaml_file): - # If the secret should be added to worker poller, load docker-compose-worker-poller.yaml to a dictionary and - # update "secrets" section. - try: - if "secrets" not in yaml_file["services"]["worker-poller"]: - yaml_file["services"]["worker-poller"]["secrets"] = [] - yaml_file["services"]["worker-poller"]["secrets"].extend(new_secrets_in_workers) - worker_poller_ready = True - except Exception: - print("Problem with editing worker-poller. Secret not added.") - yaml_file = {} - worker_poller_ready = False - return yaml_file, worker_poller_ready - - -def store_secrets(secret_name, variables): - new_secrets = [] - # list for storing secrets configuration which should be added to docker-compose-worker-poller.yaml and - # docker-compose-traps.yaml services - new_secrets_in_workers = [] - for k, v in variables.items(): - if v: - new_secrets.append( - { - "secret_name": f"{secret_name}_{k}", - "secret_config": {"environment": f"{secret_name}_{k}"}, - } - ) - new_secrets_in_workers.append( - { - "source": f"{secret_name}_{k}", - "target": f"/app/secrets/snmpv3/{secret_name}/{k}", - } - ) - return new_secrets, new_secrets_in_workers - - -def delete_secrets( - variables: dict, - path_to_compose_files: str, - secret_name: str, - make_change_in_worker_poller: bool, - make_change_in_traps: bool, -): - """ - Function to delete secrets from .env and docker-compose.yaml files - @param variables: dictionary mapping variable names to their values - @param path_to_compose_files: absolute path to directory with .env and docker-compose.yaml files - @param secret_name: name of the secret - @param make_change_in_worker_poller: flag indicating whether to delete secrets from worker poller service - @param make_change_in_traps: flag indicating whether to delete secrets from traps service - """ - secrets = [] - for key in variables.keys(): - secrets.append(f"{secret_name}_{key}") - - yaml = ruamel.yaml.YAML() - try: - with open(os.path.join(path_to_compose_files, DOCKER_COMPOSE)) as file: - yaml_file = yaml.load(file) - - yaml_file = load_compose_secrets(yaml_file, secrets) - # Save the updated docker-compose-secrets.yaml configuration - - if make_change_in_worker_poller: - # filter out secrets destined for deletion - - yaml_file["services"]["worker-poller"]["secrets"] = list( - filter( - lambda el: el["source"] not in secrets, - yaml_file["services"]["worker-poller"]["secrets"], - ) - ) - - if make_change_in_traps: - # Load docker-compose-traps.yaml to dictionary and filter out secrets destined for deletion - yaml_file["services"]["traps"]["secrets"] = list( - filter( - lambda el: el["source"] not in secrets, - yaml_file["services"]["traps"]["secrets"], - ) - ) - - except Exception as e: - print(f"Problem with editing secrets section. Secret not added. Error: {e}") - - with open(os.path.join(path_to_compose_files, DOCKER_COMPOSE), "w") as file: - yaml.dump(yaml_file, file) - - # Delete secrets from .env - delete_secrets_from_env(path_to_compose_files, secrets) - - -def delete_secrets_from_env(path_to_compose_files, secrets): - try: - # Read lines from .env - with open(os.path.join(path_to_compose_files, ".env")) as env_file: - lines = env_file.readlines() - - with open(os.path.join(path_to_compose_files, ".env"), "w") as env_file: - lines_to_write = [] - # If the environmental variable is NOT one of the secrets destined for deletion, add them to lines_to_write - for line in lines: - key = line.split("=")[0].strip() - if key not in secrets: - lines_to_write.append(line.strip()) - - # Save each line to .env. The last line should be saved without a new line symbol - for i, line in enumerate(lines_to_write): - if i < len(lines_to_write) - 1: - env_file.write(f"{line}\n") - else: - env_file.write(line) - except Exception as e: - print(f"Error: {e}") - - -def load_compose_secrets(yaml_file, secrets): - # Load docker-compose-secrets.yaml file to a dictionary and delete desired secrets - for secret in secrets: - if secret in yaml_file["secrets"]: - del yaml_file["secrets"][secret] - return yaml_file - - -def main(): - parser = argparse.ArgumentParser(description="Manage secrets in docker compose") - parser.add_argument("--delete", default="false", help="If true, delete the secret") - parser.add_argument("--secret_name", help="Secret name") - parser.add_argument("--path_to_compose", help="Path to dockerfiles") - parser.add_argument( - "--worker_poller", default="true", help="Add secret to worker poller" - ) - parser.add_argument("--traps", default="true", help="Add secret to traps") - parser.add_argument("--userName", default="", help="SNMPV3 username") - parser.add_argument("--privProtocol", default="", help="SNMPV3 privProtocol") - parser.add_argument("--privKey", default="", help="SNMPV3 privKey") - parser.add_argument("--authProtocol", default="", help="SNMPV3 authProtocol") - parser.add_argument("--authKey", default="", help="SNMPV3 authKey") - parser.add_argument("--contextEngineId", default="", help="SNMPV3 contextEngineId") - - args = parser.parse_args() - - # Assign inputs from command line to variables - delete_secret = human_bool(args.delete) - secret_name = args.secret_name - path_to_compose_files = args.path_to_compose - make_change_in_worker_poller = human_bool(args.worker_poller) - make_change_in_traps = human_bool(args.traps) - - # variables dictionary maps variables names stored inside a secret to their values - variables = { - "userName": args.userName, - "privProtocol": args.privProtocol, - "privKey": args.privKey, - "authProtocol": args.authProtocol, - "authKey": args.authKey, - "contextEngineId": args.contextEngineId, - } - - if not os.path.exists(path_to_compose_files): - print("Path to compose files doesn't exist") - return - if not secret_name: - print("Secret name not specified") - return - - if not delete_secret: - try: - create_secrets( - variables, - path_to_compose_files, - secret_name, - make_change_in_worker_poller, - make_change_in_traps, - ) - except ValueError as e: - print(e) - else: - delete_secrets( - variables, - path_to_compose_files, - secret_name, - make_change_in_worker_poller, - make_change_in_traps, - ) - - -if __name__ == "__main__": - main() diff --git a/docs/dockercompose/7-snmpv3-secrets.md b/docs/dockercompose/7-snmpv3-secrets.md index e29e8ed65..8992e4f90 100644 --- a/docs/dockercompose/7-snmpv3-secrets.md +++ b/docs/dockercompose/7-snmpv3-secrets.md @@ -1,85 +1,89 @@ # SNMPv3 secrets -Creating a secret requires updating configuration of several docker compose files. To simplify this process, inside the -`docker_compose` package there is a `manage_secrets.py` file which will automatically manage secrets. +## Migration steps +Managing SNMPv3 secrets previously required updating docker compose files using the manage_secrets.py script. +From SC4SNMP 1.15.0, this process has been simplified and can manage all SNMPv3 secrets using a single secrets.json file. + +#### 1. For setups not yet migrated to latest version +First, delete all existing secrets from docker-compose.yaml using manage_secrets.py with the following flags: + +| Flag | Description | +|---------------------|------------------------------------------------------| +| `--secret_name` | Secret name | +| `--path_to_compose` | Absolute path to directory with docker compose files | +| `--delete` | Set this flag to true to delete the secret | + +This will delete the secret with a given name from all docker compose files. If this secret hasn't been deleted from `.env` +file, it will be removed from there. + +#### 2. For setups already migrated to latest version +Manually delete the secrets from the docker-compose.yaml file under the worker-poller and worker-trap services. +Remove the corresponding entries from the .env file. + + +After deleting the secrets, follow the below mention steps to configure secrets. ## Prerequisites -Running script requires installation of `ruamel.yaml` package for python. It can be done with command: +A folder must be created to store the secrets file. +Inside this folder, create a secrets.json file that contains all SNMPv3 secrets. + +#### Example of secrets.json +```json +{ + "secret_name": { + "username": "user1", + "privprotocol": "AES", + "privkey": "privkey1", + "authprotocol": "SHA", + "authkey": "authkey1", + "contextengineid": "engineid1" + }, +} ``` -pip3 install ruamel.yaml + +> **_NOTE:_** The name of json file should be secrets.json and secrets should have non-empty fields (except contextengineid). + +## Configuration +In the .env file, set the path to the local folder containing the secrets.json: +``` +SECRET_FOLDER_PATH=/absolute/path/to/secrets/folder ``` +Secrets usage for worker-poller and worker-trap can be controlled by flags in .env: +``` +ENABLE_TRAPS_SECRETS=true +ENABLE_WORKER_POLLER_SECRETS=true +``` + + ## Creating a new secret -To create a new secret, `manage_secrets.py` must be run with the following flags: - -| Flag | Description | -|---------------------|--------------------------------------------------------------------------------| -| `--secret_name` | New secret name | -| `--path_to_compose` | Absolute path to directory with docker compose files | -| `--worker_poller` | \[OPTIONAL\] Add new secrets to worker poller. Default value is set to 'true'. | -| `--traps` | \[OPTIONAL\] Add new secrets to traps server. Default value is set to 'true'. | -| `--userName` | SNMPv3 userName | -| `--privProtocol` | SNMPv3 privProtocol | -| `--privKey` | SNMPv3 privKey | -| `--authProtocol` | SNMPv3 authProtocol | -| `--authKey` | SNMPv3 authKey | -| `--contextEngineId` | \[OPTIONAL\] SNMPv3 engine id | - - -This script, apart from updating configuration files, creates environmental variables with values of the secret at the -end of the `.env` file in the `docker_compose` directory. To apply those secrets run the -`sudo docker compose up -d` command inside the `docker_compose` directory. After execution of the command, plain text secrets -from the `.env` file can be deleted. -> **_NOTE:_** In case of any changes in `.env`, the secrets must be recreated by [deleting](#deleting-a-secret) any -> previously existing secrets and creating them once again. Changes in `.env` include creating new secrets. - -### Example of creating a secret -```shell -python3 --path_to_compose \ ---secret_name my_secret \ ---userName r-wuser \ ---privProtocol AES \ ---privKey admin1234 \ ---authProtocol SHA \ ---authKey admin1234 \ ---contextEngineId 090807060504037 -``` +To create a new secret, +create secrets.json file inside folder (at SECRET_FOLDER_PATH), add entry for the new secrets with all the details. Inside `docker_compose` directory run: ```shell -sudo docker compose up -d + ``` -Now, the following lines from the `.env` can be deleted: +## Updating existing secret + +To update any secret, +update the required fields (e.g., keys, protocols, username) for any existing secret inside secrets.json file. -```.env -my_secret_userName=r-wuser -my_secret_privProtocol=AES -my_secret_privKey=admin1234 -my_secret_authProtocol=SHA -my_secret_authKey=admin1234 -my_secret_contextEngineId=090807060504037 +Inside `docker_compose` directory run: +```shell + ``` ## Deleting a secret -To delete a secret, `manage_secrets.py` must be run with the following flags: +To delete a secret, +delete its entry from the secrets.json file. -| Flag | Description | -|---------------------|------------------------------------------------------| -| `--secret_name` | Secret name | -| `--path_to_compose` | Absolute path to directory with docker compose files | -| `--delete` | Set this flag to true to delete the secret | - -This will delete the secret with a given name from all docker compose files. If this secret hasn't been deleted from `.env` -file, it will be removed from there. - -### Example of deleting a secret +Inside `docker_compose` directory run: ```shell -python3 --path_to_compose \ ---secret_name my_secret \ ---delete true + ``` \ No newline at end of file diff --git a/entrypoint.sh b/entrypoint.sh index 24d896353..4ec421517 100755 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -3,8 +3,12 @@ set -e . /app/.venv/bin/activate LOG_LEVEL=${LOG_LEVEL:=INFO} WORKER_CONCURRENCY=${WORKER_CONCURRENCY:=4} +ENABLE_TRAPS_SECRETS=${ENABLE_TRAPS_SECRETS:=false} +ENABLE_WORKER_POLLER_SECRETS=${ENABLE_WORKER_POLLER_SECRETS:=false} wait-for-dep "${CELERY_BROKER_URL}" "${REDIS_URL}" "${MONGO_URI}" "${MIB_INDEX}" - +if [ "$ENABLE_TRAPS_SECRETS" = "true" ] || [ "$ENABLE_WORKER_POLLER_SECRETS" = "true" ]; then + python /app/secrets/manage_secrets.py +fi case $1 in inventory) diff --git a/integration_tests/.env b/integration_tests/.env index 45da870f4..cd23e2386 100644 --- a/integration_tests/.env +++ b/integration_tests/.env @@ -89,3 +89,6 @@ TRAP_LOG_LEVEL=INFO SCHEDULER_LOG_LEVEL=INFO #Secrets +SECRET_FOLDER_PATH= +ENABLE_TRAPS_SECRETS=false +ENABLE_WORKER_POLLER_SECRETS=false \ No newline at end of file diff --git a/integration_tests/automatic_setup_compose.sh b/integration_tests/automatic_setup_compose.sh index 1579fa751..5ccbb133c 100755 --- a/integration_tests/automatic_setup_compose.sh +++ b/integration_tests/automatic_setup_compose.sh @@ -105,6 +105,7 @@ SCHEDULER_CONFIG_FILE="scheduler-config.yaml" TRAPS_CONFIG_FILE="traps-config.yaml" INVENTORY_FILE="inventory-tests.csv" COREFILE="Corefile" +SECRET_FOLDER="sample_v3_values" # Get the absolute paths of the files SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH=$(realpath "$SCHEDULER_CONFIG_FILE") @@ -113,6 +114,7 @@ INVENTORY_FILE_ABSOLUTE_PATH=$(realpath "$INVENTORY_FILE") COREFILE_ABS_PATH=$(realpath "$COREFILE") SPLUNK_HEC_HOST=$(hostname -I | cut -d " " -f1) SPLUNK_HEC_TOKEN=$(cat hec_token) +SECRET_FOLDER_PATH=$(realpath "$SECRET_FOLDER") # Temporary file to store the updated .env content TEMP_ENV_FILE=".env.tmp" @@ -124,6 +126,7 @@ awk -v scheduler_path="$SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH" \ -v corefile_path="$COREFILE_ABS_PATH" \ -v splunk_hec_host="$SPLUNK_HEC_HOST" \ -v splunk_hec_token="$SPLUNK_HEC_TOKEN" \ + -v secret_folder_path="$SECRET_FOLDER_PATH" \ ' BEGIN { updated["SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH"] = 0; @@ -132,6 +135,7 @@ awk -v scheduler_path="$SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH" \ updated["COREFILE_ABS_PATH"] = 0; updated["SPLUNK_HEC_HOST"] = 0; updated["SPLUNK_HEC_TOKEN"] = 0; + updated["SECRET_FOLDER_PATH"] = 0; } { if ($1 == "SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH=") { @@ -152,6 +156,9 @@ awk -v scheduler_path="$SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH" \ } else if ($1 == "SPLUNK_HEC_TOKEN=") { print "SPLUNK_HEC_TOKEN=" splunk_hec_token; updated["SPLUNK_HEC_TOKEN"] = 1; + } else if ($1 == "SECRET_FOLDER_PATH=") { + print "SECRET_FOLDER_PATH=" secret_folder_path; + updated["SECRET_FOLDER_PATH"] = 1; } else { print $0; } @@ -175,23 +182,15 @@ awk -v scheduler_path="$SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH" \ if (updated["SPLUNK_HEC_TOKEN"] == 0) { print "SPLUNK_HEC_TOKEN=" splunk_hec_token; } + if (updated["SECRET_FOLDER_PATH"] == 0) { + print "SECRET_FOLDER_PATH=" secret_folder_path; + } } ' .env > "$TEMP_ENV_FILE" # Replace the old .env file with the updated one mv "$TEMP_ENV_FILE" .env -# Create snmpv3 secret -python3 -m pip install ruamel.yaml -python3 $(realpath "manage_secrets.py") --path_to_compose $(pwd) \ ---secret_name sv3poller \ ---userName r-wuser \ ---privProtocol AES \ ---privKey admin1234 \ ---authProtocol SHA \ ---authKey admin1234 \ ---contextEngineId 8000000903000A397056B8AC \ ---traps false sed -i "s/###LOAD_BALANCER_ID###/$(hostname -I | cut -d " " -f1)/" inventory-tests.csv echo $(green "Running SNMP simulators in Docker") diff --git a/integration_tests/sample_v3_values/secrets.json b/integration_tests/sample_v3_values/secrets.json new file mode 100644 index 000000000..20f6d9c2f --- /dev/null +++ b/integration_tests/sample_v3_values/secrets.json @@ -0,0 +1,18 @@ +{ + "sv3poller": { + "username": "r-wuser", + "privprotocol": "AES", + "privkey": "admin1234", + "authprotocol": "SHA", + "authkey": "admin1234", + "contextengineid": "8000000903000A397056B8AC" + }, + "secretv4": { + "username": "snmp-poller", + "privprotocol": "AES", + "privkey": "PASSWORD1", + "authprotocol": "SHA", + "authkey": "PASSWORD1", + "contextengineid": "8000000903000A397056B8AC" + } +} \ No newline at end of file diff --git a/integration_tests/sample_v3_values/snmpv3/test/authKey b/integration_tests/sample_v3_values/snmpv3/test/authKey deleted file mode 100644 index e69de29bb..000000000 diff --git a/integration_tests/sample_v3_values/snmpv3/test/authKeyType b/integration_tests/sample_v3_values/snmpv3/test/authKeyType deleted file mode 100644 index e69de29bb..000000000 diff --git a/integration_tests/sample_v3_values/snmpv3/test/authProtocol b/integration_tests/sample_v3_values/snmpv3/test/authProtocol deleted file mode 100644 index e69de29bb..000000000 diff --git a/integration_tests/sample_v3_values/snmpv3/test/privKey b/integration_tests/sample_v3_values/snmpv3/test/privKey deleted file mode 100644 index e69de29bb..000000000 diff --git a/integration_tests/sample_v3_values/snmpv3/test/privKeyType b/integration_tests/sample_v3_values/snmpv3/test/privKeyType deleted file mode 100644 index e69de29bb..000000000 diff --git a/integration_tests/sample_v3_values/snmpv3/test/privProtocol b/integration_tests/sample_v3_values/snmpv3/test/privProtocol deleted file mode 100644 index e69de29bb..000000000 diff --git a/integration_tests/sample_v3_values/snmpv3/test/userName b/integration_tests/sample_v3_values/snmpv3/test/userName deleted file mode 100644 index e69de29bb..000000000 diff --git a/integration_tests/splunk_test_utils.py b/integration_tests/splunk_test_utils.py index cffe7b681..9062f5ef4 100644 --- a/integration_tests/splunk_test_utils.py +++ b/integration_tests/splunk_test_utils.py @@ -165,24 +165,9 @@ def upgrade_env_compose(variable, new_value, env_path=".env"): f.writelines(lines) -def create_v3_secrets_compose( - secret_name="secretv4", - user_name="snmp-poller", - auth_key="PASSWORD1", - priv_key="PASSWORD1", - auth_protocol="SHA", - priv_protocol="AES", -): - os.system( - f'python3 $(realpath "manage_secrets.py") --path_to_compose $(pwd) \ - --secret_name {secret_name} \ - --userName {user_name} \ - --privProtocol {priv_protocol} \ - --privKey {priv_key} \ - --authProtocol {auth_protocol} \ - --authKey {auth_key} \ - --contextEngineId 8000000903000A397056B8AC' - ) +def create_v3_secrets_compose(): + upgrade_env_compose("ENABLE_TRAPS_SECRETS", "true") + upgrade_env_compose("SECRET_FOLDER_PATH", "../integration_tests/sample_v3_values") def wait_for_containers_initialization(): diff --git a/manage_secrets.py b/manage_secrets.py new file mode 100644 index 000000000..ecffda569 --- /dev/null +++ b/manage_secrets.py @@ -0,0 +1,58 @@ +import json +import os +import shutil +import sys + +BASE_DIR = "/app/secrets/snmpv3" +SECRETS_JSON = "/app/secrets/tmp/secrets.json" + + +def create_secret_files(secrets): + file_map = { + "username": "userName", + "authkey": "authKey", + "authprotocol": "authProtocol", + "privkey": "privKey", + "privprotocol": "privProtocol", + "contextengineid": "contextEngineId", + } + secret_names = [] + for secretname, details in secrets.items(): + secret_dir = os.path.join(BASE_DIR, secretname) + skip = False + for key, value in details.items(): + if key.lower() != "contextengineid" and not value: + print(f"Skipping {secretname} as value {key} is not set.") + skip = True + if not skip: + os.makedirs(secret_dir, exist_ok=True) + for key, value in details.items(): + file_name = file_map.get(key.lower()) + if file_name: + file_path = os.path.join(secret_dir, file_name) + with open(file_path, "w") as f: + f.write(str(value)) + print(f"Created secret: {secretname}") + secret_names.append(secretname) + return secret_names + + +def delete_secret_files(secret_names): + new_secret_names = set(secret_names) + existing_secret_dirs = set(os.listdir(BASE_DIR)) + + # Remove directories of the secrets that are not provided + for secret_dir in existing_secret_dirs - new_secret_names: + shutil.rmtree(os.path.join(BASE_DIR, secret_dir)) + print(f"Deleted secret: {secret_dir}") + + +if __name__ == "__main__": + if not os.path.isfile(SECRETS_JSON): + print("Path to secret Json files doesn't exist") + else: + with open(SECRETS_JSON) as f: + secrets = json.load(f) + + secret_names = create_secret_files(secrets) + delete_secret_files(secret_names) \ No newline at end of file From c4c8a8927d3e66aa46b7105bc053e2c2b8f423c4 Mon Sep 17 00:00:00 2001 From: cwadhwani-splunk Date: Wed, 15 Oct 2025 14:08:45 +0000 Subject: [PATCH 2/4] chore: Running integeration tests [run-int-tests] --- manage_secrets.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manage_secrets.py b/manage_secrets.py index ecffda569..83a666e2d 100644 --- a/manage_secrets.py +++ b/manage_secrets.py @@ -55,4 +55,4 @@ def delete_secret_files(secret_names): secrets = json.load(f) secret_names = create_secret_files(secrets) - delete_secret_files(secret_names) \ No newline at end of file + delete_secret_files(secret_names) From 1600288377acf43dce4fbc533723762ef4aef0ac Mon Sep 17 00:00:00 2001 From: cwadhwani-splunk Date: Thu, 16 Oct 2025 08:49:51 +0000 Subject: [PATCH 3/4] fix: fixing pipeline issue [run-int-tests] --- integration_tests/automatic_setup_compose.sh | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/integration_tests/automatic_setup_compose.sh b/integration_tests/automatic_setup_compose.sh index 5ccbb133c..f465db6e8 100755 --- a/integration_tests/automatic_setup_compose.sh +++ b/integration_tests/automatic_setup_compose.sh @@ -127,6 +127,7 @@ awk -v scheduler_path="$SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH" \ -v splunk_hec_host="$SPLUNK_HEC_HOST" \ -v splunk_hec_token="$SPLUNK_HEC_TOKEN" \ -v secret_folder_path="$SECRET_FOLDER_PATH" \ + -v enable_worker_poller_secrets="true" \ ' BEGIN { updated["SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH"] = 0; @@ -136,6 +137,7 @@ awk -v scheduler_path="$SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH" \ updated["SPLUNK_HEC_HOST"] = 0; updated["SPLUNK_HEC_TOKEN"] = 0; updated["SECRET_FOLDER_PATH"] = 0; + updated["ENABLE_WORKER_POLLER_SECRETS"] = 0; } { if ($1 == "SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH=") { @@ -159,6 +161,9 @@ awk -v scheduler_path="$SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH" \ } else if ($1 == "SECRET_FOLDER_PATH=") { print "SECRET_FOLDER_PATH=" secret_folder_path; updated["SECRET_FOLDER_PATH"] = 1; + } else if ($1 == "ENABLE_WORKER_POLLER_SECRETS=") { + print "ENABLE_WORKER_POLLER_SECRETS=" enable_worker_poller_secrets; + updated["ENABLE_WORKER_POLLER_SECRETS"] = 1; } else { print $0; } @@ -185,6 +190,9 @@ awk -v scheduler_path="$SCHEDULER_CONFIG_FILE_ABSOLUTE_PATH" \ if (updated["SECRET_FOLDER_PATH"] == 0) { print "SECRET_FOLDER_PATH=" secret_folder_path; } + if (updated["ENABLE_WORKER_POLLER_SECRETS"] == 0) { + print "ENABLE_WORKER_POLLER_SECRETS=" enable_worker_poller_secrets; + } } ' .env > "$TEMP_ENV_FILE" From c7234a7f5ded6af4bf27d158e89c2f84f920959e Mon Sep 17 00:00:00 2001 From: cwadhwani-splunk Date: Mon, 3 Nov 2025 08:03:05 +0000 Subject: [PATCH 4/4] chore: changes for PR comments --- docs/dockercompose/7-snmpv3-secrets.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/dockercompose/7-snmpv3-secrets.md b/docs/dockercompose/7-snmpv3-secrets.md index 8992e4f90..228df38f9 100644 --- a/docs/dockercompose/7-snmpv3-secrets.md +++ b/docs/dockercompose/7-snmpv3-secrets.md @@ -60,7 +60,7 @@ ENABLE_WORKER_POLLER_SECRETS=true ## Creating a new secret To create a new secret, -create secrets.json file inside folder (at SECRET_FOLDER_PATH), add entry for the new secrets with all the details. +create ``secrets.json`` file inside folder (at SECRET_FOLDER_PATH), add entry for the new secrets with all the details. Inside `docker_compose` directory run: