Skip to content

Commit

Permalink
Version 0.1b
Browse files Browse the repository at this point in the history
  • Loading branch information
sirion committed Jan 15, 2023
1 parent ad4d78b commit 5cc2cf2
Show file tree
Hide file tree
Showing 18 changed files with 1,752 additions and 7 deletions.
13 changes: 6 additions & 7 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,10 +1,3 @@
# Binaries for programs and plugins
*.exe
*.exe~
*.dll
*.so
*.dylib

# Test binary, built with `go test -c`
*.test

Expand All @@ -13,3 +6,9 @@

# Dependency directories (remove the comment below to include it)
# vendor/

# Development
.vscode/

# Build output
_build/
157 changes: 157 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,159 @@
# miStatusBoard

Simple Board to Monitor System Status in Groups

## Configuration File

The configuration is written in YAML.

The configuration contains four top level elements:

- `title` - The title shown in the UI
- `refreshInterval` - The number of seconds between endpoint requests
- `authorization` - How to make sure the accessing user is authorized
- `groups` - The groups (of endpoints) that are monitored

### Authorization

The `authorization` element has the following properties:

- `type` - The type of authorization (currently only "client-cert")

If the type is "client-cert", then the following additional properties are used:

- `header` - The header that is parsed for the user information (see [#web-server-configuration](Web-Server Configuration))
- `users` - The list of usernames (CN in the client certificate)

### Groups

The `groups` property is an array of groups, which have the following properties:

- `inactive` - If set to true, the group will be shown greyed out in the UI and the endpoints will not be checked
- `name` - The name to be shown as title in the UI
- `url` - The base URL used for all endpoints that use relative URLs
- `endpoints` - A list of endpoints for the group

### Endpoints

The group's `entpointss` property is an array of endpoints, which have the following properties:

- `inactive` - If set to true, the endpoint will be shown greyed out in the UI and will not be requested
- `name` - The name to be shown in the UI-endpoints-table
- `url` - The endpoint URL. If relative, the group-URL will be used to resolve it
- `targetStatus` - The status to test for. If not set checks for status code in the 200 range. It contains the following sub-properties:
- `code` - (Default: 200) The status code that the endpoint-request should return.
- `body` - (Default: "") If not set to an empty string, the returned data from the endpoint is compared to this. The string must be in base64 to support binary data.

### Example Configuration

```yaml
title: Are they all up?
authorization:
type: client-cert
header: X-SSL-Client-S-Dn
users:
- sirion
refreshInterval: 30
groups:
- name: Shops
endpoints:
- name: Amazon
url: https://amazon.com/
- name: eBay
url: https://ebay.com/
- name: AliExpress
url: https://aliexpress.com/
- name: Dev
endpoints:
- name: Codeberg
url: https://codeberg.org/
- name: Github
url: https://github.com/
- name: GitLab
url: https://gitlab.com/
- name: BitBucket
url: https://bitbucket.com/
- name: Broken
endpoints:
- name: Invalid Domain
url: https://this-does-not-exist-i-hope.org/
- name: Wikipedia
url: https://wikipedia.org/
- name: Not found
url: https://google.com/this/does/not/exist/i/hope
- name: Android Connectivity Check
url: http://connectivitycheck.android.com/generate_204
targetStatus:
code: 204
- name: Google
inactive: true
url: https://google.com/
- name: Disabled Group
inactive: true
endpoints:
- name: SAP
url: https://sap.com/
- name: Oracle
url: https://oracle.com/
```
## CLI Arguments
You can provide the following arguments when starting the application:
- `port` - (Default: 8765) The web-server port, can also be set via environment variable "PORT"
- `config` - (Default: "./config.yaml") Where to find the configuration file
- `cache` - (Default: "./cache.json") Where the endpoint-results are cached (used to enable a quick start without having to wait for all endpoints to be requested again)

## Web-Server Configuration

This is an example to setup the app with nginx:

```nginx
server {
server_name SERVERNAME;
location / {
if ($ssl_client_verify != SUCCESS) {
return 403;
}
proxy_set_header X-SSL-Client-S-DN $ssl_client_s_dn;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_pass http://localhost:8765/;
}
listen [::]:443 ssl;
ssl_certificate /PATH/TO/SERVER_CERT;
ssl_certificate_key /PATH/TO/SERVER_CERT_KEY;
ssl_verify_client on;
ssl_client_certificate /PATH/TO/CA/CERT.pem;
}
```

- `if ($ssl_client_verify != SUCCESS) {
return 403;
}` makes sure the app will only be accessed by people that have a certificate
from the Certificate-Authority that is checked by `ssl_client_certificate`.

- `proxy_set_header X-SSL-Client-S-DN $ssl_client_s_dn;` forwards the user certificate information to the application as header so that the user name can be parsed from it.

- `ssl_verify_client on;` makes the server ask for the client certificate.

- `ssl_client_certificate /PATH/TO/CA/CERT.pem;` checks the client certificate agains the CA-Certificate in the file.

## ToDos

Currently planned further development:

- Enable parsing of JSON responses and support comparing sub-properties
- Check endpoints only once even if used multiple times
- Make sure there is a graceful error message for users with the right certificate but not in the allowlist
- Allow empty allowlist (maybe nil vs []) to mean all certified users may access
- Support disabling authorization completely
- Support using HEAD instead of GET if we do not need a response body
- Live-Frontend directory in debug mode should be checked for existence.
Binary file added cmd/__debug_bin
Binary file not shown.
59 changes: 59 additions & 0 deletions cmd/arguments.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package main

import (
"flag"
"fmt"
"os"
"strconv"
)

type Arguments struct {
ConfigFile string
Port uint
CacheFile string
}

func ParseCLIArguments() *Arguments {
args := Arguments{
Port: 8765,
ConfigFile: "./config.yaml",
CacheFile: "./cache.json",
}

errors := make([]string, 0)
envPort := os.Getenv("PORT")
if envPort != "" {
port, err := strconv.ParseUint(envPort, 10, 16)
if err != nil {
errors = append(errors, fmt.Sprintf("Invalid Environment Value 'PORT': %s", err.Error()))
} else {
args.Port = uint(port)
}
}

flag.UintVar(&args.Port, "port", args.Port, "Port to listen on (if not set, taken from env variable PORT if available, otherwise uses default)")
flag.StringVar(&args.ConfigFile, "config", args.ConfigFile, "Configuration file")
flag.StringVar(&args.CacheFile, "cache", args.CacheFile, "Cache file for results")
flag.BoolVar(&DebugMode, "debug", DebugMode, "Enable debug mode (live-frontend and logging to stdout)")
showHelp := flag.Bool("help", false, "Show this help")
flag.Parse()

_, err := os.Stat(args.ConfigFile)
if err != nil {
errors = append(errors, fmt.Sprintf("Configuration file cannot be found at \"%s\"\n", args.ConfigFile))
}

if *showHelp {
flag.PrintDefaults()
os.Exit(EXIT_OK)
}

if len(errors) > 0 {
for _, e := range errors {
outError(e)
}
os.Exit(EXIT_CLI_ARGS)
}

return &args
}
48 changes: 48 additions & 0 deletions cmd/configuration.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package main

import (
"os"

"gopkg.in/yaml.v2"
)

type Configuration struct {
Title string `yaml:"title" json:"title"`
Authorization AuthorizationConfiguration `yaml:"authorization" json:"-"`
RefreshInterval float64 `yaml:"refreshInterval" json:"-"`
Groups []*Group `yaml:"groups" json:"groups"`
}

type AuthorizationConfiguration struct {
Type string `yaml:"type" json:"-"`
Header string `yaml:"header" json:"-"`
Users []string `yaml:"users" json:"-"`
authorizedUsers map[string]bool
}

func ReadConfiguration(configPath string) (*Configuration, error) {
config := Configuration{}

data, err := os.ReadFile(configPath)
if err != nil {
return nil, err
}

err = yaml.Unmarshal(data, &config)
if err != nil {
return nil, err
}

// Quick access map for authorization checking
config.Authorization.authorizedUsers = make(map[string]bool, len(config.Authorization.Users))
for _, u := range config.Authorization.Users {
config.Authorization.authorizedUsers[u] = true
}

if config.RefreshInterval < 10 {
out("Configuration: RefreshInterval too low: %f. Set to 10\n", config.RefreshInterval)
config.RefreshInterval = 10
}

return &config, nil
}
17 changes: 17 additions & 0 deletions cmd/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package main

const (
EXIT_OK = 0
EXIT_CLI_ARGS = 1
EXIT_PARSE_CONFIG = 2
EXIT_CACHE_FILE = 4
)

type Status string

const (
STATUS_GREEN Status = "green"
STATUS_YELLOW Status = "yellow"
STATUS_RED Status = "red"
STATUS_INACTIVE Status = "grey"
)
Loading

0 comments on commit 5cc2cf2

Please sign in to comment.