commit 50b6db7a0dd1957ea964a8ad27e259c3d4f70390
parent 6485aa250151b90bc29eec27a2bc8dbd7d0bd920
Author: Vetle Haflan <vetle.haflan@luxsave.com>
Date: Sat, 16 May 2020 16:27:20 +0200
Add support for authentication through Pass-Hash header
This means that *all* operations are now available through the REST API,
even though GET /events is probably the only interesting one, at least
for now.
Diffstat:
3 files changed, 62 insertions(+), 8 deletions(-)
diff --git a/get-events.sh b/get-events.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+sermoni=http://localhost:8080
+
+PASSHASH=$(printf $1 | sha256sum - | awk '{print $1}')
+curl -H "Pass-Hash: $PASSHASH" $sermoni/events
diff --git a/internal/http/auth.go b/internal/http/auth.go
@@ -5,6 +5,7 @@ import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
+ "fmt"
"io/ioutil"
"net/http"
)
@@ -17,6 +18,7 @@ const (
keyPassphrase = "passphrase"
keySessionName = "session"
headerCSRFToken = "X-Csrf-Token"
+ headerPassHash = "Pass-Hash"
)
// initHandler checks two things:
@@ -86,13 +88,25 @@ func authorized(r *http.Request) bool {
}
// Middleware for the simple sermoni authentication scheme
+// Accepts authentication both with session cookie (typically web browser) and
+// a header on the format `Pass-Hash: <sha256sum of pass phrase>`
func auth(handler http.HandlerFunc) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- // store is the global CookieStore
- if !authorized(r) || !csrfCheckPassed(r) {
- status := http.StatusUnauthorized
- http.Error(w, http.StatusText(status), status)
- return
+ passHashHeader := r.Header[headerPassHash]
+ if passHashHeader != nil {
+ // compare to hex formatted version of correct hash
+ if passHashHeader[0] != fmt.Sprintf("%x", conf.PassHash) {
+ status := http.StatusUnauthorized
+ http.Error(w, "Invalid passphrase hash", status)
+ return
+ }
+ } else {
+ // store is the global CookieStore
+ if !authorized(r) || !csrfCheckPassed(r) {
+ status := http.StatusUnauthorized
+ http.Error(w, http.StatusText(status), status)
+ return
+ }
}
handler.ServeHTTP(w, r)
})
diff --git a/readme.md b/readme.md
@@ -10,21 +10,55 @@ in the simplest way possible:
2. There's no central log of SSH logins to servers
-## Suggested use
+## Usage
+On first startup, specify a passphrase and optionally a website title:
+
+ sermoni -pass <passphrase> -title "Service monitor"
+
+Log in to the website using said passphrase and click the eye symbol to add
+services.
+
+### API
+
+#### Report
+`POST /events` is a special case used by reports. Authentication is done here
+with the service token. To report status from a service, specify the server in
+`report.sh` and run
+
+ ./report.sh <service token> <status> [<title>] [<details>]
+
+where `status` is `ok`, `error`, or `info`.
+
+For the remaining endpoints authentication is done by including a `Pass-Hash`
+header. See `get-events.sh`, for example. There's no reason for the client to
+perform the hashing, of course. Instead just hash once and store the hash
+directly on the client.
+
+```
+GET /events
+DELETE /events/<id>
+
+GET /services
+POST /services
+DELETE /services (TODO)
+PUT /services (TODO)
+```
+
+## Suggested use of services
Tokens can be set to whatever you want, but the suggested approach is to
- generate a random token for each _server_, using a cryptosecure generator
- put this secure token in a file, for instance `/root/.sermoni`
- make a new token for each _service_ by appending an identifier, so that the
- format is `<service_token>-<identifier>`
+ format is `<service_token>:<identifier>`
Example of a script using this approach:
```bash
#!/bin/bash
-service_token=$(cat /root/.sermoni)-backup
+service_token=$(cat /root/.sermoni):backup
<backup logic...>