Self-contained HTTP/HTTPS/TCP/UDP reverse proxy manager written in Go, with SQLite storage, a Bootstrap 5 admin UI (Go templates, no JS framework), and a REST API under /api/v1. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01QTXFrNP1imCW7jtfL5SZrw
57 lines
1.5 KiB
Go
57 lines
1.5 KiB
Go
package certificates
|
|
|
|
import (
|
|
"net/http"
|
|
"strings"
|
|
"sync"
|
|
)
|
|
|
|
// httpChallengeProvider implements lego's challenge.Provider for HTTP-01,
|
|
// backed by an in-memory token map served by our own HTTP listener at
|
|
// /.well-known/acme-challenge/. This avoids spinning up a second server on
|
|
// port 80 — the main proxy HTTP listener delegates to ServeHTTP below.
|
|
type httpChallengeProvider struct {
|
|
mu sync.RWMutex
|
|
tokens map[string]string // token -> key authorization
|
|
}
|
|
|
|
func newHTTPChallengeProvider() *httpChallengeProvider {
|
|
return &httpChallengeProvider{tokens: map[string]string{}}
|
|
}
|
|
|
|
func (p *httpChallengeProvider) Present(domain, token, keyAuth string) error {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
p.tokens[token] = keyAuth
|
|
return nil
|
|
}
|
|
|
|
func (p *httpChallengeProvider) CleanUp(domain, token, keyAuth string) error {
|
|
p.mu.Lock()
|
|
defer p.mu.Unlock()
|
|
delete(p.tokens, token)
|
|
return nil
|
|
}
|
|
|
|
const acmeChallengePrefix = "/.well-known/acme-challenge/"
|
|
|
|
// ServeHTTP handles ACME HTTP-01 challenge requests. Returns true if the
|
|
// request was an ACME challenge (handled or not found), false if the
|
|
// caller should continue normal routing.
|
|
func (p *httpChallengeProvider) ServeHTTP(w http.ResponseWriter, r *http.Request) bool {
|
|
if !strings.HasPrefix(r.URL.Path, acmeChallengePrefix) {
|
|
return false
|
|
}
|
|
token := strings.TrimPrefix(r.URL.Path, acmeChallengePrefix)
|
|
p.mu.RLock()
|
|
keyAuth, ok := p.tokens[token]
|
|
p.mu.RUnlock()
|
|
if !ok {
|
|
http.NotFound(w, r)
|
|
return true
|
|
}
|
|
w.Header().Set("Content-Type", "text/plain")
|
|
w.Write([]byte(keyAuth))
|
|
return true
|
|
}
|