{"record":{"id":"504c503b39409084","repo":"rancher/rancher","slug":"current-time-s-is-before-notbefore-s","errorCode":null,"errorMessage":"current time %s is before NotBefore %s","messagePattern":"current time (.+?) is before NotBefore (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"pkg/auth/providers/saml/saml_client.go","lineNumber":702,"sourceCode":"\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not parse Rancher server URL: %w\", err)\n\t}\n\tif parsed.Host != rancherParsed.Host {\n\t\treturn \"\", fmt.Errorf(\"redirect URL host %q does not match Rancher host %q\", parsed.Host, rancherParsed.Host)\n\t}\n\n\treturn redirectURL, nil\n}\n\n// checkAssertionTimeConditions returns an error if now falls outside the\n// assertion's [NotBefore, NotOnOrAfter) validity window. A nil or zero-valued\n// bound is treated as unbounded on that side.\nfunc checkAssertionTimeConditions(now time.Time, conditions *saml.Conditions) error {\n\tif conditions == nil {\n\t\treturn nil\n\t}\n\tif !conditions.NotBefore.IsZero() && now.Before(conditions.NotBefore) {\n\t\treturn fmt.Errorf(\"current time %s is before NotBefore %s\", now, conditions.NotBefore)\n\t}\n\tif !conditions.NotOnOrAfter.IsZero() && !now.Before(conditions.NotOnOrAfter) {\n\t\treturn fmt.Errorf(\"current time %s is on or after NotOnOrAfter %s\", now, conditions.NotOnOrAfter)\n\t}\n\n\treturn nil\n}\n\n// assertionCache tracks recently seen SAML assertion IDs to prevent replay attacks.\ntype assertionCache struct {\n\tmu      sync.Mutex\n\tentries map[string]time.Time // assertion ID -> expiry time\n}\n\nfunc newAssertionCache() *assertionCache {\n\treturn &assertionCache{\n\t\tentries: make(map[string]time.Time),\n\t}","sourceCodeStart":684,"sourceCodeEnd":720,"githubUrl":"https://github.com/rancher/rancher/blob/932558d4e68565aff2d2f36e89ec4a391b06e7c5/pkg/auth/providers/saml/saml_client.go#L684-L720","documentation":"checkAssertionTimeConditions enforces the SAML assertion validity window [NotBefore, NotOnOrAfter) from the IdP response Conditions. This branch fires when Rancher's clock (or the time passed in tests) is strictly before NotBefore, meaning the assertion is not yet valid — almost always clock skew where the IdP's time is ahead of the Rancher server's time, since the IdP typically stamps NotBefore at (or slightly before) its own now.","triggerScenarios":"SAML ACS callback processed within the skew window: Rancher clock behind the IdP clock by more than the IdP's not-before skew margin; an IdP that sets NotBefore in the future by policy; tests calling checkAssertionTimeConditions with a fixed now earlier than the fixture's NotBefore.","commonSituations":"VMs or containers without NTP synchronization; IdP servers with drifted clocks; multi-cluster setups where Rancher and the IdP live on hosts with different time sources; daylight-saving or timezone misconfiguration on the host.","solutions":["Synchronize clocks: enable NTP/chrony on both the Rancher server and the IdP host, then confirm with 'date' or 'timedatectl' on each","Inspect the raw SAML response XML to compare its NotBefore stamp against local time and quantify the skew","Configure the IdP to apply a notBefore skew margin (e.g. ADFS/ Shibboleak 'notBeforeSkew') that covers observed drift","Retry the login once clocks are aligned — the fresh assertion will carry updated timestamps"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"if err := sp.validateAssertion(now, assertion); err != nil {\n    if strings.Contains(err.Error(), \"before NotBefore\") && time.Until(notBefore) < 2*time.Minute {\n        // Skew window: wait until the assertion becomes valid, then re-validate once\n        time.Sleep(time.Until(notBefore))\n        return sp.validateAssertion(time.Now(), assertion)\n    }\n    return err\n}","preventionTips":["Run NTP/chrony on Rancher hosts and IdP hosts; alert on drift beyond a few seconds","Configure a notBefore skew margin on the IdP to absorb residual drift","Monitor for this error as a clock-health signal, not a SAML problem"],"tags":["saml","clock-skew","assertion","authentication","ntp"],"backgroundTag":null,"analyzedSha":"932558d4e68565aff2d2f36e89ec4a391b06e7c5","analyzedAt":"2026-08-16T04:37:02.125Z","schemaVersion":2},"datasetVersion":"2026-08-16T08:17:34.114Z"}