baseline-rb 0.8.0 → 0.9.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c6f0dd98a7dd126671fc5c691a33852573b39a31e96dd45ca0b334db4c43c6f1
4
- data.tar.gz: 3a3b85fbb2c40ca83af96cd6ca7dd26878f8190529f494e8398eb8fbe9f58b1b
3
+ metadata.gz: b5143fc59a995670ab9a7f35a7fb22353de3a471c1646c0790bfcdabe2405246
4
+ data.tar.gz: 1ce0e2e703d80a3cba8fff9cb84c441cfa41ef7dad5e47c87303dc4c7eba6849
5
5
  SHA512:
6
- metadata.gz: 47ed7629d6207f6a9ecb58dae724b45431cda6bc2791bb55794fd0f751e2418e26dda9925b18c47f122953c43bb351169fc96651535f511b20cf09aaa3d965a4
7
- data.tar.gz: ab562f184b06990e622dd0ce39dd8af2869980192124f90fcd7cbecbbdff47411e46674f6217848893ebb9e8ab22e87cbbb09511642a0ec97a439ab4dded17c6
6
+ metadata.gz: 56fc448769e9cdd00bd6ad3acfed63e5e00406165706a38c4c33049e041de68020b28994aa6f93afe872dae164d947d3999e5b9e62fce550f07b327719faae94
7
+ data.tar.gz: b7ba5f71cb034fec86a76d8081591ee753e503e6c992c5afb3fb818880cb98ed63918fabef5ca744ff8b52734510d0fda4cdf8238a21c802994a5b804decc14e
data/CHANGELOG.md CHANGED
@@ -2,6 +2,14 @@
2
2
 
3
3
  Changes to the public contract are recorded here. Versions follow Semantic Versioning.
4
4
 
5
+ ## 0.9.0 (2026-10-20)
6
+
7
+ - Generate explicitly approved NX-OS default-login, command/config-command authorization and accounting corrections with native ordered groups and separate local error fallback; preserve console, shared groups and existing materials.
8
+ - Require resolved native role evidence, peer/consumer/recovery approval and existing endpoint keys before activating remote methods. Bind rendering to complete operation fingerprints and withhold coupled authorization changes if rendering fails.
9
+
10
+ - Audit NX-OS management authentication with native global method lists, independent local error fallback and separate command/config-command authorization; retain sanitized typed evidence instead of applying IOS line-login syntax.
11
+ - Respect native SSH defaults and explicit feature state, reject malformed replacements, and prevent disabled TACACS from certifying remote AAA usage. Telnet defaults remain unknown without product-specific or explicit evidence.
12
+
5
13
  ## 0.8.0 (2026-10-20)
6
14
 
7
15
  - Generate explicitly approved Huawei S-series VTY AAA-mode corrections with native UI/profile proofs while preserving shared schemes, templates, methods and ACLs.
data/README-zh.md CHANGED
@@ -209,6 +209,8 @@ MIT,见 [LICENSE](LICENSE)。
209
209
 
210
210
  Comware/VRP TACACS 密钥按替换、撤销及方案重新进入后的最终状态归纳。`check.details[:key_slots]` 分别列出 H3C 认证/授权/计费及 Huawei 共享材料,关联已知端点并提示对端、AAA 引用和恢复前提。损坏状态需补证,检查仍受阻,不生成秘密命令。当前模型描述方案密钥,不代表已支持端点私有密钥继承。参见[TACACS 材料示例](examples/remediation_tacacs.rb)。
211
211
 
212
+ NX-OS 管理 AAA 按原生全局登录方法、独立本地错误后备及命令/配置命令授权审计,非秘密 `aaa.nxos` 证据保留这些区别。禁用 TACACS 或损坏的最后声明不能证明远程使用;完整输入采用已确认的 SSH 默认,未明确的产品 Telnet 默认继续未知。明确的 `nxos` 批准可引用现有已核验组与密钥修正默认登录、两类授权及计费;登录本地错误后备需单独批准,控制台及共享对象保持原样。参见[离线示例](examples/remediation_nxos_aaa.rb)。参见[指标契约](docs/baseline-metrics.md)。
213
+
212
214
  Comware/VRP `aaa.bindings` 描述选中管理域的有序方法、TACACS 职责/组/端点引用、来源行及登录 VTY;本地后备与 `none` 分开记录,均不代表恢复访问已经可用。TACACS 使用检查复用这些证据,拒绝旧值和损坏引用。人工方案列出具体链路证据与批准前提,不自动挑选候选组;明确批准的 Comware 7 管理域 login 方法链可在完整核验后生成修正命令。`aaa.bindings` 保留选中域视角,`aaa.domain_bindings` 扩展覆盖已建模显式域的共享 TACACS 引用;实际用户路径、其他服务提供者及其他厂商绑定仍待分别补齐。参见[AAA 审核示例](examples/remediation_authentication.rb)。
213
215
 
214
216
  `tacacs.endpoints` 可声明 `bindings: [{kind: "aaa.domain", domain: "ADMIN", role: "authentication", position: "primary", selected: true}]`,在支持的 Comware/VRP 范围内核验完整域引用集合,包含未选中域的共享使用。position 是方法链优先级,与服务器主备不同。省略 bindings 只查定义;`[]` 需已知未绑定证据。局部/未建模引用保持未知,能力项区分对象与绑定支持。当前方案展示共享引用要求,不生成绑定切换命令。参见[绑定策略示例](examples/resource_bindings.rb)。
data/README.md CHANGED
@@ -199,6 +199,8 @@ Credential review plans identify unsafe-storage and observable weak-password obj
199
199
 
200
200
  Comware/VRP TACACS key requirements retain the final key state across replacement, withdrawal, and reopened schemes. `check.details[:key_slots]` distinguishes missing H3C authentication/authorization/accounting material from Huawei shared material, associates known endpoints, and lists peer, AAA-reference, and recovery prerequisites. Malformed key state requires evidence; plans stay blocked and contain no secret commands. This models scheme-level keys, not endpoint-private-key inheritance. See the [TACACS material example](examples/remediation_tacacs.rb).
201
201
 
202
+ NX-OS management AAA auditing uses native global login methods, independent local error fallback and both command/config-command authorization. Sanitized `aaa.nxos` evidence preserves these distinctions; disabled TACACS and malformed final declarations cannot certify remote use. SSH defaults are modelled for complete input, while unknown product-specific Telnet defaults remain unknown. Explicit `nxos` approvals can correct default login, both authorization actions and accounting using existing verified groups/keys; native login fallback is approved separately. Console and shared objects stay unchanged. See the [offline example](examples/remediation_nxos_aaa.rb). See the [metric contract](docs/baseline-metrics.md).
203
+
202
204
  Comware/VRP `aaa.bindings` describes the selected management domain's ordered methods, TACACS role/group/endpoint references, source lines, and login VTY evidence. Local fallback and `none` are separate declarations; neither proves working recovery access. TACACS usage checks share this evidence and reject stale/malformed references. Plans list concrete chain evidence and approval requirements without selecting a candidate group. Explicitly approved Comware 7 login-chain corrections can bind an existing selected domain to a verified role group with local fallback, or to an approved local alternative; endpoint changes remain separate prerequisites. Comware 7 VTY account-mode corrections also accept explicit complete range approval, preserve the existing approved method chain and effective protocols, and project requested SSH-only and TACACS-login constraints together; inherited protocol rebinding needs separate temporary-transition approval. Huawei S-series VTY corrections use `authentication-mode aaa` with an explicit `vrp_s_series` profile and complete `user-interface vty` scope, preserving the selected domain, shared schemes/templates and exact current methods, including `local-case` username matching. Both mode planners require resolved authentication, authorization and accounting roles plus external recovery verification. `aaa.bindings` retains the selected-domain view; `aaa.domain_bindings` inventories explicitly modelled domains and their shared TACACS references. Actual per-user paths, other providers and vendor bindings remain separate evidence gaps. See the [AAA review example](examples/remediation_authentication.rb).
203
205
 
204
206
  `tacacs.endpoints` objects may declare typed `bindings: [{kind: "aaa.domain", domain: "ADMIN", role: "authentication", position: "primary", selected: true}]`. On supported Comware/VRP profiles this compares the exact domain-reference set, including unselected shared uses; `position` is method-chain preference, separate from server priority. Omitted bindings check definitions only; `[]` needs known unbound evidence. Partial/unmodelled references stay unknown, and capability entries separate binding support from object support. Current plans expose shared-reference requirements without binding-change commands. See the [binding-policy example](examples/resource_bindings.rb).
@@ -330,3 +330,27 @@ Huawei 的 local 与 local-case 用户名匹配规则不同。`aaa.bindings` 和
330
330
  域、共享认证/授权/计费方案、HWTACACS 模板、密钥、用户、其他范围及 ACL 均保留;恢复仍需独立认证规划,Huawei 的保存/commit 流程仍须按目标版本确认。独立状态模型验证原生 UI 命令、最终指标、共享配置保留及二次零变更。现有真实备份不含 Huawei 样本,当前 Huawei 证据来自原厂语法、合成配置及安装包验证,不能称为真机或该备份集上的 Huawei 验收。
331
331
 
332
332
  依据:[Huawei VTY 认证模式命令](https://support.huawei.com/enterprise/en/doc/EDOC1100214493/677f04d0)、[Huawei local/local-case 语义](https://info.support.huawei.com/hedex/api/pages/EDOC1100210438/AEK0901G/09/resources/dc/local-user_ap-group_view.html)、[Huawei HWTACACS、本地认证与计费故障说明](https://support.huawei.com/enterprise/en/doc/EDOC1100411502/5561445f/example-for-configuring-hwtacacs-and-local-authentication-command-authorization-and-command-auditing-for-administrators)。
333
+
334
+
335
+ ## NX-OS 原生管理认证审计
336
+
337
+ NX-OS 使用全局 default/console 登录方法,不套用 IOS 的逐线命名登录列表。`aaa.nxos` 保留非秘密方法顺序、来源行、独立的 `fallback_on_error` 状态、commands/config-commands 两类授权、默认计费和 TACACS 功能状态。关闭本地错误后备不会清空服务器组;撤销方法列表回到本地默认,损坏的最后声明不能复用先前证据。组无法解析、未知方法或未建模 AAA 控制会保留不完整状态。该图是配置证据,不证明认证服务器可达或本地恢复可用。
338
+
339
+ `ssh.vty_aaa` 依据原生默认登录链判断账户认证,`none` 不会通过;无需虚构数字 VTY 范围。`tacacs.authorization` 同时检查默认命令与配置命令授权,缺失一类不会被另一类已配置的 TACACS 掩盖。禁用 TACACS 功能时,三项 TACACS 使用检查不能通过。授权链中的 `none` 保留为无数据库后备证据,不表示本地账户恢复。
340
+
341
+ 完整输入可采用已确认的 SSH 默认启用状态;Telnet 默认随 Nexus 产品不同,缺少明确声明时不推导 SSH-only。显式 feature/no feature 按最终声明归约,损坏声明保留未知。IOS 的 login/transport input 不能作为 NX-OS 原生登录证明。原生 AAA 修正使用下节独立批准,不复用 IOS 命名列表;NX-OS VTY 协议修正命令仍未实现。
342
+
343
+ 依据:[NX-OS 原生 AAA 与本地后备](https://www.cisco.com/c/en/us/td/docs/dcn/nx-os/nexus9000/105x/configuration/security/cisco-nexus-9000-series-nx-os-security-configuration-guide-release-105x/m-configuring-aaa.html)、[原生授权方法与 none](https://www.cisco.com/c/en/us/td/docs/switches/datacenter/nexus5500/sw/command/reference/security/n5500-sec-cr/n5500-sec_cmds_a.html)、[SSH 默认状态](https://www.cisco.com/c/en/us/td/docs/dcn/nx-os/nexus9000/104x/configuration/security/cisco-nexus-9000-series-nx-os-security-configuration-guide-release-104x/m-configuring-ssh-and-telnet.html)、[Nexus 3600 的 Telnet 默认差异](https://www.cisco.com/c/en/us/td/docs/dcn/nx-os/nexus3600/102x/configuration/security/cisco-nexus-3600-nx-os-security-configuration-guide-102x/m-configuring-ssh-and-telnet.pdf)。官方语法、合成测试与备份形态验证不等同于实机登录验收。
344
+
345
+
346
+ ## NX-OS 原生管理 AAA 修正
347
+
348
+ `tacacs.login`、`tacacs.authorization`、`tacacs.accounting` 及 `ssh.vty_aaa` 可使用 `remediation.syntax_profile: nxos`。作用域固定为当前配置上下文的全局 default,不能混用 management_domain 或数字 vty_ranges;console 登录与其后备控制保持原样。所有操作都需 `management_consumers_verified: true` 和 `local_fallback_access_verified: true`;这些是外部批准,不由备份或可用组自动填充。标量已满足时不强制迁移到另一组或修改后备。
349
+
350
+ `methods` 必须明确指定。默认登录接受 `[{kind: local}]` 或一个及以上有序 `[{kind: tacacs, group: OPS}]`。远程登录还需 `local_fallback_on_error: true`;原生 fallback error local 在激活新登录组之前设置,不能误拼为 group OPS local。SSH 账户认证为 true 时可将 none 改为批准的本地或远程方法;其他已启用 TACACS/SSH 规则同时约束最终方法,不能为同一登录链批准不同目标。
351
+
352
+ 授权的远程 methods 为有序 TACACS 组后接 `{kind: local}`,分别规划 commands 与 config-commands;两者只修改不匹配项,整项校验和渲染完成后才公开操作。计费接受有序 TACACS 组,不拼接未核验的 local 尾部。三项 TACACS 规则为 false 时只支持明确批准的单个 local 替代,不从 false 推导 none、删除服务器或开放无认证访问。本地方法切换保留独立 fallback 声明。
353
+
354
+ 远程目标必须已有启用的 TACACS 功能、可解析的组地址成员和同身份端点密钥存在性,并具备 `tacacs_peer_compatibility_verified: true`。特殊 group tacacs+ 验证当前全体服务器;命名组逐成员验证。共享组的 VRF/source、其他使用者和材料保持原样,外部对端核验须涵盖既有路径。端点新增、删除、材料配置及未解析的原生引用不能由此偷偷完成;同一策略要求的端点变更须先处理。操作携带完整指纹,其他厂商呈现器或不同操作不能借用批准。
355
+
356
+ 参见[可直接运行的合成示例](../examples/remediation_nxos_aaa.rb)及上节原厂语法依据。备份形态故障注入与独立命令状态模型验证的是离线配置闭环,不能替代目标固件接受、远程职责、本地恢复和共享消费者的人工实测;保存仍是独立人工提示。
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "baseline"
4
+
5
+ # 全部身份、材料及批准均为合成示例;只生成待人工审核命令,不访问设备。
6
+ configuration = "hostname LAB\nfeature tacacs+\nno feature telnet\n" \
7
+ "tacacs-server host 192.0.2.30 key 7 SYNTHETIC-ONLY\n" \
8
+ "aaa group server tacacs+ OPS\n server 192.0.2.30\n use-vrf management\n!\n" \
9
+ "aaa authentication login default none\nno aaa authentication login default fallback error local\n" \
10
+ "aaa authentication login console local\naaa authorization commands default local\n" \
11
+ "aaa authorization config-commands default local\naaa accounting default local\nline vty\n!\n"
12
+ checks = %w[tacacs.login tacacs.authorization tacacs.accounting ssh.vty_aaa].to_h do |id|
13
+ methods = [ { "kind" => "tacacs", "group" => "OPS" } ]
14
+ methods << { "kind" => "local" } if id == "tacacs.authorization"
15
+ options = { "syntax_profile" => "nxos", "methods" => methods,
16
+ "local_fallback_access_verified" => true, "management_consumers_verified" => true, "tacacs_peer_compatibility_verified" => true }
17
+ options["local_fallback_on_error"] = true if %w[tacacs.login ssh.vty_aaa].include?(id)
18
+ [ id, { "expected" => true, "remediation" => options } ]
19
+ end
20
+ policy = Baseline::Policy.new("version" => 1, "checks" => checks)
21
+ plan = Baseline::Remediation.new(policy: policy).call(configuration: configuration, platform: :cisco_nxos, complete: true)
22
+ raise "Expected native management-role corrections" unless plan.status == :ready && plan.operations.size == 4
23
+ raise "Unexpected secret output" if plan.to_h.to_s.include?("SYNTHETIC-ONLY")
24
+ puts plan.to_text
@@ -54,13 +54,13 @@ module Baseline
54
54
  version = effective_configured_value(/\Aip ssh version\s+([12])\z/,
55
55
  family: /\A(?:no )?ip ssh version\b/, fact: "ssh.versions", reset: /\Ano ip ssh version(?: [12])?\z/)
56
56
  record_fact("ssh.versions", [ version ]) if version
57
- ssh = nxos? ? configured_state(/\Afeature ssh\z/, /\Ano feature ssh\z/) : version ? true : nil
57
+ ssh = nxos? ? nxos_feature_state("ssh", default: true) : version ? true : nil
58
58
  record_fact("ssh.enabled", ssh, matching_blocks(/\A(?:ip ssh|(?:no )?feature ssh)/))
59
59
  default_aaa = nxos? || configured_state(/\Aaaa new-model\z/, /\Ano aaa new-model\z/) == true
60
60
  vtys = matching_blocks(/\Aline vty(?:\s|\z)/).map do |section|
61
61
  login = configured_vty_login(section, default_aaa: default_aaa)
62
62
  acl_entries = section.children.reject { |entry| /\A(?:no )?access-class\s+\S+\s+out(?:\s|\z)/.match?(entry.text) }
63
- { name: section.text, protocols: effective_configured_value(/\Atransport input\s+((?:all|none|ssh|telnet)(?:\s+(?:ssh|telnet))*)\z/, entries: section.children,
63
+ { name: section.text, protocols: nxos? ? nxos_remote_protocols : effective_configured_value(/\Atransport input\s+((?:all|none|ssh|telnet)(?:\s+(?:ssh|telnet))*)\z/, entries: section.children,
64
64
  family: /\A(?:no )?transport input\b/, fact: "ssh.vty_ssh_only", reset: /\Ano transport input(?:\s+\S+)*\z/)&.split,
65
65
  authentication: login[:authentication], authentication_list: login[:authentication_list],
66
66
  acl_vrf_also: acl_entries.reverse.find { |entry| /\A(?:no )?access-class\b/.match?(entry.text) }&.text&.end_with?(" vrf-also") == true,
@@ -77,7 +77,7 @@ module Baseline
77
77
  ssh_port: rotary.empty? ? listener_port("ssh port", default: 22, scope: "management.acl", negate: "no") : nil,
78
78
  telnet_port: telnet_ports.empty? ? 23 : nil)
79
79
  telnet = if nxos?
80
- configured_state(/\Afeature telnet\z/, /\Ano feature telnet\z/)
80
+ nxos_feature_state("telnet")
81
81
  elsif vtys.any? && vtys.all? { |entry| entry[:protocols] }
82
82
  vtys.any? { |entry| (entry[:protocols] & %w[all telnet]).any? }
83
83
  end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../nxos_aaa_methods"
4
+
5
+ module Baseline
6
+ module CiscoNxos
7
+ # 原生默认方法、独立后备与命令/配置命令授权分别归约,不套用 IOS 的逐线登录列表。
8
+ module Authentication
9
+ private
10
+
11
+ def prepare_configuration
12
+ super
13
+ @nxos_aaa_graph = nil
14
+ @nxos_features = {}
15
+ end
16
+
17
+ def nxos_feature_state(name, default: nil)
18
+ key = [ name, default ]
19
+ return @nxos_features[key] if @nxos_features.key?(key)
20
+ entry = matching_blocks(/\A(?:no )?feature #{Regexp.escape(name)}(?:\s|\z)/).last
21
+ value = if entry
22
+ case entry.text
23
+ when "feature #{name}" then analyze(entry) { true }
24
+ when "no feature #{name}" then analyze(entry) { false }
25
+ else
26
+ fact = { "ssh" => "ssh.enabled", "telnet" => "services.telnet", "tacacs+" => "tacacs" }.fetch(name)
27
+ record_unknown_fact(fact, [ entry ], code: :unsupported_authentication_method)
28
+ nil
29
+ end
30
+ elsif @fact_builder.complete?
31
+ default
32
+ end
33
+ @nxos_features[key] = value
34
+ end
35
+
36
+ # SSH 缺省稳定;Telnet 缺省跨 Nexus 产品不同,缺显式声明时继续未知。
37
+ def nxos_remote_protocols
38
+ ssh, telnet = nxos_feature_state("ssh", default: true), nxos_feature_state("telnet")
39
+ return if ssh.nil? || telnet.nil?
40
+ values = []
41
+ values << "ssh" if ssh
42
+ values << "telnet" if telnet
43
+ values.empty? ? [ "none" ] : values
44
+ end
45
+
46
+ def configured_vty_login(section, default_aaa:)
47
+ foreign = matching_blocks(/\A(?:no )?(?:login|transport input)(?:\s|\z)/, section.children)
48
+ if foreign.any?
49
+ record_unknown_fact("ssh.vty_aaa", foreign, code: :unsupported_authentication_method)
50
+ record_unknown_fact("tacacs.login", foreign, code: :unsupported_authentication_method)
51
+ end
52
+ state = nxos_method_state("aaa authentication login default", "authentication")
53
+ account = state["complete"] && state["methods"].none? { |method| method["kind"] == "none" }
54
+ { authentication: foreign.any? || !state["complete"] ? nil : account ? "aaa" : "none", authentication_list: "default" }
55
+ end
56
+
57
+ # 后备行不参与主列表覆盖;撤销独立控制也不能清除现有服务器组。
58
+ def nxos_method_state(prefix, role)
59
+ state = { "methods" => [ { "kind" => "local" } ], "declared" => false, "complete" => @fact_builder.complete?, "lines" => [] }
60
+ matching_blocks(/\A(?:no )?#{Regexp.escape(prefix)}(?:\s|\z)/).each do |entry|
61
+ text = entry.text.delete_prefix("no ")
62
+ tail = text.delete_prefix(prefix).strip
63
+ next if role == "authentication" && (tail == "fallback" || tail.start_with?("fallback "))
64
+ methods = NxosAaaMethods.parse(tail, role: role)
65
+ negative = entry.text.start_with?("no ")
66
+ if methods || negative && tail.empty?
67
+ analyze(entry) { true }
68
+ state = { "methods" => negative ? [ { "kind" => "local" } ] : methods, "declared" => !negative, "complete" => true, "lines" => [ entry.line ] }
69
+ else
70
+ state = { "methods" => [], "declared" => true, "complete" => false, "lines" => [ entry.line ] }
71
+ end
72
+ end
73
+ state
74
+ end
75
+
76
+ def nxos_login_state(selector)
77
+ node = nxos_method_state("aaa authentication login #{selector}", "authentication")
78
+ fallback, lines = @fact_builder.complete? ? true : nil, []
79
+ matching_blocks(/\A(?:no )?aaa authentication login #{selector} fallback(?:\s|\z)/).each do |entry|
80
+ fallback = case entry.text
81
+ when "aaa authentication login #{selector} fallback error local" then analyze(entry) { true }
82
+ when "no aaa authentication login #{selector} fallback error local" then analyze(entry) { false }
83
+ end
84
+ lines = [ entry.line ]
85
+ end
86
+ node.merge("fallback_on_error" => fallback, "fallback_lines" => lines, "complete" => node["complete"] && !fallback.nil?)
87
+ end
88
+
89
+ def nxos_resolve_methods(node, servers, inventory)
90
+ complete = node["complete"]
91
+ methods = node["methods"].map do |method|
92
+ next method unless method["kind"] == "server_group"
93
+ name = method["group"]
94
+ known = name == "tacacs+" ? servers.any? && !@fact_builder.unresolved?("tacacs.servers") :
95
+ inventory.closed && inventory.groups.fetch(name, []).any? && inventory.references.fetch(name, []).all? { |reference| reference[:complete] }
96
+ if known
97
+ { "kind" => "tacacs", "group" => name }
98
+ elsif name == "radius"
99
+ { "kind" => "radius" }
100
+ else
101
+ complete = false
102
+ { "kind" => "unresolved" }
103
+ end
104
+ end
105
+ node.merge("methods" => methods, "complete" => complete)
106
+ end
107
+
108
+ def record_tacacs_login_usage(servers, inventory)
109
+ authentication = %w[default console].to_h { |selector| [ selector, nxos_resolve_methods(nxos_login_state(selector), servers, inventory) ] }
110
+ authorization = %w[commands config-commands].to_h do |action|
111
+ [ action, nxos_resolve_methods(nxos_method_state("aaa authorization #{action} default", "authorization"), servers, inventory) ]
112
+ end
113
+ accounting = nxos_resolve_methods(nxos_method_state("aaa accounting default", "accounting"), servers, inventory)
114
+ unsupported = matching_blocks(/\A(?:no )?aaa (?:authentication login|authorization|accounting)(?:\s|\z)/).reject do |entry|
115
+ /\A(?:no )?aaa (?:authentication login (?:default|console|ascii-authentication|error-enable|invalid-username-log|(?:mschap|mschapv2|chap) enable)(?:\s|\z)|authorization (?:commands|config-commands) default(?:\s|\z)|accounting (?:default|dot1x)(?:\s|\z))/.match?(entry.text)
116
+ end
117
+ foreign = matching_blocks(/\Aline vty(?:\s|\z)/).flat_map do |section|
118
+ matching_blocks(/\A(?:no )?(?:login|transport input)(?:\s|\z)/, section.children)
119
+ end
120
+ scope_complete = unsupported.empty? && foreign.empty?
121
+ @nxos_aaa_graph = CtxParser::Immutable.copy({ "authentication" => authentication, "authorization" => authorization,
122
+ "accounting" => { "default" => accounting }, "scope_complete" => scope_complete, "tacacs_enabled" => nxos_feature_state("tacacs+", default: false) })
123
+ record_fact("aaa.nxos", @nxos_aaa_graph)
124
+ record_unknown_fact("aaa.nxos", unsupported + foreign, code: :unsupported_authentication_method) unless scope_complete
125
+ node = authentication.fetch("default")
126
+ nxos_record_usage("tacacs.login", [ node ], scope_complete: scope_complete)
127
+ if node["complete"]
128
+ record_fact("ssh.vty_aaa", node["methods"].none? { |method| method["kind"] == "none" })
129
+ else
130
+ record_unknown_fact("ssh.vty_aaa", code: :authentication_list_unresolved)
131
+ end
132
+ if (protocols = nxos_remote_protocols)
133
+ record_fact("ssh.vty_ssh_only", protocols == [ "ssh" ])
134
+ end
135
+ end
136
+
137
+ def record_tacacs_action_usage(kind, _servers, _inventory)
138
+ nodes = @nxos_aaa_graph.fetch(kind).values
139
+ nxos_record_usage("tacacs.#{kind}", nodes, scope_complete: @nxos_aaa_graph["scope_complete"])
140
+ end
141
+
142
+ def nxos_record_usage(fact, nodes, scope_complete:)
143
+ feature = @nxos_aaa_graph["tacacs_enabled"]
144
+ if !scope_complete || nodes.empty? || nodes.any? { |node| !node["complete"] } || feature.nil?
145
+ record_unknown_fact(fact, code: :tacacs_group_unresolved)
146
+ else
147
+ used = feature && nodes.all? { |node| node["methods"].first&.fetch("kind") == "tacacs" }
148
+ lines = nodes.flat_map { |node| node["lines"] }
149
+ entries = roots.select { |entry| lines.include?(entry.line) }
150
+ record_fact(fact, used, entries)
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../cisco/command_renderer"
4
+ require_relative "../nxos_aaa_schema"
4
5
 
5
6
  module Baseline
6
7
  module CiscoNxos
@@ -14,6 +15,30 @@ module Baseline
14
15
  NTP_STYLE = :nxos
15
16
  SSH_DISABLE_COMMAND = "no feature ssh"
16
17
 
18
+ def supports?(id) = NxosAaaSchema::IDS.key?(id) || super
19
+
20
+ def render(operation, verified_aaa_operation_ids: [], **options)
21
+ return super unless operation.kind == NxosAaaSchema::KIND
22
+ unless verified_aaa_operation_ids.is_a?(Array) && verified_aaa_operation_ids.all? { |id| id.is_a?(String) && /\A[0-9a-f]{64}\z/.match?(id) } && verified_aaa_operation_ids.include?(operation.id)
23
+ raise UnsupportedOperation, "management_method_preconditions_unverified"
24
+ end
25
+ NxosAaaSchema.validate!(identity: operation.identity, scope: operation.scope, before: operation.before, after: operation.after, action: operation.action, phase: operation.phase)
26
+ role = operation.identity.fetch("role")
27
+ methods = operation.after.fetch("methods")
28
+ native = methods == [ { "kind" => "local" } ] ? "local" : "group #{methods.select { |method| method['kind'] == 'tacacs' }.map { |method| method.fetch('group') }.join(' ')}#{role == 'authorization' ? ' local' : ''}"
29
+ prefix = case role
30
+ when "authentication" then "aaa authentication login default"
31
+ when "authorization" then "aaa authorization #{operation.identity.fetch('function')} default"
32
+ when "accounting" then "aaa accounting default"
33
+ end
34
+ commands = []
35
+ if role == "authentication" && operation.before["fallback_on_error"] != operation.after["fallback_on_error"]
36
+ commands << "aaa authentication login default fallback error local"
37
+ end
38
+ commands << "#{prefix} #{native}" unless operation.before["methods"] == methods
39
+ commands.map { |command| CommandStep.new(view: [], command: command, phase: operation.phase, operation_id: operation.id) }
40
+ end
41
+
17
42
  private
18
43
 
19
44
  # NX-OS 地址族由地址本身决定,路由域使用 use-vrf。
@@ -1,11 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative "../cisco/parser"
4
+ require_relative "authentication"
4
5
 
5
6
  module Baseline
6
7
  module CiscoNxos
7
8
  # 解析 NX-OS 功能开关、角色权限及命名扩展 ACL。
8
9
  class Parser < Cisco::Parser
10
+ include Authentication
9
11
  private
10
12
 
11
13
  # 选择共享 Cisco 解析逻辑中的 NX-OS 分支。
@@ -32,6 +32,8 @@ module Baseline
32
32
  TacacsOperationSchema.validate!(kind: kind, identity: identity, scope: scope, before: before, after: after, action: action, phase: phase)
33
33
  elsif kind == VtyAuthenticationSchema::KIND
34
34
  VtyAuthenticationSchema.validate!(identity: identity, scope: scope, before: before, after: after, action: action, phase: phase)
35
+ elsif kind == NxosAaaSchema::KIND
36
+ NxosAaaSchema.validate!(identity: identity, scope: scope, before: before, after: after, action: action, phase: phase)
35
37
  elsif kind == AaaChainSchema::KIND
36
38
  AaaChainSchema.validate!(identity: identity, scope: scope, before: before, after: after, action: action, phase: phase)
37
39
  elsif kind == "syslog.reference"
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "resource_schema"
4
+
5
+ module Baseline
6
+ # NX-OS 的 group-list 是单个有序组列表,本地错误后备不是认证列表尾部方法。
7
+ module NxosAaaMethods
8
+ def self.group?(name) = name == "tacacs+" || ResourceSchema.token?(name) && !%w[group local none fallback error default console].include?(name)
9
+
10
+ def self.parse(text, role:)
11
+ return [ { "kind" => "local" } ] if text == "local"
12
+ return [ { "kind" => "none" } ] if text == "none" && %w[authentication authorization].include?(role)
13
+ tokens = text.split
14
+ return unless tokens.shift == "group"
15
+ terminal = tokens.pop if role == "authorization" && %w[local none].include?(tokens.last) || role == "authentication" && tokens.last == "none"
16
+ return unless !tokens.empty? && tokens.join(" ").bytesize <= 127 && tokens.uniq == tokens && tokens.all? { |name| group?(name) }
17
+ methods = tokens.map { |name| { "kind" => "server_group", "group" => name } }
18
+ methods << { "kind" => terminal } if terminal
19
+ methods
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,104 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "configuration_operation"
4
+ require_relative "nxos_aaa_schema"
5
+ require_relative "tacacs_remediation"
6
+
7
+ module Baseline
8
+ # 只激活现有已核验的原生方法引用,不改变共享服务器对象、控制台或既有材料。
9
+ class NxosAaaRemediation
10
+ def self.applicable?(finding, evidence)
11
+ evidence.platform == "cisco_nxos" && evidence.complete? && NxosAaaSchema::IDS.key?(finding.id) &&
12
+ !finding.expected.nil? && %i[fail unknown].include?(finding.status)
13
+ end
14
+
15
+ def initialize(policy:) = @policy = policy
16
+
17
+ def call(finding, audit)
18
+ options = @policy.rules.find { |rule| rule.id == finding.id }.remediation
19
+ require!(options["syntax_profile"] == "nxos", "native_management_method_syntax_profile")
20
+ %w[methods local_fallback_access_verified management_consumers_verified].each do |key|
21
+ require!(options[key] && (!key.end_with?("verified") || options[key] == true), key)
22
+ end
23
+ evidence = audit.configuration
24
+ require!(evidence.complete? && evidence.platform == "cisco_nxos", "complete_native_configuration")
25
+ graph = evidence.fact("aaa.nxos")
26
+ require!(evidence.fact_known?("aaa.nxos") && graph["scope_complete"], "resolved_native_management_method_scope")
27
+ nodes = [ graph["authentication"]["default"], *graph["authorization"].values, graph["accounting"]["default"] ]
28
+ require!(nodes.all? { |node| node["complete"] }, "resolved_management_login_roles")
29
+ role = NxosAaaSchema::IDS.fetch(finding.id)
30
+ methods = options.fetch("methods")
31
+ remote = methods.first["kind"] == "tacacs"
32
+ conflict! if finding.id == "ssh.vty_aaa" ? finding.expected != true : remote != finding.expected
33
+ verify_authentication_policy!(audit, methods) if role == "authentication"
34
+ stable = audit.findings.select { |item| TacacsRemediation::IDS.include?(item.id) && item.status != :not_applicable && !item.expected.nil? }
35
+ require!(stable.all? { |item| item.status == :pass }, "stable_approved_tacacs_endpoints")
36
+ if remote
37
+ require!(graph["tacacs_enabled"] == true, "existing_enabled_tacacs_feature")
38
+ require!(options["tacacs_peer_compatibility_verified"] == true, "tacacs_peer_compatibility_verified")
39
+ require!(options["local_fallback_on_error"] == true, "local_fallback_on_error") if role == "authentication"
40
+ verify_targets!(evidence, methods)
41
+ end
42
+ functions = role == "authorization" ? %w[commands config-commands] : [ "default" ]
43
+ functions.filter_map do |function|
44
+ node = graph.fetch(role).fetch(function)
45
+ before = { "methods" => node.fetch("methods") }
46
+ after = { "methods" => methods }
47
+ if role == "authentication"
48
+ before["fallback_on_error"] = node.fetch("fallback_on_error")
49
+ after["fallback_on_error"] = remote ? true : before["fallback_on_error"]
50
+ end
51
+ next if before == after
52
+ identity = { "role" => role }
53
+ identity["function"] = function if role == "authorization"
54
+ ConfigurationOperation.new(kind: NxosAaaSchema::KIND, identity: identity, scope: { "selector" => "default", "syntax_profile" => "nxos" },
55
+ before: before, after: after, action: :replace, phase: :activate, risk: finding.severity == "critical" ? "critical" : "high")
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def require!(condition, requirement)
62
+ raise TargetRemediation::Blocked.new(:authentication_chain_evidence_required, requirements: [ requirement ]) unless condition
63
+ end
64
+
65
+ def conflict! = raise(TargetRemediation::Blocked, :policy_conflict)
66
+
67
+ # 同一 default 登录链不得接受两个不同目标,已有通过的标量规则仍约束投影结果。
68
+ def verify_authentication_policy!(audit, methods)
69
+ related = audit.findings.select { |item| %w[tacacs.login ssh.vty_aaa].include?(item.id) && item.status != :not_applicable && !item.expected.nil? }
70
+ related.each do |item|
71
+ conflict! if item.id == "ssh.vty_aaa" ? item.expected != true : item.expected != (methods.first["kind"] == "tacacs")
72
+ rule = @policy.rules.find { |candidate| candidate.id == item.id }
73
+ if rule.remediation["syntax_profile"] == "nxos"
74
+ flags = %w[management_consumers_verified local_fallback_access_verified]
75
+ flags += %w[tacacs_peer_compatibility_verified local_fallback_on_error] if methods.first["kind"] == "tacacs"
76
+ conflict! if flags.any? { |key| rule.remediation[key] == false }
77
+ end
78
+ if item.status != :pass && rule.remediation["syntax_profile"] == "nxos" && rule.remediation.key?("methods")
79
+ conflict! unless rule.remediation["methods"] == methods
80
+ end
81
+ end
82
+ end
83
+
84
+ # 现有地址成员逐个关联相同身份的材料;原生组的 VRF/source 等保持原样,由外部对端核验覆盖运行路径。
85
+ def verify_targets!(evidence, methods)
86
+ require!(%w[tacacs.servers tacacs.groups tacacs.group_references tacacs.endpoints].all? { |fact| evidence.fact_known?(fact) }, "resolved_tacacs_reference_inventory")
87
+ methods.select { |method| method["kind"] == "tacacs" }.each do |method|
88
+ group = method.fetch("group")
89
+ refs = evidence.fact("tacacs.group_references")[group]
90
+ addresses = if group == "tacacs+"
91
+ evidence.fact("tacacs.servers")
92
+ else
93
+ require!(refs && refs.any? && refs.all? { |ref| ref[:kind] == "address" && ref[:complete] }, "resolved_native_tacacs_group_members")
94
+ refs.flat_map { |ref| ref[:servers] }.uniq
95
+ end
96
+ require!(addresses.any?, "resolved_tacacs_role_endpoints")
97
+ addresses.each do |address|
98
+ targets = evidence.fact("tacacs.endpoints").select { |target| target["identity"] == { "address" => address } }
99
+ require!(targets.any? && targets.all? { |target| target["attributes"]["key_present"] == true }, "existing_tacacs_endpoint_key")
100
+ end
101
+ end
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "aaa_chain_schema"
4
+ require_relative "nxos_aaa_methods"
5
+
6
+ module Baseline
7
+ # 原生全局 default 范围与职责语法独立于 Comware 域和 IOS 命名登录列表。
8
+ module NxosAaaSchema
9
+ KIND = "aaa.nxos_chain"
10
+ IDS = AaaChainSchema::IDS.merge("ssh.vty_aaa" => "authentication").freeze
11
+ APPROVALS = %w[syntax_profile methods local_fallback_on_error local_fallback_access_verified management_consumers_verified tacacs_peer_compatibility_verified].freeze
12
+
13
+ def self.methods?(methods, role:, approved:)
14
+ return false unless methods.is_a?(Array) && methods.any? && methods.all? { |method| method.is_a?(Hash) }
15
+ return true if methods == [ { "kind" => "local" } ]
16
+ return !approved && role != "accounting" if methods == [ { "kind" => "none" } ]
17
+ terminal = methods.last if %w[local none].include?(methods.last&.fetch("kind", nil))
18
+ groups = terminal ? methods[0...-1] : methods
19
+ valid = groups.any? && groups.all? do |method|
20
+ method.is_a?(Hash) && (method.keys.sort == %w[group kind] && method["kind"] == "tacacs" && NxosAaaMethods.group?(method["group"]) ||
21
+ !approved && method == { "kind" => "radius" })
22
+ end
23
+ return false unless valid && groups.uniq == groups && groups.map { |method| method["group"] || "radius" }.join(" ").bytesize <= 127
24
+ return terminal.nil? if role == "accounting"
25
+ return !terminal || !approved && terminal == { "kind" => "none" } if role == "authentication"
26
+
27
+ approved ? terminal == { "kind" => "local" } : !terminal || [ { "kind" => "local" }, { "kind" => "none" } ].include?(terminal)
28
+ end
29
+
30
+ def self.validate_approval!(value, id:)
31
+ role = IDS.fetch(id)
32
+ valid = (value.keys - APPROVALS).empty? && (!value.key?("methods") || methods?(value["methods"], role: role, approved: true)) &&
33
+ (!value.key?("local_fallback_on_error") || role == "authentication" && value["methods"] != [ { "kind" => "local" } ] && [ true, false ].include?(value["local_fallback_on_error"]))
34
+ raise ArgumentError, "Invalid native NX-OS method approval" unless valid
35
+ end
36
+
37
+ def self.validate!(identity:, scope:, before:, after:, action:, phase:)
38
+ role = identity.is_a?(Hash) && identity["role"]
39
+ fields = role == "authentication" ? %w[fallback_on_error methods] : [ "methods" ]
40
+ identity_valid = role == "authorization" ? identity.keys.sort == %w[function role] && %w[commands config-commands].include?(identity["function"]) :
41
+ %w[authentication accounting].include?(role) && identity.keys == [ "role" ]
42
+ valid = identity_valid && scope == { "selector" => "default", "syntax_profile" => "nxos" } && action == :replace && phase == :activate &&
43
+ before.is_a?(Hash) && before.keys.sort == fields && methods?(before["methods"], role: role, approved: false) &&
44
+ after.is_a?(Hash) && after.keys.sort == fields && methods?(after["methods"], role: role, approved: true)
45
+ if valid && role == "authentication"
46
+ valid = [ true, false ].include?(before["fallback_on_error"]) &&
47
+ (after["methods"] == [ { "kind" => "local" } ] ? after["fallback_on_error"] == before["fallback_on_error"] : after["fallback_on_error"] == true)
48
+ end
49
+ raise ArgumentError, "Invalid native NX-OS method operation" unless valid
50
+ end
51
+ end
52
+ end
@@ -24,7 +24,12 @@ module Baseline
24
24
  CtxParser::Immutable.copy(Audit.capabilities(platform: name).to_h do |id, capability|
25
25
  supported = capability[:status] != :unsupported && renderer.supports?(id)
26
26
  requirements = RemediationPlanner::REQUIREMENTS.fetch(id, [ "supported_rule_handler" ])
27
- if supported && id == "ssh.vty_aaa"
27
+ if supported && name == "cisco_nxos" && NxosAaaSchema::IDS.key?(id)
28
+ requirements = %w[native_management_method_syntax_profile approved_ordered_native_methods resolved_native_management_method_scope
29
+ resolved_management_login_roles local_fallback_access_verified management_consumers_verified stable_approved_tacacs_endpoints
30
+ existing_enabled_tacacs_feature_for_remote_methods existing_tacacs_endpoint_keys_for_remote_methods tacacs_peer_compatibility_verified_for_remote_methods]
31
+ requirements << "local_fallback_on_error_for_remote_authentication" if NxosAaaSchema::IDS[id] == "authentication"
32
+ elsif supported && id == "ssh.vty_aaa"
28
33
  requirements = AaaChainSchema::APPROVALS + %w[vty_ranges complete_matching_vty_scope existing_approved_management_method_chain
29
34
  exact_existing_methods_including_local_case resolved_management_login_roles known_vty_authentication_and_protocols]
30
35
  requirements += name == "h3c" ? [ "temporary_vty_protocol_rebind_approved_for_inherited_protocol_changes" ] : [ "existing_shared_tacacs_key_for_remote_roles" ]
@@ -14,6 +14,7 @@ require_relative "credential_remediation"
14
14
  require_relative "tacacs_key_remediation"
15
15
  require_relative "authentication_remediation"
16
16
  require_relative "aaa_chain_remediation"
17
+ require_relative "nxos_aaa_remediation"
17
18
  require_relative "vty_authentication_remediation"
18
19
  require_relative "resource_binding_remediation"
19
20
  require_relative "tacacs_remediation"
@@ -90,6 +91,8 @@ module Baseline
90
91
  elsif TacacsKeyRemediation.applicable?(finding, audit.configuration)
91
92
  result = TacacsKeyRemediation.new.call(finding, audit.configuration)
92
93
  check(finding, :blocked, result.fetch(:reason), requirements: result.fetch(:requirements), details: result.fetch(:details))
94
+ elsif NxosAaaRemediation.applicable?(finding, audit.configuration)
95
+ compile_aaa_chain(finding, audit, operations, steps)
93
96
  elsif AuthenticationRemediation.applicable?(finding, audit.configuration)
94
97
  if AaaChainSchema::IDS.key?(finding.id) && @policy.rules.find { |rule| rule.id == finding.id }.remediation.any?
95
98
  next compile_aaa_chain(finding, audit, operations, steps)
@@ -148,11 +151,13 @@ module Baseline
148
151
 
149
152
  # 认证链在完整核验后才公开单个原生修改,原操作指纹不能为不同目标背书。
150
153
  def compile_aaa_chain(finding, audit, operations, steps)
151
- operation = AaaChainRemediation.new(policy: @policy).call(finding, audit)
152
- rendered = @renderer.render(operation, verified_aaa_operation_ids: [ operation.id ])
153
- operations << operation
154
+ handler = audit.configuration.platform == "cisco_nxos" ? NxosAaaRemediation : AaaChainRemediation
155
+ proposed = Array(handler.new(policy: @policy).call(finding, audit))
156
+ ids = proposed.map(&:id)
157
+ rendered = proposed.flat_map { |operation| @renderer.render(operation, verified_aaa_operation_ids: ids) }
158
+ operations.concat(proposed)
154
159
  steps.concat(rendered)
155
- check(finding, :planned, nil, operation_ids: [ operation.id ], details: { verification_scope: :configured_inventory_and_external_management_approval })
160
+ check(finding, :planned, nil, operation_ids: ids, details: { verification_scope: :configured_inventory_and_external_management_approval })
156
161
  rescue TargetRemediation::Blocked => error
157
162
  check(finding, :blocked, error.reason, requirements: error.requirements)
158
163
  rescue CommandRenderer::UnsupportedOperation
data/lib/baseline/rule.rb CHANGED
@@ -6,6 +6,7 @@ require_relative "resource_schema"
6
6
  require_relative "syslog_reference_schema"
7
7
  require_relative "snmp_group_migration_schema"
8
8
  require_relative "aaa_chain_schema"
9
+ require_relative "nxos_aaa_schema"
9
10
  require_relative "vty_authentication_schema"
10
11
 
11
12
  require "ctx_parser/immutable"
@@ -85,6 +86,7 @@ module Baseline
85
86
  end
86
87
  @managed_scopes = CtxParser::Immutable.copy(options["managed_scopes"])
87
88
  @remediation = CtxParser::Immutable.copy(options.fetch("remediation", {}))
89
+ native_aaa_keys = remediation.is_a?(Hash) && [ nil, "nxos" ].include?(remediation["syntax_profile"]) ? NxosAaaSchema::APPROVALS : []
88
90
  remediation_keys = if id == "snmp.versions"
89
91
  %w[existing_v3_access_verified]
90
92
  elsif id == "snmp.write_enabled"
@@ -102,9 +104,9 @@ module Baseline
102
104
  elsif %w[syslog.minimum_servers syslog.servers syslog.destinations ntp.minimum_servers ntp.servers].include?(id)
103
105
  %w[maximum_targets order]
104
106
  elsif id == "ssh.vty_aaa"
105
- AaaChainSchema::APPROVALS + %w[vty_ranges temporary_vty_protocol_rebind_approved]
107
+ (AaaChainSchema::APPROVALS + native_aaa_keys + %w[vty_ranges temporary_vty_protocol_rebind_approved]).uniq
106
108
  elsif AaaChainSchema::IDS.key?(id)
107
- AaaChainSchema::APPROVALS
109
+ (AaaChainSchema::APPROVALS + native_aaa_keys).uniq
108
110
  elsif id == "tacacs.endpoints"
109
111
  %w[maximum_targets order syntax_profile local_fallback_access_verified tacacs_key_compatibility_verified]
110
112
  else []
@@ -120,7 +122,8 @@ module Baseline
120
122
  syntax_profiles = case id
121
123
  when "ntp.authentication" then %w[comware_legacy comware7 vrp_s_series]
122
124
  when "snmp.write_enabled" then SnmpGroupMigrationSchema::PROFILES
123
- when "ssh.vty_aaa" then VtyAuthenticationSchema::PROFILES.keys
125
+ when "ssh.vty_aaa" then VtyAuthenticationSchema::PROFILES.keys + [ "nxos" ]
126
+ when *AaaChainSchema::IDS.keys then %w[comware7 nxos]
124
127
  else [ "comware7" ]
125
128
  end
126
129
  unless (!remediation.key?("ntp_peer_key_compatibility_verified") || [ true, false ].include?(remediation["ntp_peer_key_compatibility_verified"])) &&
@@ -131,7 +134,11 @@ module Baseline
131
134
  AccessApproval.validate!(remediation["access_targets"], domain: id.start_with?("snmp.") ? :snmp : :management) if remediation.key?("access_targets")
132
135
  SyslogReferenceSchema.validate_migrations!(remediation["binding_migrations"]) if remediation.key?("binding_migrations")
133
136
  SnmpGroupMigrationSchema.validate_approvals!(remediation["group_migrations"]) if remediation.key?("group_migrations")
134
- AaaChainSchema.validate_approval!(remediation) if AaaChainSchema::IDS.key?(id) || id == "ssh.vty_aaa"
137
+ if remediation["syntax_profile"] == "nxos" && NxosAaaSchema::IDS.key?(id)
138
+ NxosAaaSchema.validate_approval!(remediation, id: id)
139
+ elsif AaaChainSchema::IDS.key?(id) || id == "ssh.vty_aaa"
140
+ AaaChainSchema.validate_approval!(remediation)
141
+ end
135
142
  if remediation.key?("vty_ranges") && !VtyAuthenticationSchema.ranges?(remediation["vty_ranges"], syntax_profile: remediation["syntax_profile"])
136
143
  raise ArgumentError, "Invalid approved VTY scope"
137
144
  end
@@ -2,5 +2,5 @@
2
2
 
3
3
  module Baseline
4
4
  # 公开包版本,供 RubyGems 元数据和运行时调用方共用。
5
- VERSION = "0.8.0"
5
+ VERSION = "0.9.0"
6
6
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: baseline-rb
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.8.0
4
+ version: 0.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gatework contributors
@@ -120,6 +120,7 @@ files:
120
120
  - examples/remediation_huawei_vty_authentication.rb
121
121
  - examples/remediation_ntp_authentication.rb
122
122
  - examples/remediation_ntp_authentication_switch.rb
123
+ - examples/remediation_nxos_aaa.rb
123
124
  - examples/remediation_palo_alto_ntp.rb
124
125
  - examples/remediation_palo_alto_syslog.rb
125
126
  - examples/remediation_palo_alto_syslog_migration.rb
@@ -147,6 +148,7 @@ files:
147
148
  - lib/baseline/cisco/tacacs_groups.rb
148
149
  - lib/baseline/cisco_ios/command_renderer.rb
149
150
  - lib/baseline/cisco_ios/parser.rb
151
+ - lib/baseline/cisco_nxos/authentication.rb
150
152
  - lib/baseline/cisco_nxos/command_renderer.rb
151
153
  - lib/baseline/cisco_nxos/parser.rb
152
154
  - lib/baseline/command_renderer.rb
@@ -181,6 +183,9 @@ files:
181
183
  - lib/baseline/ntp_commands.rb
182
184
  - lib/baseline/ntp_remediation.rb
183
185
  - lib/baseline/ntp_server_operation_schema.rb
186
+ - lib/baseline/nxos_aaa_methods.rb
187
+ - lib/baseline/nxos_aaa_remediation.rb
188
+ - lib/baseline/nxos_aaa_schema.rb
184
189
  - lib/baseline/palo_alto/command_index.rb
185
190
  - lib/baseline/palo_alto/command_renderer.rb
186
191
  - lib/baseline/palo_alto/document.rb