@@ -561,27 +561,12 @@ var _ = Describe("["+strings.ToLower(DesiredMode.String())+"-serial]", Serial, f
561561 // Delete if already exists
562562 client .Client .Secrets (pkg .PtpLinuxDaemonNamespace ).Delete (
563563 context .Background (),
564- "ptp-security-mismatch" ,
564+ testconfig . MismatchSecretName ,
565565 metav1.DeleteOptions {},
566566 )
567567 time .Sleep (2 * time .Second )
568568
569- // Create secret with spp 0 but DIFFERENT KEYS than ptp-security-conf
570- // GM signs with these keys, BC has different keys for spp 0 → signature mismatch
571- mismatchSecret := & v1core.Secret {
572- ObjectMeta : metav1.ObjectMeta {
573- Name : "ptp-security-mismatch" ,
574- Namespace : pkg .PtpLinuxDaemonNamespace ,
575- },
576- StringData : map [string ]string {
577- "ptp-security.conf" : `[security_association]
578- spp 0
579- 1 AES128 HEX:0000000000000000000000000000000000000000000000000000000000000000
580- 2 SHA256-128 HEX:FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
581- ` ,
582- },
583- }
584-
569+ mismatchSecret := testconfig .CreateMismatchSecret (pkg .PtpLinuxDaemonNamespace )
585570 _ , err := client .Client .Secrets (pkg .PtpLinuxDaemonNamespace ).Create (
586571 context .Background (),
587572 mismatchSecret ,
@@ -607,8 +592,8 @@ spp 0
607592 // Also change interface spp from 1 to 0
608593 ptp4lConf := * ptpConfig .Spec .Profile [0 ].Ptp4lConf
609594 modifiedConf := strings .Replace (ptp4lConf ,
610- "sa_file /etc/ptp-secret-mount/ptp-security-conf/ptp-security.conf" ,
611- "sa_file /etc/ptp-secret-mount/ptp-security-mismatch/ptp-security.conf" , 1 )
595+ pkg . SaFileSecurityConf ,
596+ pkg . SaFileMismatchConf , 1 )
612597 modifiedConf = strings .Replace (modifiedConf , "spp 1" , "spp 0" , 1 ) // Change first spp 1 to spp 0
613598 ptpConfig .Spec .Profile [0 ].Ptp4lConf = & modifiedConf
614599
@@ -687,7 +672,7 @@ spp 0
687672 // Delete mismatch secret
688673 client .Client .Secrets (pkg .PtpLinuxDaemonNamespace ).Delete (
689674 context .Background (),
690- "ptp-security-mismatch" ,
675+ testconfig . MismatchSecretName ,
691676 metav1.DeleteOptions {},
692677 )
693678
@@ -706,6 +691,107 @@ spp 0
706691 })
707692 })
708693
694+ // MITM attack test: verifies ptp4l logs authentication errors when receiving
695+ // packets signed with wrong key (corrupted ICV from slave's perspective)
696+ It ("PTP logs authentication error when receiving MITM packet with wrong ICV" , func () {
697+ authEnabled := os .Getenv ("PTP_AUTH_ENABLED" )
698+ if authEnabled != "true" {
699+ Skip ("MITM ICV test requires PTP_AUTH_ENABLED=true" )
700+ }
701+
702+ if fullConfig .PtpModeDesired == testconfig .TelcoGrandMasterClock {
703+ Skip ("Skipping - slave interface not available with WPC-GM" )
704+ }
705+
706+ slavePod := fullConfig .DiscoveredClockUnderTestPod
707+ Expect (slavePod ).NotTo (BeNil ())
708+ Expect (len (fullConfig .DiscoveredFollowerInterfaces )).To (BeNumerically (">" , 0 ))
709+ slaveInterface := fullConfig .DiscoveredFollowerInterfaces [0 ]
710+ slaveNodeName := slavePod .Spec .NodeName
711+
712+ By ("Verifying baseline sync" )
713+ err := metrics .CheckClockState (metrics .MetricClockStateLocked , slaveInterface , & slaveNodeName )
714+ Expect (err ).NotTo (HaveOccurred (), "Slave should be LOCKED before test" )
715+
716+ By ("Creating attacker secret with wrong key values" )
717+ _ = client .Client .Secrets (pkg .PtpLinuxDaemonNamespace ).Delete (
718+ context .Background (), testconfig .MITMAttackerSecretName , metav1.DeleteOptions {})
719+ time .Sleep (2 * time .Second )
720+
721+ mitmSecret := testconfig .CreateMITMAttackerSecret (pkg .PtpLinuxDaemonNamespace )
722+ _ , err = client .Client .Secrets (pkg .PtpLinuxDaemonNamespace ).Create (
723+ context .Background (), mitmSecret , metav1.CreateOptions {})
724+ Expect (err ).NotTo (HaveOccurred ())
725+
726+ var originalGMConfig * ptpv1.PtpConfig
727+ By ("Switching GM to attacker secret (packets will have wrong ICV)" , func () {
728+ ptpConfig , err := client .Client .PtpConfigs (pkg .PtpLinuxDaemonNamespace ).Get (
729+ context .Background (), pkg .PtpGrandMasterPolicyName , metav1.GetOptions {})
730+ Expect (err ).NotTo (HaveOccurred ())
731+ originalGMConfig = ptpConfig .DeepCopy ()
732+
733+ ptp4lConf := * ptpConfig .Spec .Profile [0 ].Ptp4lConf
734+ modifiedConf := strings .Replace (ptp4lConf ,
735+ pkg .SaFileSecurityConf ,
736+ pkg .SaFileMITMAttackerConf , 1 )
737+ ptpConfig .Spec .Profile [0 ].Ptp4lConf = & modifiedConf
738+
739+ _ , err = client .Client .PtpConfigs (pkg .PtpLinuxDaemonNamespace ).Update (
740+ context .Background (), ptpConfig , metav1.UpdateOptions {})
741+ Expect (err ).NotTo (HaveOccurred ())
742+ })
743+
744+ // Record time for log search
745+ configChangeTime := time .Now ()
746+
747+ By ("Waiting for GM to send packets with wrong ICV" )
748+ time .Sleep (30 * time .Second )
749+
750+ By ("Checking slave logs for authentication failure" )
751+ // ptp4l should log auth errors when ICV verification fails
752+ authErrorFound := false
753+ authPatterns := []string {"auth" , "Authentication" , "ICV" , "verification" }
754+ for _ , pattern := range authPatterns {
755+ matches , err := pods .GetPodLogsRegexSince (slavePod .Namespace , slavePod .Name ,
756+ pkg .PtpContainerName , pattern , true , 30 * time .Second , configChangeTime )
757+ if err == nil && len (matches ) > 0 {
758+ authErrorFound = true
759+ logrus .Infof ("Found auth error log with pattern '%s': %v" , pattern , matches [0 ])
760+ break
761+ }
762+ }
763+
764+ By ("Verifying slave detected bad packets (not in LOCKED or auth error logged)" )
765+ // Either we found auth error logs, or slave should no longer be locked
766+ if ! authErrorFound {
767+ err = metrics .CheckClockState (metrics .MetricClockStateLocked , slaveInterface , & slaveNodeName )
768+ if err != nil {
769+ logrus .Infof ("Slave lost sync due to MITM attack (expected behavior)" )
770+ } else {
771+ logrus .Warnf ("Slave still locked - auth errors may not be logged at current log level" )
772+ }
773+ } else {
774+ fmt .Fprintf (GinkgoWriter , "✓ Authentication error detected in logs\n " )
775+ }
776+
777+ By ("Restoring original GM config" , func () {
778+ Expect (originalGMConfig ).NotTo (BeNil ())
779+ originalGMConfig .SetResourceVersion ("" )
780+ _ = client .Client .PtpConfigs (pkg .PtpLinuxDaemonNamespace ).Delete (
781+ context .Background (), pkg .PtpGrandMasterPolicyName , metav1.DeleteOptions {})
782+ time .Sleep (pkg .Timeout10Seconds )
783+ _ , err := client .Client .PtpConfigs (pkg .PtpLinuxDaemonNamespace ).Create (
784+ context .Background (), originalGMConfig , metav1.CreateOptions {})
785+ Expect (err ).NotTo (HaveOccurred ())
786+ _ = client .Client .Secrets (pkg .PtpLinuxDaemonNamespace ).Delete (
787+ context .Background (), testconfig .MITMAttackerSecretName , metav1.DeleteOptions {})
788+ time .Sleep (pkg .TimeoutIn1Minute )
789+ ptphelper .WaitForPtpDaemonToExist ()
790+ })
791+
792+ logrus .Infof ("✓ MITM ICV test completed" )
793+ })
794+
709795 // Test That clock can sync in dual follower scenario when one port is down
710796 It ("Dual follower can sync when one follower port goes down" , func () {
711797 if fullConfig .PtpModeDesired != testconfig .DualFollowerClock {
0 commit comments