Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 111 additions & 0 deletions aci-preupgrade-validation-script.py
Original file line number Diff line number Diff line change
Expand Up @@ -6702,6 +6702,116 @@ def stale_dbgacEpgSummaryTask_check(tversion, **kwargs):
return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)


@check_wrapper(check_title='Certificate Expiration Check')
def certificate_expiration_check(cversion, username, password, fabric_nodes, **kwargs):
result = PASS
headers = ["Fault Code", "Severity", "Description"]
data = []
recommended_action = ""
doc_url = 'https://datacenter.github.io/ACI-Pre-Upgrade-Validation-Script/validations/#certificate-expiration-check'

fault_min_versions = [
("F4501", "6.0(4c)"), ("F4502", "6.0(4c)"), # KeyRing cert expiring/expired
("F4503", "6.1(1e)"), ("F4617", "6.1(1e)"), # TP cert expired/expiring
("F3081", "3.1(2f)"), ("F3082", "3.1(2f)"), # SAML encryption cert expiring/expired
("F4752", "6.1(5e)"), ("F4753", "6.1(5e)"), # Factory certificate expired/expiring
]
FACTORY_CERT_MIN_VERSION = "6.1(5e)"
FACTORY_CERT_EXPIRING_DAYS = 30

has_critical = has_major = has_error = False

applicable_codes = [code for code, ver in fault_min_versions if not cversion.older_than(ver)]
if applicable_codes:
fault_filter = ",".join('eq(faultInst.code,"{}")'.format(code) for code in applicable_codes)
for faultInst in icurl('class', 'faultInst.json?query-target-filter=or({})'.format(fault_filter)):
fault_attrs = faultInst['faultInst']['attributes']
if fault_attrs['lc'] not in ("raised", "soaking"):
continue
data.append([fault_attrs['code'], fault_attrs['severity'], fault_attrs['descr']])
if fault_attrs['severity'] == 'critical':
has_critical = True
elif fault_attrs['severity'] == 'major':
has_major = True

if cversion.older_than(FACTORY_CERT_MIN_VERSION) and username and password:
date_format = "%b %d %H:%M:%S %Y"
current_date_re = re.compile(
r'[A-Z][a-z]{2}\s+(?P<mon>[A-Z][a-z]{2})\s+(?P<day>\d+)\s+(?P<time>\d{2}:\d{2}:\d{2})\s+\w+\s+(?P<year>\d{4})'
)
cert_expiry_re = re.compile(
r'notAfter=(?P<date>[A-Z][a-z]{2}\s+\d+\s+\d{2}:\d{2}:\d{2}\s+\d{4})'
)

controllers = (node for node in fabric_nodes if node["fabricNode"]["attributes"]["role"] == "controller")
controllers = list(controllers)
for controller in controllers:
node_attrs = controller["fabricNode"]["attributes"]
node_id = node_attrs["id"]
node_name = node_attrs["name"]
node_addr = node_attrs.get("address")
if not node_addr:
continue
try:
c = Connection(node_addr)
c.username = username
c.password = password
c.log = LOG_FILE
c.connect()
c.cmd("date")
current_date_match = current_date_re.search(c.output)
c.cmd("acidiag verifyapic")
cert_expiry_match = cert_expiry_re.search(c.output)
except Exception as e:
data.append(["N/A", "error",
"APIC {} ({}): unable to verify factory certificate - {}".format(node_id, node_name, e)])
has_error = True
continue

try:
current_date = datetime.strptime(
"{mon} {day} {time} {year}".format(**current_date_match.groupdict()), date_format
)
except (AttributeError, ValueError):
data.append(["N/A", "error",
"APIC {} ({}): unable to determine current date".format(node_id, node_name)])
has_error = True
continue

try:
cert_expiry = datetime.strptime(" ".join(cert_expiry_match.group("date").split()), date_format)
except (AttributeError, ValueError):
data.append(["N/A", "error",
"APIC {} ({}): unable to determine factory certificate expiry date".format(node_id, node_name)])
has_error = True
continue

if cert_expiry <= current_date:
data.append(["N/A", "critical",
"APIC {} ({}): factory certificate expired on {} UTC".format(node_id, node_name, cert_expiry)])
has_critical = True
elif (cert_expiry - current_date).days <= FACTORY_CERT_EXPIRING_DAYS:
data.append(["N/A", "major",
"APIC {} ({}): factory certificate expiring on {} UTC".format(node_id, node_name, cert_expiry)])
has_major = True

if data:
if has_error:
result = ERROR
recommended_action = 'Manually verify the factory certificate expiry using `acidiag verifyapic` on the affected APIC(s).'
elif has_critical and has_major:
result = FAIL_O
recommended_action = 'Renew expired certificate(s) immediately. For certificate(s) approaching expiry, renew before they expire to avoid service disruption.'
elif has_critical:
result = FAIL_O
recommended_action = 'Renew the certificate(s) immediately to restore functionality.'
elif has_major:
result = MANUAL
recommended_action = 'Renew the certificate(s) before they expire to avoid service disruption.'

return Result(result=result, headers=headers, data=data, recommended_action=recommended_action, doc_url=doc_url)


# ---- Script Execution ----


Expand Down Expand Up @@ -6816,6 +6926,7 @@ class CheckManager:
equipment_disk_limits_exceeded,
apic_vmm_inventory_sync_faults_check,
apic_storage_inode_check,
certificate_expiration_check,

# Configurations
vpc_paired_switches_check,
Expand Down
112 changes: 111 additions & 1 deletion docs/docs/validations.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ Items | Faults | This Script
[Equipment Disk Limits][f20] | F1820: 80% -minor<br>F1821: -major<br>F1822: -critical | :white_check_mark: | :no_entry_sign:
[VMM Inventory Partially Synced][f21] | F0132: comp-ctrlr-operational-issues | :white_check_mark: | :no_entry_sign:
[APIC Storage Inode Usage][f22] | F4388: 75% - 85% -warning<br>F4389: 85% - 90% -major<br>F4390: 90% or more -critical | :white_check_mark: | :no_entry_sign:
[Certificate Expiration Check][f23] | F4501/F4502: KeyRing expiring/expired<br>F4617/F4503: TP expiring/expired<br>F3081/F3082: SAML expiring/expired<br>F4752/F4753: Factory expiring/expired | :white_check_mark: | :no_entry_sign:

[f1]: #apic-disk-space-usage
[f2]: #standby-apic-disk-space-usage
Expand All @@ -110,6 +111,7 @@ Items | Faults | This Script
[f20]: #equipment-disk-limits
[f21]: #vmm-inventory-partially-synced
[f22]: #apic-storage-inode-usage
[f23]: #certificate-expiration-check

### Configuration Checks

Expand Down Expand Up @@ -1639,7 +1641,115 @@ To recover from this fault, try the following action
type : operational
```


### Certificate Expiration Check

ACI uses various X.509 certificates for security and authentication purposes. If these certificates expire or are about to expire, it can cause service disruptions or failures. The fabric will raise different faults depending on the certificate type.

**Expiring Certificates (Major Severity):**

* **F4501**: KeyRing X.509 Certificate expiring - This fault occurs when a custom KeyRing X.509 Certificate is going to expire in one month.

* **F3081**: SAML X.509 Certificate expiring - This fault occurs when the SAML X.509 Certificate is going to expire in one month.

* **F4617**: TP X.509 Certificate expiring - This fault occurs when a Trust Point X.509 Certificate is expiring.

* **F4752**: Factory X.509 Certificate expiring - This fault occurs when the factory Certificate is expiring.


**Expired Certificates (Critical Severity):**

* **F4502**: KeyRing X.509 Certificate expired - This fault occurs when a custom KeyRing X.509 Certificate has expired.

* **F4503**: TP X.509 Certificate expired - This fault occurs when a Trust Point X.509 Certificate has expired.

* **F3082**: SAML X.509 Certificate expired - This fault occurs when the SAML Encryption X.509 Certificate has expired.

* **F4753**: Factory X.509 Certificate expired - This fault occurs when the factory Certificate has expired.


**Recommended Actions:**

* For expiring certificates (F4501, F3081, F4617, F4752): Renew the certificate(s) before they expire to avoid service disruption.

* For expired certificates (F4502, F4503, F3082, F4753): Renew the certificate(s) immediately to restore functionality.

!!! example "Fault Example (F4502: Expired KeyRing Certificate)"
The following shows an example of an expired KeyRing certificate:
```
admin@apic1:~> moquery -c faultInst -f 'fault.Inst.code=="F4502"'
Total Objects shown: 1

# fault.Inst
code : F4502
ack : no
alert : no
annotation :
cause : cert-expired
changeSet :
childAction :
created : 2026-05-12T05:23:26.549+00:00
delegated : no
descr : KeyRing Certificate THD_KEYRING expired
dn : uni/userext/pkiext/keyring-THD_KEYRING/fault-F4502
domain : security
extMngdBy : undefined
highestSeverity : critical
lastTransition : 2026-05-12T05:25:51.231+00:00
lc : raised
modTs : never
occur : 1
origSeverity : critical
prevSeverity : critical
rn : fault-F4502
rule : pki-key-ring-custom-key-ring-expired
severity : critical
status :
subject : security-provider
title :
type : operational
uid :
userdom : all
```

!!! example "Fault Example (F4501: Expiring KeyRing Certificate)"
The following shows an example of a KeyRing certificate expiring in one month:
```
admin@apic1:~> moquery -c faultInst -f 'fault.Inst.code=="F4501"'
Total Objects shown: 1

# fault.Inst
code : F4501
ack : no
alert : no
annotation :
cause : cert-expiring
changeSet :
childAction :
created : 2026-04-12T05:23:26.549+00:00
delegated : no
descr : KeyRing Certificate THD_KEYRING expiring in one month
dn : uni/userext/pkiext/keyring-THD_KEYRING/fault-F4501
domain : security
extMngdBy : undefined
highestSeverity : major
lastTransition : 2026-04-12T05:25:51.231+00:00
lc : raised
modTs : never
occur : 1
origSeverity : major
prevSeverity : major
rn : fault-F4501
rule : pki-key-ring-custom-key-ring-expiring
severity : major
status :
subject : security-provider
title :
type : operational
uid :
userdom : all
```


## Configuration Check Details

### VPC-paired Leaf switches
Expand Down
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_F3081.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F3081",
"severity": "major",
"lc": "raised",
"descr": "SAML Signing Certificate expiring in one month",
"dn": "uni/userext/samlext/samlidp-IDP1/fault-F3081"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_F3082.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F3082",
"severity": "critical",
"lc": "raised",
"descr": "SAML Encryption Certificate has expired",
"dn": "uni/userext/samlext/samlencert-default/fault-F3082"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_F4501.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F4501",
"severity": "major",
"lc": "raised",
"descr": "KeyRing Certificate THD_KEYRING expiring",
"dn": "uni/userext/pkiext/keyring-THD_KEYRING/fault-F4501"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_F4502.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F4502",
"severity": "critical",
"lc": "raised",
"descr": "KeyRing Certificate THD_KEYRING expired",
"dn": "uni/userext/pkiext/keyring-THD_KEYRING/fault-F4502"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_F4503.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F4503",
"severity": "critical",
"lc": "raised",
"descr": "TP Certificate THD_CA expired",
"dn": "uni/userext/pkiext/tp-THD_CA/fault-F4503"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_F4617.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F4617",
"severity": "major",
"lc": "raised",
"descr": "TP Certificate expiring",
"dn": "uni/fabric/comm-default/https/fault-F4617"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_F4752.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F4752",
"severity": "major",
"lc": "raised",
"descr": "Factory certificate expiring",
"dn": "topology/pod-1/node-1/sys/ch/fault-F4752"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_F4753.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F4753",
"severity": "critical",
"lc": "raised",
"descr": "Factory certificate expired",
"dn": "topology/pod-1/node-1/sys/ch/fault-F4753"
}
}
}
]
13 changes: 13 additions & 0 deletions tests/checks/certificate_expiration_check/faultInst_cleared.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"faultInst": {
"attributes": {
"code": "F4502",
"severity": "critical",
"lc": "retaining",
"descr": "KeyRing Certificate THD_KEYRING expired",
"dn": "uni/userext/pkiext/keyring-THD_KEYRING/fault-F4502"
}
}
}
]
Loading
Loading