Skip to content

Add 'env_var_' prefix option for handling secrets - #775

Open
marehler wants to merge 10 commits into
netascode:developfrom
marehler:env_var_issue720
Open

Add 'env_var_' prefix option for handling secrets#775
marehler wants to merge 10 commits into
netascode:developfrom
marehler:env_var_issue720

Conversation

@marehler

@marehler marehler commented Apr 17, 2026

Copy link
Copy Markdown
Contributor

Related Issue(s)

Fixes #720

Related Collection Role

  • cisco.nac_dc_vxlan.validate
  • cisco.nac_dc_vxlan.dtc.create
  • cisco.nac_dc_vxlan.dtc.deploy
  • cisco.nac_dc_vxlan.dtc.remove
  • other

Related Data Model Element

  • vxlan.fabric
  • vxlan.global
  • vxlan.topology
  • vxlan.underlay
  • vxlan.overlay
  • vxlan.overlay_extensions
  • vxlan.policy
  • vxlan.multisite
  • defaults.vxlan
  • other

Proposed Changes

Add plugin to lookup up secrets starting with the 'env_var_' prefix. Same functionality as already supported for switch credentials. Examples:

---
---
vxlan:
  underlay:
    bgp:
      max_paths: 4
      authentication_enable: true
      authentication_key_type: 3
      authentication_key: env_var_bgp_auth_key
    multicast:
      ipv4:
        authentication_enable: true
        authentication_key: env_var_mcast_auth_key
  global:
    ebgp:
      aaa_freeform: |
        feature tacacs+
        tacacs-server key 7 env_var_tacacs_key
        ip tacacs source-interface mgmt0
        tacacs-server timeout 20
  • Created plugins/action/common/prepare_plugins/prep_005_resolve_env_vars.py
  • Runs as prepare plugin 005 in the prepare_service_model pipeline — after fabric/global setup and list defaults (001–004), before any topology processing (104+)
  • Recursively walks the entire model_extended data model
  • Any string value starting with env_var_ is resolved via os.getenv()
  • If the env var isn't set, a warning is emitted and the original string is kept unchanged
  • Verbose logging (-v) shows how many variables were resolved; -vvv shows each individually with its data model path
  • No impact on existing switch credential flow: get_credentials.py operates on the rendered inv_list (after templates), not the raw data model. The two mechanisms are complementary and don't interfere.

Test Notes

  1. BGP Fabric
TASK [cisco.nac_dc_vxlan.validate : Prepare Service Model] ***************************************************************************************************
task path: /Users/marehler/Git/sac-ndfc/nac-ndfc/collections/ansible_collections/cisco/nac_dc_vxlan/roles/validate/tasks/sub_main.yml:110
Resolved 'env_var_mcast_auth_key' from environment variable at 'vxlan.underlay.multicast.ipv4.authentication_key'
Resolved 'env_var_bgp_auth_key' from environment variable at 'vxlan.underlay.bgp.authentication_key'
Resolved 'env_var_tacacs_key' from environment variable at 'vxlan.global.ebgp.aaa_freeform'
Resolved 3 environment variable(s) in the data model
  1. VXLAN EVPN Fabric
TASK [cisco.nac_dc_vxlan.validate : Prepare Service Model] *******************************************************************
task path: /Users/marehler/Git/sac-ndfc/nac-ndfc/collections/ansible_collections/cisco/nac_dc_vxlan/roles/validate/tasks/sub_main.yml:110
Resolved 'env_var_ospf_auth_key' from environment variable at 'vxlan.underlay.ospf.authentication_key'
Resolved 1 environment variable(s) in the data model
  1. MSD Fabric
TASK [cisco.nac_dc_vxlan.validate : Prepare Service Model] ***************************************************************************************************
task path: /Users/marehler/Git/sac-ndfc/nac-ndfc/collections/ansible_collections/cisco/nac_dc_vxlan/roles/validate/tasks/sub_main.yml:110
Resolved 'env_var_dci_password' from environment variable at 'vxlan.multisite.overlay_dci.ebgp_password'
Resolved 1 environment variable(s) in the data model

Cisco Nexus Dashboard Version

3.2.2
4.2.1

Checklist

  • Latest commit is rebased from develop with merge conflicts resolved
  • New or updates to documentation has been made accordingly
  • Assigned the proper reviewers

@marehler
marehler requested a review from a team as a code owner April 17, 2026 07:39
@marehler marehler added the enhancement New feature or request label Apr 17, 2026
@marehler

marehler commented Jun 2, 2026

Copy link
Copy Markdown
Contributor Author

Sync'd fork with release 0.8.0.

@juburnet
juburnet requested review from ccoueffe and mikewiebe June 9, 2026 15:02
@juburnet juburnet added the 0.9.0 Release 0.9.0 label Jun 9, 2026
@juburnet juburnet added the ready for review PR Ready for Review label Jul 23, 2026
@marehler
marehler requested a review from skaszlik July 27, 2026 09:42

@skaszlik skaszlik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this new method of handling the passwords, please investigate how we can merge the prep_005_resolve_env_vars plugin with existing get_credentials.py to simplify the code. It looks like the prep_005 can completely replace the get_credentials.py

@marehler

marehler commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

Changed implementation so that resolved environment variables are not shown in the rendered files. Summary:

prep_005_resolve_env_vars.py

  • Before: prepare() called resolve_env_vars_recursive() which replaced env_var_XXX tokens with actual secret values in model_extended → secrets ended up in rendered files
  • After: prepare() calls validate_env_vars_recursive() which only checks env vars exist and warns if missing — tokens stay as-is in model_extended
  • resolve_env_vars_recursive() is kept and now exported for use by build_resource_data.py

build_resource_data.py

  • Imports copy and resolve_env_vars_recursive from prep_005
  • In build_resource(), after post-hook module_data adoption (line 336): deep-copies module_data, resolves env_var_ tokens in the copy, and uses the resolved copy as module_data
  • The rendered file keeps the env_var_XXX placeholders
  • The module_data sent to NDFC modules has actual values

Please note env_var_ secrets are still visible in Ansible logs. For switch credentials (get_credentials.py) using the dcnm_inventory module, the module's argument spec marks password and user_name with no_log: True. Ansible automatically redacts any parameter marked this way in task output → VALUE_SPECIFIED_IN_NO_LOG_PARAMETER. However for secrets using env_var_ and the dcnm_fabric module, the module accepts a generic config dict. Individual keys like protocol authentication keys are not marked no_log in the argument spec.

@marehler

Copy link
Copy Markdown
Contributor Author

With this new method of handling the passwords, please investigate how we can merge the prep_005_resolve_env_vars plugin with existing get_credentials.py to simplify the code. It looks like the prep_005 can completely replace the get_credentials.py

get_credentials.py has a different scope and fallback behavior. get_credentials has smart cascading fallbacks — if an env_var_ credential isn't set, it falls back to group_vars (ndfc_switch_username/ndfc_switch_password). The generic resolver in build_resource_data has no fallback — if the env var isn't set, the token stays as-is and a warning is emitted.
get_credentials also does more than env var resolution — it handles per-switch credential lookup from the data model, POAP discovery credentials, and deep-copy credential enrichment. It's a credential workflow plugin that happens to support env_var_ tokens, not a generic env var resolver. In conclusion, I think prep_005_resolve_env_vars cannot completely replace it.

@marehler
marehler requested a review from skaszlik July 29, 2026 08:59

@ccoueffe ccoueffe left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM but we need to document in netascode because It doesn't work during validate and we need a force_run_all in that case.

@marehler

Copy link
Copy Markdown
Contributor Author
  • Fixed env var resolution for policies in case of diff run. Root cause: diff.updated is computed from the rendered YAML before resolve_env_vars_recursive runs in build_resource_data.py. When _resolve_create_data prefers diff.updated during diff_run mode, unresolved env_var_ tokens are sent to ND. Change in manage_resources.py: In _resolve_create_data, when taking the diff.updated path, deep-copy the data and run resolve_env_vars_recursive on it before returning. Two imports added (copy, resolve_env_vars_recursive).
  • Documented limitations as mentioned by Charly.

@skaszlik skaszlik left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

0.9.0 Release 0.9.0 enhancement New feature or request ready for review PR Ready for Review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Support for 'env_var_' prefix option to secure any data model secret

4 participants