rancher/rancher · error

current time %s is before NotBefore %s

Error message

current time %s is before NotBefore %s

What it means

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.

Source

Thrown at pkg/auth/providers/saml/saml_client.go:702

	if err != nil {
		return "", fmt.Errorf("could not parse Rancher server URL: %w", err)
	}
	if parsed.Host != rancherParsed.Host {
		return "", fmt.Errorf("redirect URL host %q does not match Rancher host %q", parsed.Host, rancherParsed.Host)
	}

	return redirectURL, nil
}

// checkAssertionTimeConditions returns an error if now falls outside the
// assertion's [NotBefore, NotOnOrAfter) validity window. A nil or zero-valued
// bound is treated as unbounded on that side.
func checkAssertionTimeConditions(now time.Time, conditions *saml.Conditions) error {
	if conditions == nil {
		return nil
	}
	if !conditions.NotBefore.IsZero() && now.Before(conditions.NotBefore) {
		return fmt.Errorf("current time %s is before NotBefore %s", now, conditions.NotBefore)
	}
	if !conditions.NotOnOrAfter.IsZero() && !now.Before(conditions.NotOnOrAfter) {
		return fmt.Errorf("current time %s is on or after NotOnOrAfter %s", now, conditions.NotOnOrAfter)
	}

	return nil
}

// assertionCache tracks recently seen SAML assertion IDs to prevent replay attacks.
type assertionCache struct {
	mu      sync.Mutex
	entries map[string]time.Time // assertion ID -> expiry time
}

func newAssertionCache() *assertionCache {
	return &assertionCache{
		entries: make(map[string]time.Time),
	}

View on GitHub (pinned to 932558d4e6)

Solutions

  1. Synchronize clocks: enable NTP/chrony on both the Rancher server and the IdP host, then confirm with 'date' or 'timedatectl' on each
  2. Inspect the raw SAML response XML to compare its NotBefore stamp against local time and quantify the skew
  3. Configure the IdP to apply a notBefore skew margin (e.g. ADFS/ Shibboleak 'notBeforeSkew') that covers observed drift
  4. Retry the login once clocks are aligned — the fresh assertion will carry updated timestamps
Defensive patterns

Strategy: retry

Try / catch

if err := sp.validateAssertion(now, assertion); err != nil {
    if strings.Contains(err.Error(), "before NotBefore") && time.Until(notBefore) < 2*time.Minute {
        // Skew window: wait until the assertion becomes valid, then re-validate once
        time.Sleep(time.Until(notBefore))
        return sp.validateAssertion(time.Now(), assertion)
    }
    return err
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of rancher/rancher@932558d4e6 (2026-08-16). Data as JSON: /api/errors/504c503b39409084. Report an issue: GitHub.