Skip to content
This repository was archived by the owner on Apr 11, 2021. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 11 additions & 39 deletions Gopkg.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

20 changes: 19 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,25 @@ beeping returns HTTP 500 when check fail. The body contains the reason of the fa

## HTTP Basic Auth

Just add the 'auth' option in your JSON.
### Protect your BeePing server

Add the auth-* arguments :

- **auth-user** The auth username
- **auth-secret** The auth user secret
- **auth-method** The auth secret digest mechanism

**For now, only the 'clear' method is supported.**

For example :

```
$ go run beeping.go -auth-user "john" -auth-password "passw0rd"
```

### Authenticated request to the client

Add the 'auth' field in your JSON.

```
$ curl -XPOST http://localhost:8080/check -d '{"url":"http://127.0.0.1:3000","auth":"john:secret"}'
Expand Down
126 changes: 103 additions & 23 deletions beeping.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package main
import (
"crypto/tls"
"encoding/base64"
"encoding/json"
"errors"
"flag"
"fmt"
"io/ioutil"
Expand All @@ -15,7 +17,6 @@ import (
"strings"
"time"

"github.com/gin-gonic/gin"
"github.com/oschwald/geoip2-golang"
"github.com/tcnksm/go-httpstat"
"github.com/yanc0/beeping/sslcheck"
Expand All @@ -32,6 +33,20 @@ var port *string
var tlsmode *bool
var validatetarget *bool

var authUser *string
var authSecret *string
var authMethod *string

type HTTPAuth struct {
User, Secret, Method string
}

var auth *HTTPAuth

type ErrorMessage struct {
Message string `json:"message"`
}

type Beeping struct {
Version string `json:"version"`
Message string `json:"message"`
Expand Down Expand Up @@ -81,6 +96,16 @@ type Response struct {
SSL *sslcheck.CheckSSL `json:"ssl,omitempty"`
}

func NewErrorMessage(message string) *ErrorMessage {
var response = ErrorMessage{}
response.Message = message
return &response
}

func InvalidJSONResponse() *ErrorMessage {
return NewErrorMessage("Invalid JSON sent")
}

func NewResponse() *Response {
var response = Response{}
response.Timeline = &Timeline{}
Expand Down Expand Up @@ -143,61 +168,116 @@ func main() {
port = flag.String("port", "8080", "The port to bind the server to")
tlsmode = flag.Bool("tlsmode", false, "Activate SSL/TLS versions and Cipher support checks (slow)")
validatetarget = flag.Bool("validatetarget", true, "Perform some security checks on the target provided")
authUser = flag.String("auth-user", "", "HTTP Auth User e.g. 'admin'")
authSecret = flag.String("auth-secret", "", "HTTP Auth Secret e.g. 'passw0rd'")
authMethod = flag.String("auth-method", "clear", "HTTP Auth Method (only 'clear' for now)")
flag.Parse()

gin.SetMode("release")
instantiateAuthMechanism()

router := gin.New()
router.POST("/check", handlerCheck)
router.GET("/", handlerDefault)
http.HandleFunc("/check", handlerCheck)
http.HandleFunc("/", handlerDefault)

log.Println("[INFO] Listening on", *listen, *port)
router.Run(*listen + ":" + *port)
http.ListenAndServe(*listen+":"+*port, nil)
}

func instantiateAuthMechanism() {
if *authUser == "" {
return
}
if *authSecret == "" {
panic(errors.New("Auth secret can not be empty."))
}
switch strings.ToLower(*authMethod) {
case "clear":
break
default:
panic(errors.New("Unsupported auth method."))
}
auth = &HTTPAuth{*authUser, *authSecret, *authMethod}
log.Printf("[INFO] HTTP Auth enabled: %v\n", auth)
}

func handlerDefault(c *gin.Context) {
func checkAuth(w http.ResponseWriter, r *http.Request) bool {
if auth == nil {
return true
}
user, pass, _ := r.BasicAuth()
fmt.Println(user + " " + pass)
//TODO depending the auth method, transform the pass
if user == auth.User && pass == auth.Secret {
return true
}
log.Println("[INFO] Unauthorized (", user, ")")
w.Header().Add("WWW-Authenticate", "Basic realm=\"Access Denied\"")
http.Error(w, "401, Unauthorized", 401)
return false
}

func handlerDefault(w http.ResponseWriter, r *http.Request) {
if !checkAuth(w, r) {
return
}
var beeping Beeping
beeping.Version = VERSION
beeping.Message = MESSAGE
log.Println("[INFO] Beeping version", beeping.Version)
c.JSON(http.StatusOK, beeping)
jsonRes, _ := json.Marshal(beeping)
w.Header().Set("Content-Type", "application/json")
w.Write(jsonRes)
}

func handlerCheck(c *gin.Context) {
func handlerCheck(w http.ResponseWriter, r *http.Request) {
if !checkAuth(w, r) {
return
}

var check = NewCheck()
if c.BindJSON(&check) != nil {

w.Header().Set("Content-Type", "application/json")

decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&check)
if err != nil {
log.Println("[WARN] Invalid JSON sent")
c.JSON(http.StatusBadRequest, gin.H{"message": "invalid json sent"})
jsonRes, _ := json.Marshal(InvalidJSONResponse())
w.Write(jsonRes)
return
}

// with security checks
if *validatetarget {
if err := check.validateTarget(); err != nil {
log.Println("[WARN] Invalid target:", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
return
}
if !*validatetarget {
response, err := CheckHTTP(check)
if err != nil {
log.Println("[WARN] Check failed:", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
jsonRes, _ := json.Marshal(NewErrorMessage(err.Error()))
w.Write(jsonRes)
return
}
log.Println("[INFO] Successful check:", check.URL, "-", response.HTTPRequestTime, "ms")
c.JSON(http.StatusOK, response)
jsonRes, _ := json.Marshal(response)
w.Write(jsonRes)
return
}

if err := check.validateTarget(); err != nil {
log.Println("[WARN] Invalid target:", err.Error())
jsonRes, _ := json.Marshal(NewErrorMessage(err.Error()))
w.Write(jsonRes)
return
}

// without security checks
response, err := CheckHTTP(check)
if err != nil {
log.Println("[WARN] Check failed:", err.Error())
c.JSON(http.StatusInternalServerError, gin.H{"message": err.Error()})
jsonRes, _ := json.Marshal(NewErrorMessage(err.Error()))
w.Write(jsonRes)
return
}

log.Println("[INFO] Successful check:", check.URL, "-", response.HTTPRequestTime, "ms")
c.JSON(http.StatusOK, response)
jsonRes, _ := json.Marshal(response)
w.Write(jsonRes)
}

// CheckHTTP do HTTP check and return a beeping response
Expand Down