nomostatic

Lab Guide for Snort Inspection Through TLS Resign Decryption

Read the techtorial here…. to get the background on why we decrypt.

THE LAB AT A GLANCEREAD BEFORE CONFIGURING

Before you configure the lab

This workflow applies to compatible FTD models managed by either cloud-delivered FMC (cdFMC) or a self-hosted Secure Firewall Management Center (FMC). Use a supported device/software combination with the Snort 3 and TLS 1.3 decryption capabilities used here. Menu names and feature availability can vary by release. The guide builds the measured inspection path behind the companion TechTorial: a controlled client, two isolated routed segments, a Python HTTPS origin and narrowly scoped Resign decryption. It finishes with request- and response-signature controls and restoration checks. It does not install vulnerable PHP or reproduce an exploitation chain.

The working decryption rule uses Decrypt – Resign, Replace Key Only enabled, TCP/443, exact lab hosts/zones and all certificate-status match conditions set to Any. Keep the client’s certificate/hostname validation, the origin trust anchor and backend issuer protection. Any skips a match condition; invalid-backend-certificate denial remains untested in this lab.

The measured results are historical observations from October 27, 2026. Instructions below create fresh PKI and fresh replay IDs rather than reusing those certificates or overwriting those results. The publication setup personalizes copies of measured runners only for a new public CA fingerprint and CA job/path/issuer name; it sends no traffic. Its offline validation is distinct from the recorded appliance tests.

Measured baseline: our recorded results used a Secure Firewall 1210CE running FTD 10.1.0 build 160, managed by cdFMC. Those details identify the test environment; they are not a requirement to use that model or management deployment. The interface names and IP addresses below are examples you can replace consistently for your environment.

01PrepareIsolate the two segments, generate fresh certificates and stage disabled policies.
02VerifyDeploy each window, then correlate exact ports, signatures and actual TLS actions.
03RestoreClose the firewall exception and confirm the original responder owns the listener.
THE PRACTICAL DECISION

Start with a controlled client and a disabled lab exception. Confirm deployment completion before each batch, preserve the evidence, and verify cleanup before the next phase.

GO DEEPER · COMPLETE WALKTHROUGHStep-by-step lab guide15 steps · fresh PKI · HTTPS controls · evidence and cleanupOpenCollapse

A reproducible, scoped TLS inspection lab with evidence and restoration checks at every phase.

1. Prepare the hosts and record the starting state

Use an FTD device registered to your cdFMC or self-hosted FMC, with normal connectivity confirmed. In this guide, FMC means either management deployment; UI examples follow our cdFMC session. Cisco also documents rule-based decryption for self-hosted FMC. Cisco self-hosted FMC decryption guide[1] Record show version, Snort engine, current running LSP/SRU, manager and device VDB observations, ACP assignment and policy deployment state. Our device reported 10.1.0 build 160 and LSP lsp-rel-20261021-1822. Device VDB 433 and manager VDB 441 were separate observations, not interchangeable version fields. Readers should record their current values and installed SID revisions.

Use two Linux hosts with Python 3.9 or newer on the client, Python 3.11 or newer on the origin, and OpenSSL, curl, iproute2 and systemd available before isolation. The measured client used OpenSSL 1.1.1n/curl 7.74.0; the origin used OpenSSL 3.0.22. Do not downgrade a newer supported host to match those historical versions. Confirm curl supports TLS 1.3, –tls13-ciphers and –curves.

Unpack the public toolkit into /tmp/nomostatic-publication on each host. Verify its supplied SHA256SUMS.txt before executing scripts. The toolkit contains source and synthetic fixtures, not certificate private keys. Use a fresh job throughout the preparation example:

job=RESIGN-CA-20261027-10
kit=/tmp/nomostatic-publication
cd "$kit"
sha256sum -c SHA256SUMS.txt
python3 --version
openssl version
curl --version

Change the date/suffix for your lab and use it consistently. Certificate validity begins when generated; the job name is only a label. Choose the interfaces and unused IP ranges available in your environment. The command examples and stock toolkit use our recorded values; map your substitutions before using them. The toolkit’s CA-personalization step changes the CA job/pin only and does not automatically change topology settings.

On both hosts, confirm forwarding is off and no bridge connects the lab NIC to management:

sysctl net.ipv4.ip_forward net.ipv6.conf.all.forwarding
ip -br address
ip -4 route show
ip -d link show type bridge

The observed lab hosts retained separate Wi-Fi management paths. The responders bind the lab address, not all interfaces. Keep management access separate and verify that the hosts do not route traffic between the lab and normal LAN.

2. Configure the two routed firewall interfaces

Choose two available routed interfaces or suitable isolated VLAN subinterfaces for the client and origin segments. Check for subnet overlap with existing routes, VPNs and NAT. We used the following example assignments:

  • Ethernet1/5: lab-client, LAB-CLIENT-ZONE, 192.0.2.1/24.
  • Ethernet1/6: lab-origin, LAB-ORIGIN-ZONE, 10.20.30.1/24.
  • Client eth0: 192.0.2.50/24.
  • Origin eth0: 10.20.30.40/24, return route through 10.20.30.1.
  • VIP: 192.0.2.10, reserved on the client segment.
  • Application: app.nomostatic.test, TCP/443.

In your FMC, open Devices → Device Management → your FTD → Interfaces and select the interfaces assigned to the two lab roles. Configure their names, security zones and static IPv4 addresses. On our 1210, we first disabled SwitchPort on Ethernet1/5 and Ethernet1/6 and used regular firewall interface Mode None. That conversion and UI field are model-specific; use the regular routed-interface procedure appropriate to your device or subinterfaces. Enable the selected interfaces, review the change comparison, deploy to the intended device and verify normal traffic. Preserve the existing normal-LAN, WAN, management and VLAN settings. Cisco interface procedure for our recorded 1210 example[2]

Substitute by role, across the complete path

Ethernet1/5, Ethernet1/6, eth0 and the example addresses are not required values. Record your equivalents for the client-facing FTD interface/gateway, origin-facing FTD interface/gateway, client IP/NIC, origin IP/NIC and NAT VIP. Keep the two segments isolated, the VIP unused and reachable from the client segment, and the origin return path through FTD.

Apply each substitution consistently to FMC interface addresses, objects, zones, routing, NAT, ACP/decryption rules, host routes and commands. For example, replacing 10.20.30.40 also changes the origin object, NAT target, origin bind/listener checks and expected event tuple; replacing the client NIC changes every eth0 route check.

Changing only the FTD physical port names while keeping the same lab zones, addresses and host NICs does not require changing the host collectors. Map the new FTD ports to the same logical client/origin roles.

The stock scripts intentionally retain our fixed topology. To use another topology, adapt a separate working copy of the responders, service units, collectors and their expected-value checks before running it. Review source binds and client/probe allowlists, cURL source/resolve values, VIP and client constants, route/NIC assertions, listener-readiness checks and connection-correlation expectations. Regenerate the affected script/helper pin and file manifests in that adapted copy; preserve the unchanged original bundle and synthetic fixture-body hashes. The stock configure_client.py verifies original templates and will reject changed template bytes. Adapt its generation/verification logic as part of a custom toolkit rather than claiming an edited template is original.

This is a documented example toolkit, not a topology auto-discovery tool. Run the same route, certificate, isolation, health, negative-control and cleanup gates after adaptation. The policy method is portable; the recorded test outcomes and native source hashes still describe our original topology.

Assign host addresses using the hosts’ existing network managers. The client can retain its management default route. Validate the lab route explicitly:

ip -4 route get 192.0.2.10 from 192.0.2.50
ip -4 neigh show dev eth0

For the example assignments, the route must use eth0/source 192.0.2.50; for substitutions, require your selected lab NIC and client source address. If the host configuration omitted the lab route, correct that configuration. A temporary VIP-only route is:

sudo ip -4 route replace 192.0.2.10/32 dev eth0 src 192.0.2.50

This does not replace the management default route. On the origin, verify the return path:

ip -4 route get 192.0.2.50 from 10.20.30.40

For the example assignments, it should use eth0 through 10.20.30.1; substitute your origin NIC and FTD gateway consistently. A failed VIP neighbor entry means resolve addressing/ARP first; a TLS test cannot diagnose that layer for you.

3. Stage isolation and NAT with the lab allow rule disabled

Create host objects for your mapped client, origin and VIP. Our example objects are LAB-CLIENT=192.0.2.50, LAB-ORIGIN=10.20.30.40 and LAB-VIP=192.0.2.10, plus LAB-HTTPS for TCP destination port 443. These logical role names are used throughout the guide.

Our measured ACP was assigned only to the test firewall. Check assignments before editing yours. If it is shared, use a reviewed device-specific policy preserving existing associations and normal traffic behavior.

Create LAB-HTTPS-ALLOW, initially disabled: Allow from LAB-CLIENT-ZONE/LAB-CLIENT to LAB-ORIGIN-ZONE/LAB-ORIGIN, TCP/443, with connection logging to the event viewer. Leave application and URL conditions unset for the lab comparison.

Below this narrow exception, add lab isolation Block rules covering other traffic from either lab zone and other traffic to either lab zone. Place the exception and isolation rules ahead of existing general permits/Trust decisions that would otherwise take precedence. In our lab, some blocks had to move to the Mandatory section above normal-LAN permits; names alone did not establish isolation. Keep a temporary HTTP/8080 exception disabled or omit it from this HTTPS-only build.

Deploy and verify isolation while the HTTPS exception remains disabled. Use Packet Tracer for client→VIP, normal-LAN→lab, lab-client→normal-LAN and lab-origin→normal-LAN paths, plus a normal-traffic baseline. Capture the matched rule and final action. These are configuration-path simulations, not live IPS payload results.

Add address-only Static Auto NAT in the NAT policy assigned to your test FTD: original/source LAB-ORIGIN on lab-origin maps to LAB-VIP on lab-client. Keep the same TCP port and retain proxy ARP for the on-link VIP. Review NAT ordering for earlier matches. The inbound VIP flow should translate to your mapped origin address (10.20.30.40 in our example) and still hit an isolation/disabled-exception drop until the test window opens. Deploy and retain the Packet Tracer translation and ACP result. Cisco NAT guidance[3]

4. Create fresh certificates and separate the trust roles

Run the PKI helper on a trusted Linux administrative host with OpenSSL. It creates new P-256 keys and SHA-256 certificates inside a fresh mode-0700 directory. These are short-lived lab signing identities, not a production PKI or hardware-backed key store.

job=RESIGN-CA-20261027-10
kit=/tmp/nomostatic-publication
bash "$kit/tools/create_lab_pki.sh" "$job"

It generates a separate origin root, an origin leaf for app.nomostatic.test, and a Resign signing CA. The origin leaf lasts 30 days; the Resign CA lasts 14 days. Its private directory must stay outside public downloads, evidence and TAC bundles. Retain the origin-root master offline after provisioning.

Transfer through your authenticated administrative channel:

  • To the origin: origin-chain.pem from public/ and origin-key.pem from private/, into a restricted temporary folder.
  • To the client: public/ only, including lab-origin-root.pem and lab-resign-ca.pem.
  • To the workstation used to import certificates into your FMC: lab-resign-ca.pem and its matching lab-resign-ca-key.pem in a private per-user folder; remove the extra key working copy after successful import. File removal is not secure erasure.

Compare certificate fingerprints over the trusted administrative connection. Do not accept a fingerprint solely because it travelled with an untrusted copy of the certificate.

In your FMC’s PKI objects, import lab-origin-root.pem as Trusted CA NOMOSTATIC-LAB-ROOT. Import lab-resign-ca.pem and lab-resign-ca-key.pem as Internal CA LAB-RESIGN-CA. The origin root’s private key is not imported. Verify subjects, expiry, fingerprints and certificate/key pairing in the import result. A frontend server certificate is not required for this Resign recipe.

The client will trust the origin root for the bypass baseline and the dedicated Resign CA for intercepted traffic. The cookie runner uses only the pinned Resign CA, so an origin-certificate bypass cannot silently pass that check. The other runners also inspect the expected Resign issuer.

Our measured CA import omitted CRL attachment after a rejected CRL import. This guide does not establish revocation enforcement or advise disabling existing revocation policy. Keep client certificate verification enabled in every test.

5. Install the origin service

On a fresh origin, create the existing-style non-login service account if absent. Copy the incoming origin certificate/key into /etc/nomostatic-lab with restricted permissions. In this example the transferred files are in /tmp/nomostatic-private, already mode 0700:

(
set -euo pipefail
kit=/tmp/nomostatic-publication
for target in /opt/nomostatic-lab/lab_responder.py \
  /etc/nomostatic-lab/origin-chain.pem /etc/nomostatic-lab/origin-key.pem \
  /etc/systemd/system/nomostatic-lab.service; do
  if sudo test -e "$target" || sudo test -L "$target"; then
    printf 'Existing lab installation: preserve it before using a fresh-install recipe.\n' >&2
    exit 1
  fi
done
id nomostatic-lab >/dev/null 2>&1 || \
  sudo useradd --system --no-create-home --home-dir /nonexistent \
    --shell /usr/sbin/nologin nomostatic-lab
sudo install -d -o root -g nomostatic-lab -m 0750 \
  /opt/nomostatic-lab /etc/nomostatic-lab
sudo install -o root -g nomostatic-lab -m 0640 \
  /tmp/nomostatic-private/origin-chain.pem /etc/nomostatic-lab/origin-chain.pem
sudo install -o root -g nomostatic-lab -m 0640 \
  /tmp/nomostatic-private/origin-key.pem /etc/nomostatic-lab/origin-key.pem
sudo install -o root -g nomostatic-lab -m 0640 \
  "$kit/tools/templates/lab_responder.py" /opt/nomostatic-lab/lab_responder.py
sudo install -o root -g root -m 0644 \
  "$kit/tools/templates/nomostatic-lab.service" /etc/systemd/system/nomostatic-lab.service
sudo systemd-analyze verify /etc/systemd/system/nomostatic-lab.service
sudo systemctl daemon-reload
sudo systemctl start nomostatic-lab.service
)

These are fresh-install instructions. Preserve an existing service/configuration before replacing any file; the recorded lab’s original service was retained unchanged during cookie tests. Do not enable this lab service at boot unless that is part of your chosen lab lifecycle.

With the stock example configuration, the source binds only 10.20.30.40:443 and allows 192.0.2.50 and 10.20.30.1. A substituted topology must use its mapped bind address and narrowly scoped client/FTD probe addresses in the adapted service configuration. It executes no PHP, serves no arbitrary files, bounds requests and uses TLS 1.3/HTTP/1.1. The service runs as nomostatic-lab with only the bind-port capability, a read-only protected system view, private temporary directory and protected home access.

Wait for readiness, then match MainPID to the ss listener. Type=simple start success alone is insufficient:

systemctl show nomostatic-lab.service -p ActiveState -p SubState -p MainPID
sudo ss -ltnp 'sport = :443'
sudo journalctl -u nomostatic-lab.service --since '5 minutes ago' \
  --utc -n 40 --no-pager -o cat

The expected socket is your mapped origin IP on TCP/443 (10.20.30.40:443 in the stock example), owned by that service’s Python PID. Our original service once took over five seconds to reach its listening log; the updated cookie switch helper allows a 30-second readiness deadline.

6. Prepare the client replay tools without reusing our CA

On the client, place the transferred public files in /tmp/nomostatic-public. Enter the Resign CA DER SHA-256 obtained through the trusted admin connection, not the old fingerprint printed in historical results:

job=RESIGN-CA-20261027-10
kit=/tmp/nomostatic-publication
read -r -p 'Trusted Resign CA DER SHA256 (64 lowercase hex characters): ' ca_pin
python3 -B "$kit/tools/configure_client.py" \
  --job "$job" --public-dir /tmp/nomostatic-public --ca-sha256 "$ca_pin"

The setup verifies template hashes, public CA type/expiry/name and the explicit CA pin. It refuses existing job destinations, creates the fixed multipart and cookie fixtures, and installs replay copies under ~/nomostatic-lab/replay/$job. Trust files are under ~/nomostatic-lab/resign-control/$job/client. Only the CA job/path/issuer name and public pin change in the stock runtime runners; the topology remains the fixed example unless you adapt a separate toolkit as described in step 2. No system trust store is modified and no lab request is sent.

On the client, verify that the process-local profile works with its Python/OpenSSL build:

job=RESIGN-CA-20261027-10
replay="$HOME/nomostatic-lab/replay/$job"
OPENSSL_CONF="$replay/cookie/tools/tls-profile.cnf" \
  python3 -B "$replay/cookie/tools/nomostatic_cookie_https.py" tls-profile

It must report TLS_AES_128_GCM_SHA256 as the only TLS 1.3 cipher. P-256 is configured in that profile. The profile affects only the explicitly launched process. Stop if it is ignored; do not loosen verification or change the operating system’s global OpenSSL configuration.

The fixture checks should reproduce multipart body lengths 198 / 313 / 305 bytes and cookie header-line lengths 96 / 101 / 101 / 98 bytes. Fixture contents and their hashes match the measured controls.

7. Stage the intrusion policies

Create a dedicated LAB-IPS in Prevention mode. Import tools/templates/nomostatic-lab.rules into a local Snort 3 group and verify acceptance. Choose an unused local SID in a new environment; the recorded lab used 2000:1000001:2 after import. Set its effective action to Alert. Its fast_pattern modifier belongs on the content option; the earlier standalone-keyword version failed import.

Attach LAB-IPS to disabled LAB-HTTPS-ALLOW. A disabled ACP rule still needs its intended inspection policy staged and deployed before the window opens. The UI may expose security-level/group inclusion before individual overrides; inspect the final effective rule action, revision and Set By value rather than assuming a selected level provides the desired action.

Prepare separate copies for enforcement:

  • LAB-IPS-MARKER-BLOCK: local marker Block, with unrelated test signatures disabled.
  • LAB-IPS-HTTPS-67360: installed 1:67360:2 Block, marker and 67375 disabled, Web Applications Included.
  • LAB-IPS-HTTPS-67375: installed 1:67375:1 Block, marker and 67360 disabled, Web Applications Included.

The marker Block policy name is a guide label; the evidence ledger retains the names actually recorded for each historical run. Verify the installed raw rule text/revision before replay. If the rule conditions differ, redesign the control for that revision rather than forcing the old expectation. The recorded variables were HOME_NET=Any and EXTERNAL_NET=any; validate the source/destination variable matches in your policy.

Keep the standard published Talos rule bodies unchanged. For this revision, 67360’s detection_filter is part of the result and must not be removed to make a single request alert. Its recorded condition was count 10 in five seconds, tracked by source. SID 67375 had no rate condition and inspected the response.

8. Stage the working Resign policy

Cisco recommends Resign for outgoing traffic and Known Key/Replace Certificate for incoming traffic. This recipe deliberately uses a controlled client whose trust bundle we administer to validate the measured client-to-origin inspection path. It is not an Internet-facing ingress deployment recommendation.

Our measured ACP initially had no decryption policy. If yours already has one, preserve its rules/defaults in a reviewed device-specific copy or use a separate lab ACP/device; do not replace normal-traffic decryption with this empty lab policy.

Create NOMOSTATIC-LAB-TLS with a Do Not Decrypt default for the lab’s initially non-decrypting baseline. Associate it at the existing ACP level. Keep other normal-traffic associations and settings unchanged. This default is a test-scope decision, not a demonstrated fail-closed production design.

In its rule-based editor, stage LAB-RESIGN-SCOPE disabled:

  • Action Decrypt – Resign; Internal CA LAB-RESIGN-CA; Replace Key Only enabled.
  • Source LAB-CLIENT-ZONE / LAB-CLIENT 192.0.2.50.
  • Destination LAB-ORIGIN-ZONE / LAB-ORIGIN 10.20.30.40, the real translated server.
  • Destination TCP/443; source ports Any.
  • Every certificate-status match condition Any, including Valid.
  • Application, URL/category, user, DN, separate certificate-match, cipher-suite and TLS-version match conditions unset.
  • End-of-connection logging enabled.

Keep NOMOSTATIC-LAB-ROOT in policy Trusted CAs and retain the existing protection against resigning an untrusted issuer. Confirm TLS 1.3 decryption enabled. Record the adaptive-probe and ACP TLS server-identity discovery settings; ours were enabled. Cipher/version conditions are not how this experiment pins negotiation: the client does that. Cisco warns against those match conditions on decrypt actions because they can interfere with ClientHello processing. Cisco rule guidelines[4]

Add LAB-BYPASS, disabled and above Scope, with the same exact zones/hosts/TCP443 and action Do Not Decrypt. Keep any earlier experimental LAB-DECRYPT/LAB-RESIGN rules disabled during the clean comparison. This recipe does not require creating a Replace Certificate or Known Key rule.

All Cert Status fields at Any means none of those statuses gates rule selection. It does not establish invalid-certificate acceptance or rejection. In valid-backend tests, FMC subsequently reported certificate status Valid. Our earlier Valid=Yes rule attempts bypassed with an unsupported-cipher classification; the later measured Any path succeeded, but that is not a proven universal fix or isolated TAC root cause.

Deploy the staged policy and disabled lab exception. Capture show ssl-policy-config and the applied policy state, and confirm normal traffic. The matching decryption rule belongs to the ACP-level policy; the intrusion action belongs to the lab Allow rule’s selected policy.

9. Use the same deployment and evidence gate for every phase

Before sending traffic, record the phase name, intended action, selected intrusion policy, effective rule override, decryption-rule state, deployment identifier and successful completion. An editor Save or pending deployment is not an applied policy. Do not infer the current policy from an earlier run’s label.

Use a fresh batch ID for every attempt. Retain failed starts and pre-deployment runs separately. On the origin, capture hostname, UTC time, time-sync state, service state/MainPID and the listener before traffic. After the batch, capture the retained journal with its run IDs and surrounding errors. These are metadata commands, not requests:

hostname
date -u +%Y-%m-%dT%H:%M:%SZ
timedatectl status --no-pager
systemctl show nomostatic-lab.service -p ActiveState -p SubState -p MainPID
sudo ss -ltnp 'sport = :443'

Save that output into a fresh mode-0700 per-phase directory and retain its hashes. The supplied capture-67360-origin.sh provides preflight/capture modes for the matching 67360 batch pattern; for other phases, use the same metadata and the main service journal, without recreating a missing preflight after a test.

The fresh public replay examples use suffix 10, distinct from the historical result IDs. They are instructions to reproduce the control, not additional recorded results.

10. Bypass baseline and Resign health

For PATH-A-10, select LAB-IPS at Alert, enable LAB-BYPASS, keep Scope disabled, enable LAB-HTTPS-ALLOW, deploy and wait for completion. Use the origin-root CA and the same marker body, host, VIP, TLS offer and source address:

(
set -euo pipefail
umask 077
unset SSLKEYLOGFILE
job=RESIGN-CA-20261027-10
run_id=PATH-A-10
trust="$HOME/nomostatic-lab/resign-control/$job/client"
parent="$HOME/nomostatic-lab/bypass"
install -d -m 0700 "$parent"
mkdir -m 0700 "$parent/$run_id"
cd "$parent/$run_id"
printf '%s\n' 'NOMOSTATIC-INGRESS-20261022-6C9D71' > probe.txt
date -u +%Y-%m-%dT%H:%M:%SZ > start-utc.txt
curl_exit=0
curl --disable --http1.1 --tlsv1.3 --tls-max 1.3 \
  --tls13-ciphers TLS_AES_128_GCM_SHA256 --curves P-256 \
  --noproxy '*' --interface 192.0.2.50 --connect-timeout 5 --max-time 20 \
  --cacert "$trust/lab-origin-root.pem" \
  --resolve app.nomostatic.test:443:192.0.2.10 \
  --header 'Content-Type: text/plain' --header "X-Nomostatic-Run: $run_id" \
  --data-binary @probe.txt --verbose --stderr tls.txt \
  --dump-header headers.txt --output response.bin \
  --write-out 'HTTP=%{http_code} verify=%{ssl_verify_result} local=%{local_ip}:%{local_port} remote=%{remote_ip}:%{remote_port}\n' \
  https://app.nomostatic.test/nomostatic-lab/inspect > client.txt || curl_exit=$?
printf 'curl_exit_code=%s\n' "$curl_exit" >> client.txt
date -u +%Y-%m-%dT%H:%M:%SZ > end-utc.txt
sha256sum probe.txt start-utc.txt end-utc.txt tls.txt headers.txt response.bin client.txt > SHA256SUMS.txt
cat client.txt
)

Require HTTP 204, successful certificate verification, the origin issuer, a completed origin response, matched LAB-BYPASS/Do Not Decrypt and no marker SID on that actual port. Allow/connectivity alone is not evidence of either readable payload inspection or a signature failure.

For the Resign health check, disable Bypass, enable Scope, keep LAB-IPS/Allow, deploy, and use a fresh health ID with the two-root client bundle:

job=RESIGN-CA-20261027-10
curl --disable --http1.1 --tlsv1.3 --tls-max 1.3 \
  --tls13-ciphers TLS_AES_128_GCM_SHA256 --curves P-256 \
  --noproxy '*' --interface 192.0.2.50 --connect-timeout 5 --max-time 20 \
  --cacert "$HOME/nomostatic-lab/resign-control/$job/client/lab-client-ca-resign.pem" \
  --resolve app.nomostatic.test:443:192.0.2.10 \
  --header 'X-Nomostatic-Run: RESIGN-HEALTH-10' --verbose \
  https://app.nomostatic.test/health

Require the new Resign issuer and normal hostname/chain verification at the client, HTTP 204, matching origin run ID, and FMC named LAB-RESIGN-SCOPE with TLS Actual Action=Decrypt (Resign) and TLS Status=Decrypt (Resign). Save the actual cipher/version, certificate status, flags and any cache-hit/miss indication. A successful health GET is the prerequisite to payload controls, not their verdict.

11. Local-marker HTTPS Alert and Block

With Scope enabled and Bypass/old experimental rules disabled, attach LAB-IPS to LAB-HTTPS-ALLOW, confirm marker Alert, deploy, then run on the client:

job=RESIGN-CA-20261027-10
replay="$HOME/nomostatic-lab/replay/$job"
bash "$replay/run_https_alert_controls.sh" HTTPS-ALERT-10

Expect clean HTTP 204 with a 25-byte body, marker HTTP 204 with a 35-byte body, and URI-negative HTTP 404. Require exact imported local GID:SID:revision at Alert for marker only, no marker event on either negative, actual Resign and correct rule on all three connections, plus matching origin completions. The URI-negative carries the marker to a different route; its 404 is expected.

Close the window, capture the main-service journal, disable the lab exception and deploy. Stage LAB-IPS-MARKER-BLOCK on the disabled exception with the local rule at Block. Reopen Scope/Allow only after the next deployment completes, then:

job=RESIGN-CA-20261027-10
replay="$HOME/nomostatic-lab/replay/$job"
bash "$replay/run_https_block_controls.sh" HTTPS-BLOCK-10

Require clean HTTP 204, URI-negative HTTP 404, marker no HTTP response after verified Resign TLS, and the exact local SID with actual Block on its source port. Require both negative SID searches and matching origin observations. No completed marker response is a bounded observation, not proof that zero request bytes reached the server. Capture, close and deploy before waiting for event ingestion.

12. Rate-aware SID 67360 HTTPS Block

Attach LAB-IPS-HTTPS-67360 while the lab exception is disabled. Verify the installed revision’s complete conditions, Block override and source/destination variables. Deploy the scoped window and confirm normal traffic before sending anything. Keep the multipart helper’s generated files unchanged.

On the origin, optionally use the supplied capture helper before traffic:

kit=/tmp/nomostatic-publication
bash "$kit/tools/templates/capture-67360-origin.sh" preflight TALOS-67360-HTTPS-BLOCK-10

On the client:

job=RESIGN-CA-20261027-10
replay="$HOME/nomostatic-lab/replay/$job"
python3 -B "$replay/run_talos_67360_https.py" TALOS-67360-HTTPS-BLOCK-10

The runner sends a clean control, waits six seconds, dispatches eleven fixed candidates with upload-notification observations, then sends the field-name-negative. It does not automatically retry a blocked candidate or remove the rule’s rate condition.

Capture the origin immediately after the batch, then close/deploy the lab window:

kit=/tmp/nomostatic-publication
bash "$kit/tools/templates/capture-67360-origin.sh" capture TALOS-67360-HTTPS-BLOCK-10

Correlate actual client ports. The historical result had candidates 1–10 complete HTTP 204 with 313-byte bodies and candidate11 at exact 1:67360:2 Block with no HTTP response, plus two successful negatives without SID events. Do not demand an identical threshold position if preceding traffic or state differs; preserve the actual result. The client’s notification span is not Snort’s match interval. The first ten historical completions must remain visible in any prevention claim.

13. SID 67375 HTTPS response-cookie Block

An additional Alert-only HTTPS cookie run is optional. Plaintext calibration and the recorded intentional HTTPS Block with three negative controls support the scoped enforcement claim. This guide goes directly to Block; it does not claim a successful HTTPS cookie Alert trial.

On the origin, while the lab exception is disabled, stage the unchanged bounded cookie service using the source bundle and its measured R2 switch helper:

kit=/tmp/nomostatic-publication
bash "$kit/tools/origin-cookie/manage_cookie_https.sh" stage

It must leave original Python active/listening and the cookie fixture inactive. Stage LAB-IPS-HTTPS-67375 with 1:67375:1 Block, the other test SIDs disabled and Web Applications Included. Enable Scope/Allow and finish deployment before starting the fixture’s ten-minute window.

On the origin:

kit=/tmp/nomostatic-publication
bash "$kit/tools/origin-cookie/manage_cookie_https_r2.sh" start TALOS-67375-HTTPS-BLOCK-10

Require Fixture ready and the temporary service’s exact PID on 10.20.30.40:443. On the client:

(
set -euo pipefail
umask 077
job=RESIGN-CA-20261027-10
replay="$HOME/nomostatic-lab/replay/$job"
cd "$replay/cookie"
console_log=TALOS-67375-HTTPS-BLOCK-10-client-console.txt
(set -C; : > "$console_log")
OPENSSL_CONF="$PWD/tools/tls-profile.cnf" \
  python3 -B ./tools/run_talos_67375_https.py TALOS-67375-HTTPS-BLOCK-10 \
  2>&1 | tee -a "$console_log"
)

Require three exact expected HTTP 200 negative responses over verified Resign TLS. CANDIDATE must have verified Resign TLS, zero application-response bytes and no candidate cookie observed. A timeout or reset alone is not an IPS verdict; any delivered bytes must be preserved and inspected rather than discarded.

On the origin, run capture then rollback even if the client reports failure. Run them as separate commands so a capture error does not prevent restoration:

kit=/tmp/nomostatic-publication
bash "$kit/tools/origin-cookie/manage_cookie_https_r2.sh" capture TALOS-67375-HTTPS-BLOCK-10
bash "$kit/tools/origin-cookie/manage_cookie_https_r2.sh" rollback TALOS-67375-HTTPS-BLOCK-10

Require original-file hash checks, the original Python PID owning 10.20.30.40:443, temporary service inactive and unchanged original boot enablement. Automatic fixture expiry stops the temporary service; it does not restart original Python. If rollback fails, keep LAB-HTTPS-ALLOW disabled, capture both service journals and listener state, and resolve the actual startup error before another window. Do not assume that the failure is a firewall or TLS problem.

FMC must show 1:67375:1 actual Block for the candidate, in LAB-IPS-HTTPS-67375, with actual Resign evidence. Search both orientations: the intrusion event may be origin:443→client:ephemeral while the connection row uses client-initiated orientation. Confirm no matching SID for the three negative ports. Origin headers_written=true is compatible with FTD blocking that response later.

14. Capture the verdict and close the lab

For every phase, preserve:

  • Original client output, raw response bytes, request/fixture hashes, actual ports and UTC windows.
  • Origin preflight, listener ownership, run-ID journal entries and nearby errors.
  • Connection events: access rule, decryption policy/rule, expected/actual TLS action, cipher/version, certificate status and flow error.
  • Intrusion events: actual action, GID:SID:revision, intrusion policy, tuple/direction and any inline reason/result.
  • Negative searches, deployment completion evidence, runtime/content versions and final restoration checks.

Download filtered events without assuming their column set. One export in our lab contained only Connection records; it could confirm all four decryption outcomes but not signature detection. A Connection Allow is compatible with an Intrusion Alert. A missing record in a filtered CSV is not proof that an intrusion never occurred.

Keep the export’s displayed timestamp and timezone/offset. Our matching exports showed a UTC-04:00 offset on the recorded dates even though the viewer was described as EST. The original UTC client/origin logs remain the time anchors; preserve any skew rather than silently correcting raw timestamps.

Hash native archives before transfer and verify the hashes after transfer. Never include private keys, unrelated captures or real session data. If a publication label changes, retain original evidence and a mapping; normalized extracts must say they were normalized. Historical TALOS-67375-HTTPS-BLOCK-03 uses that canonical publication name and normalized working journal IDs, with its original source mapping retained.

For a fresh reproduction, park the lab with LAB-HTTPS-ALLOW disabled, Scope/Bypass and old experimental decryption rules disabled, HTTP exception disabled, original Python restored/listening and normal traffic confirmed. Do not detach or replace a shared normal-traffic policy during cleanup. Our historical environment restored its earlier original LAB-IPS/experimental-rule baseline; that legacy state is not a requirement for this Resign-only build.

15. Read the historical results correctly

  • HTTPS-ALERT-01: marker port 33615 at 2000:1000001:2 Alert; clean 53867 and URI-negative 60135 without marker events; actual Resign; all expected responses.
  • HTTPS-BLOCK-02: marker port 42203 at the exact local SID Block and no HTTP response; clean 36221 and URI-negative 42563 unaffected; matching origin context and cleanup confirmed.
  • TALOS-67360-HTTPS-BLOCK-01: eleven upload notifications over 0.49295092 seconds, first ten completed; candidate eleven, port 51317, at 1:67360:2 Block; two negative controls passed; actual Resign and cleanup confirmed.
  • TALOS-67375-HTTPS-BLOCK-03: candidate port 33169 at 1:67375:1 Block, client reports zero response bytes; negative ports 59431, 39799 and 33101 complete the expected HTTP 200 responses and have no SID events; origin wrote all four responses; recovery and cleanup confirmed by operator.

These are supplied native-console/journal/FMC observations and operator confirmations. Full later HTTPS native archives and event/deployment snapshots have not all been independently reviewed. The last cookie intrusion detail shows TLS Status Decrypt (Resign); per-connection named-rule details for that batch were not supplied. Its four clients did verify Resign TLS. The cookie result is not an independently inspected HTTPS response.bin hash claim.

The authoritative result records and normalized/source-label mapping are listed in the companion evidence index. None of these controls establishes a real PHP memory leak, execution, invalid-backend-certificate rejection, global fail-closed behavior, general Internet-client compatibility or production capacity. Those require distinct experiments.

References

  1. Cisco self-hosted FMC decryption guide

  2. Cisco interface procedure for our recorded 1210 example

  3. Cisco NAT guidance

  4. Cisco rule guidelines

  5. Cisco cloud-delivered FMC traffic decryption

  6. Cisco certificate-status condition examples

  7. Snort rate condition

  8. Snort cookie buffers

Filed under

One response to “Lab Guide for Snort Inspection Through TLS Resign Decryption”

Leave a Reply

Your email address will not be published. Required fields are marked *

Verified by MonsterInsights