Skip to content

Commit c479cfd

Browse files
jrauh01julianbrost
authored andcommitted
Handle debug endpoints in extra http.ServeMux
1 parent 83656e1 commit c479cfd

File tree

2 files changed

+35
-36
lines changed

2 files changed

+35
-36
lines changed

doc/20-HTTP-API.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,28 +43,29 @@ EOF
4343
## Debugging Endpoints
4444

4545
There are multiple endpoints for dumping specific configurations.
46+
All of them are prefixed by `/debug`.
4647
To use those, the `debug-password` must be set and supplied via HTTP Basic Authentication next to an arbitrary username.
4748

4849
### Dump Config
4950

5051
The database-stored configuration from Icinga Notifications current viewpoint can be dumped as JSON.
5152

5253
```
53-
curl -v -u ':debug-password' 'http://localhost:5680/dump-config'
54+
curl -v -u ':debug-password' 'http://localhost:5680/debug/dump-config'
5455
```
5556

5657
### Dump Incidents
5758

5859
The current incidents can be dumped as JSON.
5960

6061
```
61-
curl -v -u ':debug-password' 'http://localhost:5680/dump-incidents'
62+
curl -v -u ':debug-password' 'http://localhost:5680/debug/dump-incidents'
6263
```
6364

6465
### Dump Schedules
6566

6667
All schedules with their assignee can be dumped in a human-readable form.
6768

6869
```
69-
curl -v -u ':debug-password' 'http://localhost:5680/dump-schedules'
70+
curl -v -u ':debug-password' 'http://localhost:5680/debug/dump-schedules'
7071
```

internal/listener/listener.go

Lines changed: 31 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,14 @@ func NewListener(db *database.DB, runtimeConfig *config.RuntimeConfig, logs *log
3434
logs: logs,
3535
runtimeConfig: runtimeConfig,
3636
}
37+
38+
debugMux := http.NewServeMux()
39+
debugMux.HandleFunc("/dump-config", l.DumpConfig)
40+
debugMux.HandleFunc("/dump-incidents", l.DumpIncidents)
41+
debugMux.HandleFunc("/dump-schedules", l.DumpSchedules)
42+
43+
l.mux.Handle("/debug/", http.StripPrefix("/debug", l.requireDebugAuth(debugMux)))
3744
l.mux.HandleFunc("/process-event", l.ProcessEvent)
38-
l.mux.HandleFunc("/dump-config", l.DumpConfig)
39-
l.mux.HandleFunc("/dump-incidents", l.DumpIncidents)
40-
l.mux.HandleFunc("/dump-schedules", l.DumpSchedules)
4145
return l
4246
}
4347

@@ -150,57 +154,54 @@ func (l *Listener) ProcessEvent(w http.ResponseWriter, req *http.Request) {
150154
_, _ = fmt.Fprintln(w)
151155
}
152156

153-
// checkDebugPassword checks if the valid debug password was provided. If there is no password configured or the
154-
// supplied password is incorrect, it sends an error code and returns false. True is returned if access is allowed.
155-
func (l *Listener) checkDebugPassword(w http.ResponseWriter, r *http.Request) bool {
156-
expectedPassword := daemon.Config().DebugPassword
157-
if expectedPassword == "" {
158-
w.WriteHeader(http.StatusForbidden)
159-
_, _ = fmt.Fprintln(w, "config dump disables, no debug-password set in config")
157+
// requireDebugAuth is a middleware that checks if the valid debug password was provided. If there is no password
158+
// configured or the supplied password is incorrect, it sends an error code and does not redirect the request.
159+
func (l *Listener) requireDebugAuth(next http.Handler) http.Handler {
160+
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
161+
expectedPassword := daemon.Config().DebugPassword
162+
if expectedPassword == "" {
163+
w.WriteHeader(http.StatusForbidden)
164+
_, _ = fmt.Fprintln(w, "config dump disabled, no debug-password set in config")
160165

161-
return false
162-
}
166+
return
167+
}
163168

164-
_, providedPassword, _ := r.BasicAuth()
165-
if subtle.ConstantTimeCompare([]byte(expectedPassword), []byte(providedPassword)) != 1 {
166-
l.logger.Warnw("Unauthorized request", zap.String("url", r.RequestURI))
169+
_, providedPassword, _ := r.BasicAuth()
170+
if subtle.ConstantTimeCompare([]byte(expectedPassword), []byte(providedPassword)) != 1 {
171+
l.logger.Warnw("Unauthorized request", zap.String("url", r.RequestURI))
167172

168-
w.Header().Set("WWW-Authenticate", `Basic realm="debug"`)
169-
w.WriteHeader(http.StatusUnauthorized)
170-
_, _ = fmt.Fprintln(w, "please provide the debug-password as basic auth credentials (user is ignored)")
171-
return false
172-
}
173+
w.Header().Set("WWW-Authenticate", `Basic realm="debug"`)
174+
w.WriteHeader(http.StatusUnauthorized)
175+
_, _ = fmt.Fprintln(w, "please provide the debug-password as basic auth credentials (user is ignored)")
176+
return
177+
}
173178

174-
return true
179+
next.ServeHTTP(w, r)
180+
})
175181
}
176182

183+
// DumpConfig is used as /debug prefixed endpoint to dump the current live configuration of the daemon.
184+
// The authorization has to be done beforehand.
177185
func (l *Listener) DumpConfig(w http.ResponseWriter, r *http.Request) {
178186
if r.Method != http.MethodGet {
179187
w.WriteHeader(http.StatusMethodNotAllowed)
180188
_, _ = fmt.Fprintln(w, "GET required")
181189
return
182190
}
183191

184-
if !l.checkDebugPassword(w, r) {
185-
return
186-
}
187-
188192
enc := json.NewEncoder(w)
189193
enc.SetIndent("", " ")
190194
_ = enc.Encode(&l.runtimeConfig.ConfigSet)
191195
}
192196

197+
// DumpIncidents is used as /debug prefixed endpoint to dump all incidents. The authorization has to be done beforehand.
193198
func (l *Listener) DumpIncidents(w http.ResponseWriter, r *http.Request) {
194199
if r.Method != http.MethodGet {
195200
w.WriteHeader(http.StatusMethodNotAllowed)
196201
_, _ = fmt.Fprintln(w, "GET required")
197202
return
198203
}
199204

200-
if !l.checkDebugPassword(w, r) {
201-
return
202-
}
203-
204205
incidents := incident.GetCurrentIncidents()
205206
encodedIncidents := make(map[int64]json.RawMessage)
206207

@@ -230,17 +231,14 @@ func (l *Listener) DumpIncidents(w http.ResponseWriter, r *http.Request) {
230231
_ = enc.Encode(encodedIncidents)
231232
}
232233

234+
// DumpSchedules is used as /debug prefixed endpoint to dump all schedules. The authorization has to be done beforehand.
233235
func (l *Listener) DumpSchedules(w http.ResponseWriter, r *http.Request) {
234236
if r.Method != http.MethodGet {
235237
w.WriteHeader(http.StatusMethodNotAllowed)
236238
_, _ = fmt.Fprintln(w, "GET required")
237239
return
238240
}
239241

240-
if !l.checkDebugPassword(w, r) {
241-
return
242-
}
243-
244242
l.runtimeConfig.RLock()
245243
defer l.runtimeConfig.RUnlock()
246244

0 commit comments

Comments
 (0)