1155
i--
1156
continue outer
1157
}
1158
- } else {
1158
+ } else if !automationPolicyMergeShadows(i, j, aps) {
1159
+ // merge only if it won't change which policy handles a name
1160
// avoid repeated subjects
1161
for _, subj := range aps[j].SubjectsRaw {
1162
if !slices.Contains(aps[i].SubjectsRaw, subj) {
1208
return -1
1209
}
1210
1211
+// automationPolicyMergeShadows returns true if merging aps[j] into aps[i]
1212
+// (where i < j) would change which policy handles a name. This happens
1213
+// when a subject of a policy between i and j overlaps with a subject of
1214
+// aps[j], in either direction.
1215
+//
1216
+// For example, say the policies are for "example.com" (i),
1217
+// "foo.example.com" (i+1), and "*.example.com" (j). Merging j into i moves
1218
+// "*.example.com" before the i+1 policy. Then the wildcard matches
1219
+// "foo.example.com" first, and the i+1 policy is never used.
1220
+//
1221
+// The reverse case matters too. If the wildcard is in i+1 and the name is
1222
+// in j, merging moves the name before the wildcard, and its policy changes.
1223
+// A merge should not change the result, so it is skipped here as well.
1224
+//
1225
+// This uses certmagic.MatchWildcard, which the TLS app also uses to pick
1226
+// a policy for a name.
1227
+func automationPolicyMergeShadows(i, j int, aps []*caddytls.AutomationPolicy) bool {
1228
+ for k := i + 1; k < j; k++ {
1229
+ for _, kSubj := range aps[k].SubjectsRaw {
1230
+ for _, jSubj := range aps[j].SubjectsRaw {
1231
+ if certmagic.MatchWildcard(kSubj, jSubj) ||
1232
+ certmagic.MatchWildcard(jSubj, kSubj) {
1233
+ return true
1234
+ }
1235
+ }
1236
+ }
1237
+ }
1238
+ return false
1239
+}
1240
+
1241
// subjectQualifiesForPublicCert is like certmagic.SubjectQualifiesForPublicCert() except
1242
// that this allows domains with multiple wildcard levels like '*.*.example.com' to qualify
1243
// if the automation policy has OnDemand enabled (i.e. this function is more lenient).