From: Petre Rodan Subject: fix for mail/opensmtpd-filters/rspamd arbitrary headers To: ports@openbsd.org Date: Sun, 31 May 2026 07:46:00 +0300 hi! after a migration from postfix+rspamd to opensmtpd+rspamd I realized that the MTA no longer accepts the additional headers set by the rspamd rmilter module and thus spam emails can no longer be properly filtered later on my procmail setup. there is a pull request [1] on the rspamd repository that fixes the problem, I have been testing it for a week now and it works great. can we please include it as a patch into ports? this pull request has been ignored for more than 2 years now and other OS-es [2] also decided to go the route of including it as a patch instead of waiting for upstream. thank you, peter [1] https://github.com/poolpOrg/filter-rspamd/pull/52 [2] https://github.com/freebsd/freebsd-ports/commit/f1ae10256ba13f28aac1390e13675d9c97bf7a17 --- filter-rspamd.go.orig 2023-06-14 17:13:00 UTC +++ filter-rspamd.go @@ -135,7 +135,7 @@ func linkAuth(s *session, params []string) { var user, res string if version < "0.7" { - res = params[len(params) - 1] + res = params[len(params)-1] user = strings.Join(params[0:len(params)-1], "|") } else { res = params[0] @@ -479,48 +479,71 @@ func rspamdQuery(s *session, token string) { } if len(rr.Headers.Add) > 0 { - authHeaders := map[string]string{} + // Authentication headers from Rspamd are in the form of: + // "ARC-Seal": { "order": 1, "value": text } + // "ARC-Authentication-Results": { "order": 1, "value": text } + // "ARC-Message-Signature": { "order": 1, "value": text } + // Unfortunately they all have an order of 1, + // thought they should be ordered as per RFC 8617 + // so we give them a fixed order, and shift everything after + orderFix := map[string]int{ + "ARC-Seal": 0, + "ARC-Message-Signature": 1, + "ARC-Authentication-Results": 2, + } + // headers, indexed by order + headers := map[int]map[string]string{-1: {}} + for h, t := range rr.Headers.Add { + if h == "" { + continue + } + switch v := t.(type) { - /** - * Authentication headers from Rspamd are in the form of: - * ARC-Seal : { order : 1, value : text } - * ARC-Message-Signature : { order : 1, value : text } - * Unfortunately they all have an order of 1, so we - * make a map of them and print them in proper order. - */ + case string: + // Regular headers from Rspamd used to be plain strings + // continue to accept this old format, just in case + + headers[-1][h] = v + case map[string]interface{}: - if h != "" { - v, ok := v["value"].(string) - if ok { - authHeaders[h] = v + // They are now in the form { "value": text, "order": -1 } + // so we convert them, and use `order` as the map index + + vv, ok_v := v["value"].(string) + vo, ok_o := v["order"].(float64) + if ok_v && ok_o { + vo := int(vo) + if vo > -1 { + o_fix, ok := orderFix[h] + if ok { + vo = o_fix + } else { + // if not in orderFix, add after + vo = vo + len(orderFix) + } } + + if _, ok := headers[vo]; !ok { + headers[vo] = map[string]string{} + } + headers[vo][h] = vv } - /** - * Regular X-Spam headers from Rspamd are plain strings. - * Insert these at the top. - */ - case string: - writeHeader(s, token, h, v) + default: } } - /** - * Prefix auth headers to incoming mail in proper order. - */ - if len(authHeaders) > 0 { - hdrs := []string{ - "ARC-Seal", - "ARC-Message-Signature", - "ARC-Authentication-Results", - "Authentication-Results"} - - for _, h := range hdrs { - if authHeaders[h] != "" { - writeHeader(s, token, h, authHeaders[h]) - } + // add the headers in order + orders := make([]int, 0, len(headers)) + for k := range headers { + orders = append(orders, k) + } + sort.Ints(orders) + for _, o := range orders { + for h, v := range headers[o] { + writeHeader(s, token, h, v) } } }