knadh/listmonk · error

error getting SNS cert: %v

Error message

error getting SNS cert: %v

What it means

verifyNotif wraps any error from getCert when retrieving the SNS signing certificate referenced by the notification's SigningCertURL. getCert can fail for URL validation, HTTP fetch errors, non-200 responses, or PEM/x509 parse errors; all surface as this wrapped message. Without the certificate, the library cannot verify the notification's signature.

Source

Thrown at internal/bounce/webhooks/ses.go:205

	}

	b.WriteString("Timestamp" + "\n" + n.Timestamp + "\n")

	if n.Token != "" {
		b.WriteString("Token" + "\n" + n.Token + "\n")
	}
	b.WriteString("TopicArn" + "\n" + n.TopicArn + "\n")
	b.WriteString("Type" + "\n" + n.Type + "\n")

	return b.Bytes()
}

// verifyNotif verifies the signature on a notification payload.
func (s *SES) verifyNotif(n sesNotif) error {
	// Get the message signing certificate.
	cert, err := s.getCert(n.SigningCertURL)
	if err != nil {
		return fmt.Errorf("error getting SNS cert: %v", err)
	}

	sign, err := base64.StdEncoding.DecodeString(n.Signature)
	if err != nil {
		return err
	}

	return cert.CheckSignature(x509.SHA1WithRSA, s.buildSignature(n), sign)
}

// getCert takes the SNS certificate URL and fetches it and caches it for the first time,
// and returns the cached cert for subsequent calls.
func (s *SES) getCert(certURL string) (*x509.Certificate, error) {
	// Ensure that the cert URL is Amazon's.
	u, err := url.Parse(certURL)
	if err != nil {
		return nil, err
	}

View on GitHub (pinned to 670c01717d)

Solutions

  1. Unwrap the error to see the root cause (%v prints the inner getCert error, e.g. 'invalid SNS certificate URL' vs a network error).
  2. Verify outbound network access to the certificate host (curl the SigningCertURL from the server).
  3. Confirm the SES/SNS notification includes a valid SigningCertURL pointing at sns.<region>.amazonaws.com.
  4. If transient, SNS will redeliver the notification — ensure your endpoint returns 5xx on this error so SNS retries instead of swallowing it.
  5. Check the cert cache is not serving stale entries and that getCert's HTTP fetch and PEM parsing handle your region's cert format.

Example fix

// before: opaque wrap loses context for ops
return fmt.Errorf("error getting SNS cert: %v", err)
// after: return the error unwrapped and classify for retry
if err := ...; err != nil {
  return fmt.Errorf("error getting SNS cert: %w", err) // use %w so errors.Is/As work
}
Defensive patterns

Strategy: retry

Validate before calling

u, err := url.Parse(notif.SigningCertURL)
if err != nil || u.Host == "" {
  return errors.New("notification missing SigningCertURL")
}
resp, err := http.Head(notif.SigningCertURL)
if err != nil || resp.StatusCode != http.StatusOK {
  return fmt.Errorf("cert URL unreachable: %s", notif.SigningCertURL)
}

Type guard

func hasValidCertURL(n sesNotif) bool {
  u, err := url.Parse(n.SigningCertURL)
  return err == nil && u.Scheme == "https" && strings.HasSuffix(u.Host, ".amazonaws.com")
}

Try / catch

if err := ses.ProcessBounce(notif); err != nil {
  if strings.Contains(err.Error(), "error getting SNS cert") {
    // transient network/cert fetch issue: return 5xx so SNS redelivers
    log.Error("cert fetch failed, will let SNS retry", "err", err)
    http.Error(w, "temporary failure", http.StatusServiceUnavailable)
    return
  }
  http.Error(w, "bad request", http.StatusBadRequest)
}

Prevention

When it happens

Trigger: Called from ProcessSubscription or ProcessBounce when the notification's SigningCertURL is malformed or not an Amazon URL (invalid SNS certificate URL), the HTTPS fetch to sns.amazonaws.com fails (network/DNS outage), Amazon returns non-200, or the body is not a valid PEM certificate.

Common situations: Corporate egress firewall blocking outbound HTTPS to sns.<region>.amazonaws.com; forged/spoofed webhook posts with attacker-controlled SigningCertURL (rejected here); Amazon rotating/cert URL 404ing transiently; empty SigningCertURL in hand-crafted test payloads.

Related errors


AI-assisted analysis of knadh/listmonk@670c01717d (2026-09-01). Data as JSON: /api/errors/8d9793c2458a2630. Report an issue: GitHub.