f5-veil 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. f5_veil-1.2.0/.gitignore +88 -0
  2. f5_veil-1.2.0/CHANGELOG.md +167 -0
  3. f5_veil-1.2.0/DISCLAIMER.md +29 -0
  4. f5_veil-1.2.0/LICENSE +21 -0
  5. f5_veil-1.2.0/PKG-INFO +341 -0
  6. f5_veil-1.2.0/README.md +309 -0
  7. f5_veil-1.2.0/SECURITY.md +41 -0
  8. f5_veil-1.2.0/docs/architecture.md +802 -0
  9. f5_veil-1.2.0/pyproject.toml +69 -0
  10. f5_veil-1.2.0/requirements.txt +1 -0
  11. f5_veil-1.2.0/src/veil/__init__.py +3 -0
  12. f5_veil-1.2.0/src/veil/__main__.py +8 -0
  13. f5_veil-1.2.0/src/veil/ad_dn_discovery.py +186 -0
  14. f5_veil-1.2.0/src/veil/answer_file.py +382 -0
  15. f5_veil-1.2.0/src/veil/apm_var_literal_discovery.py +115 -0
  16. f5_veil-1.2.0/src/veil/cert_keychain_discovery.py +131 -0
  17. f5_veil-1.2.0/src/veil/cli.py +778 -0
  18. f5_veil-1.2.0/src/veil/client_policy_discovery.py +122 -0
  19. f5_veil-1.2.0/src/veil/data_group_records_discovery.py +173 -0
  20. f5_veil-1.2.0/src/veil/description_discovery.py +146 -0
  21. f5_veil-1.2.0/src/veil/diagnostics.py +56 -0
  22. f5_veil-1.2.0/src/veil/fqdn_discovery.py +110 -0
  23. f5_veil-1.2.0/src/veil/ip_discovery.py +225 -0
  24. f5_veil-1.2.0/src/veil/irule_comment_discovery.py +115 -0
  25. f5_veil-1.2.0/src/veil/krb_realm_discovery.py +106 -0
  26. f5_veil-1.2.0/src/veil/ldap_filter_discovery.py +168 -0
  27. f5_veil-1.2.0/src/veil/leak_detector.py +496 -0
  28. f5_veil-1.2.0/src/veil/ledger.py +569 -0
  29. f5_veil-1.2.0/src/veil/monitor_recv_discovery.py +96 -0
  30. f5_veil-1.2.0/src/veil/remote_role_discovery.py +198 -0
  31. f5_veil-1.2.0/src/veil/saml_oauth_discovery.py +171 -0
  32. f5_veil-1.2.0/src/veil/scanner.py +652 -0
  33. f5_veil-1.2.0/src/veil/snmp_discovery.py +338 -0
  34. f5_veil-1.2.0/src/veil/sshd_discovery.py +157 -0
  35. f5_veil-1.2.0/src/veil/substitute.py +1086 -0
  36. f5_veil-1.2.0/src/veil/syslog_discovery.py +196 -0
  37. f5_veil-1.2.0/src/veil/tokenizer.py +93 -0
  38. f5_veil-1.2.0/src/veil/ucs_archive.py +211 -0
  39. f5_veil-1.2.0/src/veil/username_discovery.py +118 -0
  40. f5_veil-1.2.0/tests/.gitkeep +0 -0
  41. f5_veil-1.2.0/tests/README.md +69 -0
  42. f5_veil-1.2.0/tests/conftest.py +6 -0
  43. f5_veil-1.2.0/tests/test_ad_dn_bareword_redaction.py +235 -0
  44. f5_veil-1.2.0/tests/test_ad_dn_redaction.py +308 -0
  45. f5_veil-1.2.0/tests/test_answer_file.py +369 -0
  46. f5_veil-1.2.0/tests/test_apm_firewall_kinds.py +147 -0
  47. f5_veil-1.2.0/tests/test_apm_var_literal_redaction.py +155 -0
  48. f5_veil-1.2.0/tests/test_bareword_infix_redaction.py +250 -0
  49. f5_veil-1.2.0/tests/test_caption_servicename_redaction.py +106 -0
  50. f5_veil-1.2.0/tests/test_cert_keychain_redaction.py +216 -0
  51. f5_veil-1.2.0/tests/test_cli.py +553 -0
  52. f5_veil-1.2.0/tests/test_cli_multi_file.py +269 -0
  53. f5_veil-1.2.0/tests/test_cli_ucs.py +399 -0
  54. f5_veil-1.2.0/tests/test_client_policy_redaction.py +185 -0
  55. f5_veil-1.2.0/tests/test_data_group_records_redaction.py +172 -0
  56. f5_veil-1.2.0/tests/test_description_redaction.py +266 -0
  57. f5_veil-1.2.0/tests/test_filestore_colon_redaction.py +100 -0
  58. f5_veil-1.2.0/tests/test_fqdn_leaf_form_redaction.py +121 -0
  59. f5_veil-1.2.0/tests/test_fqdn_redaction.py +271 -0
  60. f5_veil-1.2.0/tests/test_gtm_kinds.py +214 -0
  61. f5_veil-1.2.0/tests/test_integration_real_configs.py +483 -0
  62. f5_veil-1.2.0/tests/test_ip_discovery.py +226 -0
  63. f5_veil-1.2.0/tests/test_ip_substitution.py +238 -0
  64. f5_veil-1.2.0/tests/test_irule_tcl_redaction.py +530 -0
  65. f5_veil-1.2.0/tests/test_krb_realm_redaction.py +165 -0
  66. f5_veil-1.2.0/tests/test_ldap_filter_redaction.py +174 -0
  67. f5_veil-1.2.0/tests/test_leak_detector.py +374 -0
  68. f5_veil-1.2.0/tests/test_ledger.py +130 -0
  69. f5_veil-1.2.0/tests/test_ltm_extras_kinds.py +237 -0
  70. f5_veil-1.2.0/tests/test_monitor_recv_redaction.py +109 -0
  71. f5_veil-1.2.0/tests/test_net_and_gtm_region_kinds.py +82 -0
  72. f5_veil-1.2.0/tests/test_oauth_key_id_redaction.py +66 -0
  73. f5_veil-1.2.0/tests/test_profile_kind.py +208 -0
  74. f5_veil-1.2.0/tests/test_qstring_wrapped_header_paths.py +166 -0
  75. f5_veil-1.2.0/tests/test_remote_role_redaction.py +279 -0
  76. f5_veil-1.2.0/tests/test_saml_oauth_redaction.py +216 -0
  77. f5_veil-1.2.0/tests/test_scan_many.py +273 -0
  78. f5_veil-1.2.0/tests/test_scanner.py +148 -0
  79. f5_veil-1.2.0/tests/test_snmp_redaction.py +396 -0
  80. f5_veil-1.2.0/tests/test_sshd_banner_redaction.py +238 -0
  81. f5_veil-1.2.0/tests/test_substitute.py +446 -0
  82. f5_veil-1.2.0/tests/test_syslog_redaction.py +236 -0
  83. f5_veil-1.2.0/tests/test_tokenizer.py +44 -0
  84. f5_veil-1.2.0/tests/test_ucs_archive.py +305 -0
  85. f5_veil-1.2.0/tests/test_username_redaction.py +208 -0
  86. f5_veil-1.2.0/tests/test_version_field_skip.py +115 -0
@@ -0,0 +1,88 @@
1
+ # ============================================================================
2
+ # VEIL-specific — NEVER commit obfuscation artifacts
3
+ # ============================================================================
4
+ *.answers.enc
5
+ *.sanitized.conf
6
+ *.sanitized.tcl
7
+ *.sanitized.ucs
8
+ *.restored.conf
9
+ test_configs/customer/
10
+ test_configs/real/
11
+ test_configs/_phase_verify/
12
+ *.ucs
13
+
14
+ # ============================================================================
15
+ # Session / handoff state — may contain personal network info, hostnames,
16
+ # customer device names, etc. Used to prime new Claude Code sessions; not
17
+ # part of the project history. NEVER commit.
18
+ # ============================================================================
19
+ BRIDGE_NEXT_SESSION.md
20
+ V12_LEAK_FIX_PLAN.md
21
+ CLAUDE.md
22
+ .claude/
23
+
24
+ # ============================================================================
25
+ # Python
26
+ # ============================================================================
27
+ __pycache__/
28
+ *.py[cod]
29
+ *$py.class
30
+ *.so
31
+ .Python
32
+ build/
33
+ develop-eggs/
34
+ dist/
35
+ downloads/
36
+ eggs/
37
+ .eggs/
38
+ lib/
39
+ lib64/
40
+ parts/
41
+ sdist/
42
+ var/
43
+ wheels/
44
+ share/python-wheels/
45
+ *.egg-info/
46
+ .installed.cfg
47
+ *.egg
48
+ MANIFEST
49
+
50
+ # Virtual environments
51
+ .env
52
+ .venv
53
+ env/
54
+ venv/
55
+ ENV/
56
+
57
+ # Testing
58
+ .coverage
59
+ .coverage.*
60
+ .pytest_cache/
61
+ htmlcov/
62
+ .tox/
63
+ .nox/
64
+ coverage.xml
65
+ *.cover
66
+ .hypothesis/
67
+
68
+ # Type checkers
69
+ .mypy_cache/
70
+ .pyre/
71
+ .pytype/
72
+ .ruff_cache/
73
+
74
+ # ============================================================================
75
+ # IDE / Editor
76
+ # ============================================================================
77
+ .vscode/
78
+ .idea/
79
+ *.swp
80
+ *.swo
81
+ *~
82
+
83
+ # ============================================================================
84
+ # OS
85
+ # ============================================================================
86
+ .DS_Store
87
+ Thumbs.db
88
+ desktop.ini
@@ -0,0 +1,167 @@
1
+ # Changelog
2
+
3
+ All notable changes to **f5-veil** are documented here.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.2.0] — 2026-06-16
9
+
10
+ ### Added — input-source expansions
11
+
12
+ - **Multi-file two-pass ingestion** for `bigip_base.conf` +
13
+ `bigip.conf` pairs. The base file's objects (VLANs, self-IPs,
14
+ route-domains, etc.) are registered in a shared ledger before the
15
+ main file's references need to resolve. New `scan_many` API; new
16
+ CLI `--input <path>` repeatable flag plus `--output-dir`.
17
+ - **UCS archive ingestion** (extract-only). CLI auto-detects `.ucs`
18
+ input, extracts the allowlist members (`config/bigip_base.conf`,
19
+ `config/bigip.conf`, optional `config/bigip_user.conf`),
20
+ obfuscates each, writes to `--output-dir`. UCS is never modified
21
+ or re-packed; allowlist excludes `bigip_script.conf` (deferred —
22
+ iApp templates collide with the IP placeholder model; tracked for
23
+ v1.3 / v2.0).
24
+
25
+ ### Added — leak-coverage hardening (19 finding-groups + follow-ups)
26
+
27
+ Driven by real-corpus manual inspection. Approximately 28 new
28
+ `Kind` values across 12 new walkers.
29
+
30
+ - **Unknown top-level body walkers** — closes the brace-skip gap on
31
+ blocks whose body content carries customer-identifying data:
32
+ - `sys snmp` — community / trap bucket headers, plaintext
33
+ community strings, `sys-contact`, `sys-location`
34
+ (`SNMP_COMMUNITY`, `SNMP_TRAP`, `SNMP_COMMUNITY_SECRET`,
35
+ `SYS_CONTACT`, `SYS_LOCATION`)
36
+ - `sys syslog` — remote-server bucket headers (`SYSLOG_SERVER`)
37
+ - `sys sshd` — banner text (multi-line QSTRING; `SSHD_BANNER`)
38
+ - `auth remote-role role-info` — bucket-path discovery
39
+ (`REMOTE_ROLE`)
40
+ - **Nested-bucket walkers inside known top-level kinds** — the
41
+ enclosing kind's body is brace-skipped by the main loop, so
42
+ nested-object bareword names leak:
43
+ - `cert-key-chain` bucket names inside `ltm profile client-ssl`
44
+ bodies (`CERT_KEY_CHAIN`)
45
+ - `client-policy` bucket names inside APM profile bodies
46
+ (`CLIENT_POLICY`)
47
+ - **Cross-cutting field walkers**:
48
+ - Identity / hostname — `admin-name`, `basic-auth-username`,
49
+ `basic-auth-realm`, `user`, `account-name`, `server-name` →
50
+ `USERNAME`
51
+ - Kerberos realm — `realm` field with uppercase value (catches
52
+ public-TLD realms like `BOGUS.COM` that the FQDN walker by
53
+ design skips) → `KRB_REALM`
54
+ - LDAP filter — `filter` field inside LDAP-flavoured blocks →
55
+ `LDAP_FILTER`
56
+ - LDAP base-DN bareword — extends `AD_GROUP_DN` walker to catch
57
+ bareword `DC=...,DC=...` values in `base-dn` /
58
+ `search-base-dn` fields
59
+ - SAML / OAuth identifiers — `entity-id`, `sso-uri`,
60
+ `single-logout-uri`, `single-logout-response-uri`, `audience`
61
+ (braced-list form), `issuer`, `key-id` as dedicated kinds so
62
+ non-FQDN-shaped opaque values are caught (`SAML_ENTITY_ID`,
63
+ `SAML_SSO_URI`, `SAML_SLO_URI`, `SAML_SLO_RESPONSE_URI`,
64
+ `OAUTH_AUDIENCE`, `OAUTH_ISSUER`, `OAUTH_KEY_ID`)
65
+ - `caption` and `service-name` folded into the `DESC` walker
66
+ - Monitor `recv` strings — `MONITOR_RECV`
67
+ - Data-group `records` bucket headers (context-gated, catches
68
+ public-TLD entries that the global FQDN walker skips) —
69
+ `DATA_GROUP_RECORD`
70
+ - APM `expression "return {LITERAL}"` Tcl-literal pattern in
71
+ `variable-assign` bodies — `APM_VAR_LITERAL`
72
+ - **Substring-substitution variants**:
73
+ - F5 filestore colon-separator
74
+ (`:Common:<leaf>_<index>_<index>`) — covers `cache-path`
75
+ references that the slash-form substring sub missed
76
+ - FQDN-shaped leaf form for path-shape entries whose leaf is
77
+ public-TLD — covers `source-path /config/ssl/ssl.csr/<fqdn>.com`
78
+ references
79
+ - QSTRING-wrapped header path detection — catches bot-defense
80
+ signature path shapes
81
+ - Per-kind right-boundary protection (FQDN compound filenames)
82
+ - **Fixes**:
83
+ - IP-walker version-field exclusion — `version 17.5.1.5` no
84
+ longer gets substituted as if it were an IPv4 address
85
+ - AD_GROUP_DN qualifier relaxation — drop CN= requirement so
86
+ OU-prefix DNs (LDAP `base` values) are redacted as a whole,
87
+ not just the DC suffix
88
+ - Orphan-check substring-shadow exemption — FQDN entries
89
+ intentionally shadowed by longer SAML/OAuth ledger entries
90
+ don't trip the cross-reference integrity assertion
91
+
92
+ ### Verification
93
+
94
+ Real-corpus canary count for the integration pair (homelab
95
+ AD-domain root-label, case-insensitive grep) went from **40 → 0**
96
+ across the v1.2 cycle. Full test suite: **503 → 660+** tests
97
+ passing, zero regressions, byte-exact round-trip preserved.
98
+
99
+ ### Known gaps (documented, operator review required)
100
+
101
+ See [docs/architecture.md](docs/architecture.md) for the full
102
+ list. Highlights:
103
+
104
+ - iRule `varname` customer-name leaks
105
+ - Public-TLD FQDNs outside the dedicated walker / cert-path /
106
+ source-path contexts
107
+ - Free-text Tcl expression literals (`expression "[mcget {...}]"`)
108
+ without a recognised shape
109
+
110
+ ## [1.1.1] — never published to PyPI
111
+
112
+ Corrective license swap — standard MIT + non-binding `DISCLAIMER.md`
113
+ replaces the prior "MIT-Modified Named-Party Exclusion" language.
114
+ PyPI republish was deferred; v1.1.1's content is included in v1.2.0.
115
+
116
+ ## [1.1.0] — pushed 2026-06-13
117
+
118
+ ### Added
119
+
120
+ - **BAREWORD infix substring substitution** — catches identifiers
121
+ embedded in compound barewords. Examples:
122
+ - `application-uri https://10.0.0.42/path` (IP inside URL)
123
+ - `iRule references like /Common/web1:80` (path inside compound)
124
+ - IP ranges like `10.0.0.1-10.0.0.50`
125
+ - File-storage compound filenames `<fqdn>_<index>_<index>`
126
+ - Word-boundary protection prevents partial matches against longer
127
+ numeric / identifier runs.
128
+
129
+ ## [1.0.0] — pushed 2026-06-13
130
+
131
+ First stable release. Production-shaped against real BIG-IP
132
+ configurations from a controlled-environment lab corpus.
133
+
134
+ ### Added
135
+
136
+ - **CLI**: `veil obfuscate` and `veil deobfuscate` commands. Exit
137
+ codes 0 (success), 2 (CLI usage), 3 (input not readable),
138
+ 4 (diagnostics non-empty without `--allow-incomplete`),
139
+ 5 (leak detector tripped under `--strict`).
140
+ - **Answer file**: AES-256-GCM-encrypted, scrypt KDF, atomic
141
+ writes.
142
+ - **Path-shape kinds**: pool, virtual server, node, monitor, iRule,
143
+ partition (LTM); pool, wide-IP, server, datacenter, region (GTM);
144
+ VLAN, route-domain, self-IP, trunk (net); policy, profile (APM);
145
+ policy, rule-list, address-list, port-list (security firewall);
146
+ data-group, SNAT, SNAT pool, virtual-address (LTM extras);
147
+ profile (LTM, with factory built-in exemption); UNKNOWN
148
+ best-effort registration for unrecognised top-level blocks.
149
+ - **IP literal handling**: bare IPv4 / IPv6 substituted into RFC
150
+ 5737 / RFC 3849 docs ranges, preserving source `/24` and `/64`
151
+ structure first-seen-first-allocated.
152
+ - **Free-text**: description bodies (QSTRING, bareword, braced),
153
+ Tcl `#` comments inside `ltm rule` bodies, LDAP / AD distinguished
154
+ names (`CN=...,DC=...`) inside any QSTRING, internal-FQDN
155
+ discovery (`*.local`, `*.corp`, `*.lan`, `*.internal`,
156
+ `*.intranet`, `*.home.arpa`, `*.private`).
157
+ - **Leak detector**: post-substitution check that flags common
158
+ patterns (RFC1918 / CGNAT / link-local IPs, internal FQDNs, MAC
159
+ addresses, identifier-shaped barewords, paths with non-safe
160
+ partitions).
161
+ - **Strict mode**: `--strict` aborts on any leak-detector warning.
162
+
163
+ ## Pre-1.0
164
+
165
+ Versions 0.0.1 through 0.0.14 were development-cycle iterations.
166
+ The detailed history is captured in the git log; consult
167
+ `git log --oneline` for per-commit context.
@@ -0,0 +1,29 @@
1
+ # DISCLAIMER
2
+
3
+ ## Software License
4
+
5
+ This project is released under the **MIT License**. The full text of that
6
+ license is in [LICENSE](LICENSE) and is the only legally binding license
7
+ governing your use, modification, and redistribution of this software.
8
+
9
+ ## Personal Statement of Intent
10
+
11
+ The following is a personal statement from the author. It is **not** a
12
+ software license term, **not** an additional restriction under the MIT
13
+ License, and **not** enforceable as a condition of use. It is provided here
14
+ only as a statement of the author's personal preferences regarding the
15
+ audience of this work.
16
+
17
+ > The author would prefer that this software not be used, redistributed, or
18
+ > incorporated by Austin Geraci or by WorldTech IT (or by any employee,
19
+ > agent, contractor, consultant, or affiliate acting on their behalf, or by
20
+ > any entity they own, operate, or control).
21
+ >
22
+ > This preference does not modify the MIT license grant. Any party covered
23
+ > by the MIT license remains licensed under its terms.
24
+ >
25
+ > Inspired by Stewart Semple's Black 3.0 paint license.
26
+
27
+ If you are evaluating this project for inclusion in another work, for
28
+ packaging, or for compliance review: treat the MIT license file as
29
+ authoritative and ignore the statement above for legal purposes.
f5_veil-1.2.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Blake Deakins
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to
7
+ deal in the Software without restriction, including without limitation the
8
+ rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9
+ sell copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20
+ FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
21
+ IN THE SOFTWARE.
f5_veil-1.2.0/PKG-INFO ADDED
@@ -0,0 +1,341 @@
1
+ Metadata-Version: 2.4
2
+ Name: f5-veil
3
+ Version: 1.2.0
4
+ Summary: F5 BIG-IP config obfuscator/de-obfuscator — sanitize customer configs for safe AI analysis
5
+ Project-URL: Homepage, https://github.com/BDeakins/f5-veil
6
+ Project-URL: Issues, https://github.com/BDeakins/f5-veil/issues
7
+ Project-URL: Source, https://github.com/BDeakins/f5-veil
8
+ Author: Blake Deakins
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai-safety,bigip,f5,irules,obfuscation,redaction,security,tmos
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: Intended Audience :: System Administrators
16
+ Classifier: Operating System :: OS Independent
17
+ Classifier: Programming Language :: Python :: 3
18
+ Classifier: Programming Language :: Python :: 3.10
19
+ Classifier: Programming Language :: Python :: 3.11
20
+ Classifier: Programming Language :: Python :: 3.12
21
+ Classifier: Topic :: Security
22
+ Classifier: Topic :: System :: Networking
23
+ Classifier: Topic :: System :: Systems Administration
24
+ Requires-Python: >=3.10
25
+ Requires-Dist: cryptography>=42.0
26
+ Provides-Extra: dev
27
+ Requires-Dist: mypy>=1.10; extra == 'dev'
28
+ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
29
+ Requires-Dist: pytest>=8.0; extra == 'dev'
30
+ Requires-Dist: ruff>=0.5; extra == 'dev'
31
+ Description-Content-Type: text/markdown
32
+
33
+ # f5-veil
34
+
35
+ F5 BIG-IP config obfuscator / de-obfuscator — sanitize customer configs
36
+ for safe AI analysis, then restore identifiers byte-exactly after the
37
+ AI is done.
38
+
39
+ ## Status
40
+
41
+ **v1.2** — production-shaped against real BIG-IP configurations.
42
+
43
+ Covers ~50 object kinds across LTM, GTM, net, APM, sys, security
44
+ firewall, and SAML/OAuth/Kerberos/SNMP/syslog/SSHD bodies. Bare
45
+ IPv4 / IPv6 literals substituted to RFC 5737 / RFC 3849 docs ranges
46
+ with `/24` and `/64` source-subnet preservation. All three
47
+ description body forms (QSTRING, bareword, braced) plus `caption`
48
+ and `service-name` fields redacted. Tcl `#` comments inside `ltm
49
+ rule` bodies redacted. Identifier substring substitution inside
50
+ every QSTRING **and every BAREWORD** (catches monitor send/recv
51
+ strings, APM policy expressions, bot-defense signatures, URL-shaped
52
+ barewords, IP ranges, F5 filestore colon-separator paths
53
+ (`:Common:<leaf>_<index>_<index>`), public-TLD FQDN leafs in
54
+ source-paths). LDAP / AD distinguished names embedded in any
55
+ QSTRING **and** as bareword `base-dn` / `search-base-dn` values.
56
+ Kerberos realms (uppercase form, public-TLD support). SAML / OAuth
57
+ identifier fields (entity-id, sso-uri, slo-uri, audience, issuer,
58
+ key-id) as dedicated kinds — non-FQDN-shaped opaque values are
59
+ caught. APM `expression "return {LITERAL}"` Tcl-literal patterns
60
+ catch hard-coded session-variable values (domains, usernames,
61
+ occasionally credentials). Multi-file two-pass ingestion
62
+ (`bigip_base.conf` + `bigip.conf`). UCS archive ingestion
63
+ (extract-only). AES-256-GCM-encrypted answer file with scrypt KDF.
64
+ Round-trip is byte-exact for every shape the parser covers.
65
+
66
+ Real-corpus canary count for the v1.2 integration pair went from
67
+ 40 → 0 across the v1.2 leak-coverage cycle (19 finding-groups
68
+ discovered via manual inspection plus post-sign-off follow-ups).
69
+ 660+ tests pass with byte-exact round-trip preserved.
70
+
71
+ Documented gaps (see [docs/architecture.md](docs/architecture.md)
72
+ "Known gaps"): iRule `varname` customer leaks, public-TLD FQDNs
73
+ outside cert/path/SAML contexts, free-text Tcl expression literals
74
+ without recognised shape.
75
+
76
+ ## The Problem
77
+
78
+ F5 engineers want to use AI tools (Claude, ChatGPT, Copilot, etc.) to
79
+ analyze configurations, write iRules, and troubleshoot issues. But
80
+ customer configurations contain identifying information — IPs,
81
+ hostnames, pool names, virtual server names, monitor names, AD group
82
+ DNs, partition labels — that cannot legally or contractually be sent
83
+ to a third-party AI under most customer NDAs and employer policies.
84
+ The penalty for leaking customer data to an AI is often immediate
85
+ termination.
86
+
87
+ ## What VEIL does
88
+
89
+ ```
90
+ veil obfuscate → sanitized.conf + answers.enc (encrypted)
91
+ → safe to paste into AI
92
+
93
+ [engineer collaborates with AI on the sanitized config]
94
+
95
+ veil deobfuscate → restored.conf (real identifiers reinstated,
96
+ including in any new content the AI generated)
97
+ ```
98
+
99
+ Every customer-identifying value gets a typed placeholder
100
+ (`POOL_0001`, `VS_0001`, `NODE_0001`, `IRULE_0001`, `DESC_0001`,
101
+ `AD_GROUP_DN_0001`, `SAML_ENTITY_ID_0001`, `SNMP_COMMUNITY_SECRET_0001`,
102
+ `SSHD_BANNER_0001`, `USERNAME_0001`, `APM_VAR_LITERAL_0001`, etc.),
103
+ the original bytes go into an encrypted answer file, and the
104
+ de-obfuscator restores everything byte-exactly — including any
105
+ placeholder text the AI produced in new content it wrote.
106
+
107
+ ## Safety warnings
108
+
109
+ > **VEIL is a safety net, not a guarantee.**
110
+ > A parser miss = customer data leaked to an LLM = potential
111
+ > career-ending incident. Always review the sanitized output before
112
+ > sending it anywhere.
113
+
114
+ - Read the sanitized file end-to-end before sending it to AI.
115
+ - The leak detector flags common patterns (RFC1918 IPs, `.local` /
116
+ `.corp` / `.lan` / `.internal` domains, MAC addresses,
117
+ identifier-shaped barewords, paths with non-safe partitions). It is
118
+ heuristic — a clean run is strong evidence, not proof.
119
+ - Use `--strict` mode to abort on any leak-detector warning.
120
+ - Use `--allow-incomplete` only when you understand exactly which kinds
121
+ the parser doesn't yet recognise.
122
+ - Protect the answer file as you would a UCS archive. Anyone with the
123
+ file and the passphrase can recover the original configuration.
124
+ - Never commit `*.answers.enc` or `*.sanitized.conf` to a repo. The
125
+ shipped `.gitignore` blocks both — keep it that way.
126
+ - VEIL does not attempt to obfuscate inside binary blobs, base64-encoded
127
+ archives, or compiled artifacts. Strip those before obfuscation.
128
+
129
+ ## Installation
130
+
131
+ ```bash
132
+ pip install f5-veil
133
+ ```
134
+
135
+ Or from source:
136
+
137
+ ```bash
138
+ git clone https://github.com/BDeakins/f5-veil
139
+ cd f5-veil
140
+ pip install -e .
141
+ ```
142
+
143
+ Requires Python 3.10 or newer.
144
+
145
+ ## Usage
146
+
147
+ ```bash
148
+ # Obfuscate a single bigip.conf
149
+ veil obfuscate --input bigip.conf \
150
+ --output bigip.sanitized.conf \
151
+ --answer-file bigip.answers.enc
152
+
153
+ # De-obfuscate (AI may have introduced new content; placeholders inside
154
+ # new content are restored too)
155
+ veil deobfuscate --input bigip.modified.conf \
156
+ --output bigip.restored.conf \
157
+ --answer-file bigip.answers.enc
158
+
159
+ # Dry-run obfuscation — report what would change, write nothing
160
+ veil obfuscate --input bigip.conf --dry-run
161
+
162
+ # Strict mode — abort if the leak detector finds anything suspicious
163
+ veil obfuscate --input bigip.conf --strict ...
164
+
165
+ # Allow-incomplete mode — proceed even with unhandled top-level blocks
166
+ # (e.g. ltm dns, security dos). Use only when you've reviewed the
167
+ # diagnostics and understand the residual leak surface.
168
+ veil obfuscate --input bigip.conf --allow-incomplete ...
169
+ ```
170
+
171
+ ### Multi-file mode (`bigip_base.conf` + `bigip.conf`)
172
+
173
+ ```bash
174
+ # Pass both files; base file first so its objects (VLANs, self-IPs,
175
+ # route domains) land in the ledger before the main file's references
176
+ # need to resolve. Output goes to a directory keyed by basename.
177
+ veil obfuscate --input bigip_base.conf \
178
+ --input bigip.conf \
179
+ --output-dir sanitized/ \
180
+ --answer-file device.answers.enc
181
+
182
+ veil deobfuscate --input sanitized/bigip_base.conf \
183
+ --input sanitized/bigip.conf \
184
+ --output-dir restored/ \
185
+ --answer-file device.answers.enc
186
+ ```
187
+
188
+ `--input` order on the deobfuscate side must match the order recorded
189
+ in the answer file at obfuscation time. Reordering is a hard error,
190
+ not a silent miscorrelation.
191
+
192
+ ### UCS archive mode (`device.ucs`)
193
+
194
+ ```bash
195
+ # Hand VEIL the UCS directly. It extracts the allowlisted config-file
196
+ # members (config/bigip_base.conf, config/bigip.conf, and the
197
+ # optional config/bigip_user.conf), obfuscates each, and writes them
198
+ # as separate text files into --output-dir. Everything else in the
199
+ # UCS (bigip_script.conf, certs, keys, licenses, binaries, state
200
+ # files, .diffVersions snapshots) is ignored — never read, never
201
+ # written.
202
+ veil obfuscate --input device.ucs \
203
+ --output-dir sanitized/ \
204
+ --answer-file device.answers.enc
205
+
206
+ # Deobfuscate the sanitized text files via the standard multi-file
207
+ # flow. VEIL does NOT recreate the UCS — if you need a closed-loop
208
+ # UCS for restore, re-pack the restored files into the original
209
+ # archive yourself (e.g. with tar).
210
+ veil deobfuscate --input sanitized/bigip_base.conf \
211
+ --input sanitized/bigip.conf \
212
+ --input sanitized/bigip_user.conf \
213
+ --output-dir restored/ \
214
+ --answer-file device.answers.enc
215
+ ```
216
+
217
+ **Note on `bigip_script.conf`:** the file containing iRules and
218
+ iApp templates is intentionally NOT in the v1.2 UCS allowlist —
219
+ its iApp template bodies contain literal RFC 5737 docs-range IPs
220
+ in user-facing help text that collide with VEIL's IP placeholder
221
+ model. See `docs/architecture.md` ("UCS archive ingestion") for the
222
+ threat model, allowlist rationale, and the architectural fix
223
+ planned for v1.3 / v2.0. If you need iRule / iApp coverage today,
224
+ hand `bigip_script.conf` to the LLM as a separate plain-text file.
225
+
226
+ Exit codes: 0 success, 2 CLI usage error, 3 input not readable, 4
227
+ diagnostics non-empty without `--allow-incomplete`, 5 leak detector
228
+ tripped under `--strict`.
229
+
230
+ ## Identifier scope
231
+
232
+ **Obfuscated by VEIL (v1.2):**
233
+
234
+ - **LTM:** pool, virtual server, node, monitor, iRule, partition,
235
+ profile (custom — built-ins like `/Common/http` pass through as
236
+ universal BIG-IP signal), data-group name, data-group **records**
237
+ (operator-chosen lookup keys, even public-TLD ones), SNAT, SNAT
238
+ pool, virtual-address
239
+ - **GTM:** pool, wide-IP, server, datacenter, region
240
+ - **Net:** VLAN, route-domain, self-IP, trunk
241
+ - **APM:** policy, profile, `cert-key-chain` and `client-policy`
242
+ nested bucket names, `expression "return {LITERAL}"` Tcl literals
243
+ in `variable-assign` blocks
244
+ - **SAML / OAuth:** entity-id, sso-uri, single-logout-uri,
245
+ single-logout-response-uri, audience, issuer, key-id — dedicated
246
+ kinds so non-FQDN-shaped opaque values are caught (the FQDN
247
+ walker alone wouldn't catch URN entity-IDs or public-TLD URLs)
248
+ - **Identity / field walkers:** `admin-name`, `basic-auth-username`,
249
+ `basic-auth-realm`, `user`, `account-name`, `server-name` →
250
+ `USERNAME`; LDAP `filter` field; LDAP `base-dn` / `search-base-dn`
251
+ bareword DC=...,DC=... shapes
252
+ - **Sys family:** `sys snmp` body (community / trap bucket headers,
253
+ plaintext community strings, `sys-contact`, `sys-location`);
254
+ `sys syslog` remote-server bucket headers; `sys sshd` banner
255
+ text (multi-line QSTRING covered); `auth remote-role role-info`
256
+ bucket headers
257
+ - **Kerberos:** uppercase realm values (`ACME.CORP`, public TLDs
258
+ included — the FQDN walker by design only catches internal-suffix
259
+ realms)
260
+ - **Security firewall:** policy, rule-list, address-list, port-list
261
+ - **Network literals:** bare IPv4 / IPv6 (substituted into RFC 5737 /
262
+ RFC 3849 docs ranges, preserving source `/24` and `/64` structure
263
+ first-seen-first-allocated); IP-walker skips version-field values
264
+ (`version 17.5.1.5` no longer gets substituted as an IP)
265
+ - **Free-text:**
266
+ - `description` / `caption` / `service-name` bodies — QSTRING,
267
+ bareword, and braced forms all redacted to `DESC_NNNN`
268
+ - Tcl `#` comments inside `ltm rule` bodies — redacted to
269
+ `IRULE_COMMENT_NNNN`
270
+ - LDAP / AD distinguished names (`CN=...,DC=...` AND
271
+ `OU=...,DC=...`) anywhere inside any QSTRING — redacted to
272
+ `AD_GROUP_DN_NNNN`
273
+ - Internal-FQDN discovery (`*.local`, `*.corp`, `*.lan`,
274
+ `*.internal`, `*.intranet`, `*.home.arpa`, `*.private`) inside
275
+ any WORD or QSTRING — redacted to `FQDN_NNNN`
276
+ - Monitor `recv` strings (HTML titles, product names) — redacted
277
+ to `MONITOR_RECV_NNNN`
278
+ - F5 filestore colon-separator paths
279
+ (`:Common:<leaf>_<index>_<index>`) — caught via substring sub
280
+ variant on path-shape entries
281
+ - Any other ledger identifier appearing as a substring inside any
282
+ QSTRING / BAREWORD (monitor send-strings, APM policy
283
+ expressions, bot-defense signatures, URL-shaped barewords like
284
+ `https://10.0.0.42/path`, IP ranges like `10.0.0.1-10.0.0.50`)
285
+ — substring-substituted in place with word-boundary protection
286
+ - Multi-file mode: `bigip_base.conf` + `bigip.conf` ingest as a
287
+ shared ledger so base-file objects substitute correctly when
288
+ referenced from the main file
289
+ - UCS archive mode: extract-only, allowlists `config/bigip_base.conf`,
290
+ `config/bigip.conf`, `config/bigip_user.conf`
291
+
292
+ **Documented gaps (operator review required):**
293
+
294
+ - iRule `varname` customer-name leaks — renaming would break
295
+ positional Tcl refs, so VEIL does not auto-redact
296
+ - Public-TLD FQDNs outside the dedicated walker / cert-path /
297
+ source-path contexts — the global FQDN walker only catches
298
+ internal-suffix TLDs to avoid false positives on legitimate
299
+ public DNS references
300
+ - Free-text Tcl expression literals (`expression "[mcget {...}]"`)
301
+ without a recognised shape
302
+ - Persistent cross-run identifier map (deferred to v2.0)
303
+ - Folder-as-own-kind (`/Common/folder/sub/leaf` currently collapses
304
+ folder into the leaf placeholder) — v1.3+
305
+
306
+ ## Roadmap
307
+
308
+ - **v1.0** — `bigip.conf` only. Shipped.
309
+ - **v1.1** — BAREWORD infix substring substitution (catches URLs,
310
+ IP ranges, compound barewords). Shipped.
311
+ - **v1.2** — `bigip_base.conf` multi-file two-pass discovery, UCS
312
+ archive ingestion (extract-only), `auth remote-role role-info`
313
+ bucket-path discovery, plus a 19-finding-group leak-coverage
314
+ hardening cycle driven by real-corpus manual inspection:
315
+ sys snmp / sys syslog / sys sshd body walkers, cert-key-chain and
316
+ client-policy nested bucket walkers, identity / Kerberos realm /
317
+ LDAP filter / SAML+OAuth / data-group-record / monitor recv /
318
+ APM expression literal field walkers, filestore colon-separator
319
+ substring sub, FQDN-shaped leaf substring sub. Real-corpus canary
320
+ count for the integration pair: 40 → 0. Shipped.
321
+ - **v1.3** — Personal-use Docker image + thin FastAPI wrapper around
322
+ the CLI (paste config in browser, get sanitized output and encrypted
323
+ answer file out). RAM-only processing, no auth, **not for internet
324
+ exposure**. CLI remains the canonical distribution. See the threat
325
+ model in [docs/architecture.md](docs/architecture.md).
326
+ - **v2.0** — Persistent cross-run identifier map (same source
327
+ identifier → same placeholder across runs, for ongoing engagements).
328
+ - **v2.1 / v3.0** — Hardened multi-user web service (auth, HTTPS,
329
+ audit logging, hard ephemerality guarantees, rate limiting). Shares
330
+ design surface with the v2.0 persistent map (auth + secret storage).
331
+
332
+ ## License
333
+
334
+ MIT — see [LICENSE](LICENSE).
335
+
336
+ A personal statement of intent regarding the audience of this work is in
337
+ [DISCLAIMER.md](DISCLAIMER.md). It is not a license term.
338
+
339
+ ## Security
340
+
341
+ See [SECURITY.md](SECURITY.md) for vulnerability reporting policy.