Skip to content

Commit

Permalink
cleanup
Browse files Browse the repository at this point in the history
  • Loading branch information
metachris committed Nov 14, 2024
1 parent f1216c9 commit 42e0f6a
Show file tree
Hide file tree
Showing 6 changed files with 55 additions and 25 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,4 +21,5 @@
/build
/cert.pem
/key.pem
/pipe.fifo
/pipe.fifo
/basic-auth-secret.txt
22 changes: 14 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,19 +106,25 @@ The config file ([systemapi-config.toml](./systemapi-config.toml)) includes a `b
- If the file exists and is not empty, then the APIs are authenticated for passwords that match the hash in this file.

```bash
# Set `basic_auth_secret_path` in the config file and create it empty
touch .basic-auth-secret
# Create an empty secrets file and enable `basic_auth_secret_path` in the config file
touch basic-auth-secret.txt
vi systemapi-config.toml

# Start the server,
$ go run cmd/system-api/main.go --config systemapi-config.toml
go run cmd/system-api/main.go --config systemapi-config.toml

# Initially, requests are unauthenticated
$ curl localhost:3535/livez
curl -v localhost:3535/livez

# Set the basic auth secret. From here on, authentication is required for all API requests.
curl -d "foobar" localhost:3535/api/v1/set-basic-auth

# Fails with '401 Unauthorized' because no basic auth credentials are provided
curl -v localhost:3535/livez

# Set the basic auth secret
$ curl -d "foobar" localhost:3535/api/v1/set-basic-auth
# Works if correct basic auth credentials are provided
curl -v -u admin:foobar localhost:3535/livez

# Now requests are authenticated
$ curl -u admin:foobar -v localhost:3535/livez
# The update also shows up in the logs:
curl -u admin:foobar localhost:3535/logs
```
5 changes: 3 additions & 2 deletions systemapi-config.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
[general]
listen_addr = "0.0.0.0:3535"
pipe_file = "pipe.fifo"
pprof = true
log_json = false
log_debug = true

# The path to the secret file used for basic auth
basic_auth_secret_path = "/tmp/basic_auth_secret"
# Enable HTTP Basic auth by setting a file for the hashed secret
basic_auth_secret_path = "basic-auth-secret.txt"

[actions]
# reboot = "reboot"
Expand Down
9 changes: 5 additions & 4 deletions systemapi/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,11 @@ import (
)

type systemAPIConfigGeneral struct {
ListenAddr string `toml:"listen_addr"`
PipeFile string `toml:"pipe_file"`
LogJSON bool `toml:"log_json"`
LogDebug bool `toml:"log_debug"`
ListenAddr string `toml:"listen_addr"`
PipeFile string `toml:"pipe_file"`
LogJSON bool `toml:"log_json"`
LogDebug bool `toml:"log_debug"`
EnablePprof bool `toml:"pprof"` // Enables pprof endpoints

BasicAuthSecretPath string `toml:"basic_auth_secret_path"`
}
Expand Down
20 changes: 15 additions & 5 deletions systemapi/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,8 @@ import (
)

type HTTPServerConfig struct {
Config *SystemAPIConfig
Log *httplog.Logger
EnablePprof bool
Config *SystemAPIConfig
Log *httplog.Logger

DrainDuration time.Duration
GracefulShutdownDuration time.Duration
Expand Down Expand Up @@ -104,22 +103,32 @@ func (s *Server) getRouter() http.Handler {

mux.Use(httplog.RequestLogger(s.log))
mux.Use(middleware.Recoverer)

// Enable a custom HTTP Basic Auth middleware
mux.Use(BasicAuth("system-api", s.getBasicAuthHashedCredentials))

// Common APIs
mux.Get("/", s.handleLivenessCheck)
mux.Get("/livez", s.handleLivenessCheck)

// Event (log) APIs
mux.Get("/api/v1/new_event", s.handleNewEvent)
mux.Get("/api/v1/events", s.handleGetEvents)
mux.Get("/logs", s.handleGetLogs)

// API to set the basic auth secret
mux.Post("/api/v1/set-basic-auth", s.handleSetBasicAuthCreds)

// API to trigger an action
mux.Get("/api/v1/actions/{action}", s.handleAction)

// API to upload a file
mux.Post("/api/v1/file-upload/{file}", s.handleFileUpload)

if s.cfg.EnablePprof {
s.log.Info("pprof API enabled")
// Optionally, pprof
if s.cfg.Config.General.EnablePprof {
mux.Mount("/debug", middleware.Profiler())
s.log.Info("pprof API enabled: /debug/pprof/")
}

return mux
Expand Down Expand Up @@ -353,5 +362,6 @@ func (s *Server) handleSetBasicAuthCreds(w http.ResponseWriter, r *http.Request)

s.basicAuthHash = secretHash
s.log.Info("Basic auth secret updated")
s.addInternalEvent(fmt.Sprintf("basic auth secret updated. new hash: %s", secretHash))

Check failure on line 365 in systemapi/server.go

View workflow job for this annotation

GitHub Actions / Lint

fmt.Sprintf can be replaced with string concatenation (perfsprint)
w.WriteHeader(http.StatusOK)
}
21 changes: 16 additions & 5 deletions systemapi/server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ func execRequest(t *testing.T, router http.Handler, method, url string, requestB
return execRequestAuth(t, router, method, url, requestBody, "", "")
}

// Helper to create prepared executors for specific API endpoints
func makeRequestExecutor(t *testing.T, router http.Handler, method, url string) func(basicAuthUser, basicAuthPass string, requestBody io.Reader) (statusCode int, responsePayload []byte) {
// Helper to create prepared test runners for specific API endpoints
func createRequestRunner(t *testing.T, router http.Handler, method, url string) func(basicAuthUser, basicAuthPass string, requestBody io.Reader) (statusCode int, responsePayload []byte) {
t.Helper()
return func(basicAuthUser, basicAuthPass string, requestBody io.Reader) (statusCode int, responsePayload []byte) {
return execRequestAuth(t, router, method, url, requestBody, basicAuthUser, basicAuthPass)
Expand All @@ -69,7 +69,7 @@ func TestGeneralHandlers(t *testing.T) {
code, _ := execRequest(t, router, http.MethodGet, "/livez", nil)
require.Equal(t, http.StatusOK, code)

// Test /api/v1/events
// /api/v1/events is initially empty
code, respBody := execRequest(t, router, http.MethodGet, "/api/v1/events", nil)
require.Equal(t, http.StatusOK, code)
require.Equal(t, "[]\n", string(respBody))
Expand All @@ -78,6 +78,17 @@ func TestGeneralHandlers(t *testing.T) {
code, _ = execRequest(t, router, http.MethodGet, "/api/v1/new_event?message=foo", nil)
require.Equal(t, http.StatusOK, code)
require.Len(t, srv.events, 1)
require.Equal(t, "foo", srv.events[0].Message)

// /api/v1/events now has an entry
code, respBody = execRequest(t, router, http.MethodGet, "/api/v1/events", nil)
require.Equal(t, http.StatusOK, code)
require.Contains(t, string(respBody), "foo")

// /logs should also work
code, respBody = execRequest(t, router, http.MethodGet, "/logs", nil)
require.Equal(t, http.StatusOK, code)
require.Contains(t, string(respBody), "foo\n")
}

func TestBasicAuth(t *testing.T) {
Expand Down Expand Up @@ -107,8 +118,8 @@ func TestBasicAuth(t *testing.T) {
router := srv.getRouter()

// Prepare request helpers
reqGetLiveZ := makeRequestExecutor(t, router, http.MethodGet, "/livez")
reqSetBasicAuthSecret := makeRequestExecutor(t, router, http.MethodPost, "/api/v1/set-basic-auth")
reqGetLiveZ := createRequestRunner(t, router, http.MethodGet, "/livez")
reqSetBasicAuthSecret := createRequestRunner(t, router, http.MethodPost, "/api/v1/set-basic-auth")

// Initially, /livez should work without basic auth
code, _ := reqGetLiveZ("", "", nil)
Expand Down

0 comments on commit 42e0f6a

Please sign in to comment.