diff --git a/caddy/Dockerfile.caddy b/caddy/Dockerfile.caddy index 66d36f1..3043d40 100644 --- a/caddy/Dockerfile.caddy +++ b/caddy/Dockerfile.caddy @@ -7,7 +7,16 @@ RUN xcaddy build ${CADDY_VERSION} \ --with github.com/caddy-dns/cloudflare \ --with github.com/caddyserver/replace-response +# Autodiscover Handler in Go bauen (Go ist im Builder-Image bereits verfügbar) +COPY autodiscover-handler.go /src/autodiscover-handler.go +WORKDIR /src +RUN CGO_ENABLED=0 go build -ldflags="-s -w" -o /usr/bin/autodiscover-handler autodiscover-handler.go + FROM caddy:${CADDY_VERSION} COPY --from=builder /usr/bin/caddy /usr/bin/caddy -RUN mkdir -p /var/log/caddy +COPY --from=builder /usr/bin/autodiscover-handler /usr/local/bin/autodiscover-handler +COPY start.sh /usr/local/bin/start.sh +RUN chmod +x /usr/local/bin/start.sh /usr/local/bin/autodiscover-handler \ + && mkdir -p /var/log/caddy +CMD ["/usr/local/bin/start.sh"] \ No newline at end of file diff --git a/caddy/autodiscover-handler.go b/caddy/autodiscover-handler.go new file mode 100644 index 0000000..3e44a81 --- /dev/null +++ b/caddy/autodiscover-handler.go @@ -0,0 +1,109 @@ +package main + +import ( + "fmt" + "io" + "log" + "net/http" + "regexp" + "strings" +) + +const port = "8280" + +var emailRegex = regexp.MustCompile(`(?i)([^<]+)`) + +func main() { + http.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, "OK") + }) + + http.HandleFunc("/autodiscover/autodiscover.xml", handleAutodiscover) + // Outlook sendet manchmal mit Großbuchstaben + http.HandleFunc("/Autodiscover/Autodiscover.xml", handleAutodiscover) + http.HandleFunc("/AutoDiscover/AutoDiscover.xml", handleAutodiscover) + + log.Printf("[autodiscover] Listening on port %s", port) + if err := http.ListenAndServe(":"+port, nil); err != nil { + log.Fatal(err) + } +} + +func handleAutodiscover(w http.ResponseWriter, r *http.Request) { + var email string + + if r.Method == http.MethodPost { + body, err := io.ReadAll(r.Body) + if err == nil { + if match := emailRegex.FindStringSubmatch(string(body)); len(match) > 1 { + email = strings.TrimSpace(match[1]) + } + } + r.Body.Close() + } + + var domain string + if email != "" { + parts := strings.SplitN(email, "@", 2) + if len(parts) == 2 { + domain = parts[1] + } + } + if domain == "" { + domain = extractDomainFromHost(r.Host) + } + + log.Printf("[autodiscover] %s from %s - email=%q domain=%s", r.Method, r.RemoteAddr, email, domain) + + w.Header().Set("Content-Type", "application/xml") + w.WriteHeader(http.StatusOK) + fmt.Fprint(w, buildResponse(domain, email)) +} + +func extractDomainFromHost(host string) string { + // Strip port + if idx := strings.Index(host, ":"); idx >= 0 { + host = host[:idx] + } + parts := strings.Split(host, ".") + if len(parts) >= 3 && strings.EqualFold(parts[0], "autodiscover") { + return strings.Join(parts[1:], ".") + } + if len(parts) >= 2 { + return strings.Join(parts[len(parts)-2:], ".") + } + return host +} + +func buildResponse(domain, loginName string) string { + return fmt.Sprintf(` + + + + email + settings + + IMAP + imap.%s + 993 + off + %s + off + on + on + + + SMTP + smtp.%s + 465 + off + %s + off + on + on + + + +`, domain, loginName, domain, loginName) +} \ No newline at end of file diff --git a/caddy/start.sh b/caddy/start.sh new file mode 100644 index 0000000..e888840 --- /dev/null +++ b/caddy/start.sh @@ -0,0 +1,8 @@ +#!/bin/sh +set -e + +# Autodiscover handler im Hintergrund starten +/usr/local/bin/autodiscover-handler & + +# Caddy im Vordergrund +exec caddy run --config /etc/caddy/Caddyfile --adapter caddyfile \ No newline at end of file diff --git a/caddy/update-caddy-certs.sh b/caddy/update-caddy-certs.sh index 7196e42..5d3485d 100755 --- a/caddy/update-caddy-certs.sh +++ b/caddy/update-caddy-certs.sh @@ -78,36 +78,7 @@ OUTPUT="${OUTPUT}(email_settings) {\n" # --- 1. Outlook Classic Autodiscover (POST + GET XML) --- OUTPUT="${OUTPUT} # Outlook Autodiscover (XML) - POST und GET\n" OUTPUT="${OUTPUT} route /autodiscover/autodiscover.xml {\n" -OUTPUT="${OUTPUT} header Content-Type \"application/xml\"\n" -OUTPUT="${OUTPUT} respond \`\n" -OUTPUT="${OUTPUT}\n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} email\n" -OUTPUT="${OUTPUT} settings\n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} IMAP\n" -OUTPUT="${OUTPUT} imap.{labels.1}.{labels.0}\n" -OUTPUT="${OUTPUT} 993\n" -OUTPUT="${OUTPUT} off\n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} off\n" -OUTPUT="${OUTPUT} on\n" -OUTPUT="${OUTPUT} on\n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} SMTP\n" -OUTPUT="${OUTPUT} smtp.{labels.1}.{labels.0}\n" -OUTPUT="${OUTPUT} 465\n" -OUTPUT="${OUTPUT} off\n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} off\n" -OUTPUT="${OUTPUT} on\n" -OUTPUT="${OUTPUT} on\n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT} \n" -OUTPUT="${OUTPUT}\` 200\n" +OUTPUT="${OUTPUT} reverse_proxy localhost:8280\n" OUTPUT="${OUTPUT} }\n" OUTPUT="${OUTPUT}\n"