Add API key authentication for proxy endpoints.

Support multiple API keys from config, env, and CLI, enforce auth on non-public endpoints, and pass keys through remote deploy verification.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
GitHub Actions
2026-05-08 13:08:27 +08:00
parent c1a0fe2949
commit 450faefaf9
8 changed files with 229 additions and 2 deletions

View File

@@ -126,7 +126,7 @@ func NewServer(addr string, svc *service.Service) *Server {
s.http = &http.Server{
Addr: addr,
Handler: s.withRecorder(withCORS(mux)),
Handler: s.withRecorder(withCORS(s.withAuth(mux))),
ReadHeaderTimeout: 10 * time.Second,
}
return s
@@ -1686,6 +1686,65 @@ func writeOpenAIError(w http.ResponseWriter, status int, kind string, message st
})
}
func isPublicPath(path string) bool {
switch path {
case "/", "/health", "/runtime/status", "/v1/runtime/status":
return true
default:
return false
}
}
func isAnthropicPath(path string) bool {
switch path {
case "/v1/messages", "/v1/messages/count_tokens":
return true
default:
return false
}
}
func extractAPIKey(r *http.Request) string {
if value := strings.TrimSpace(r.Header.Get("x-api-key")); value != "" {
return value
}
auth := strings.TrimSpace(r.Header.Get("Authorization"))
if auth == "" {
return ""
}
parts := strings.SplitN(auth, " ", 2)
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
return ""
}
return strings.TrimSpace(parts[1])
}
func (s *Server) withAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if isPublicPath(r.URL.Path) {
next.ServeHTTP(w, r)
return
}
keys := s.svc.APIKeys()
if len(keys) == 0 {
next.ServeHTTP(w, r)
return
}
provided := extractAPIKey(r)
for _, key := range keys {
if provided == key {
next.ServeHTTP(w, r)
return
}
}
if isAnthropicPath(r.URL.Path) {
writeAnthropicError(w, http.StatusUnauthorized, "authentication_error", "invalid or missing API key")
return
}
writeOpenAIError(w, http.StatusUnauthorized, "authentication_error", "invalid or missing API key")
})
}
func streamingHeaders(w http.ResponseWriter) {
w.Header().Set("Content-Type", "text/event-stream")
w.Header().Set("Cache-Control", "no-cache")