Skip to content

Commit d6cf662

Browse files
authored
Bitwarden Auth Feature (#1)
* add basic auth type * add proxy settings * add doc bitwarden usage * configure bitwarden * ssl doc * proxy doc
1 parent 350fcae commit d6cf662

File tree

3 files changed

+181
-9
lines changed

3 files changed

+181
-9
lines changed

.github/workflows/build.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,10 @@ on:
44
push:
55
tags:
66
- '*'
7-
#branches:
8-
# - main
9-
#release:
10-
# types: [created]
7+
branches:
8+
- dev
9+
release:
10+
types: [created]
1111

1212
jobs:
1313
fyne-cross-and-release:

README.md

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,60 @@ metrics are fetched from your existing Prometheus backend and displayed on the c
55

66
configure your relevant metrics in [metrics.json](metrics.json), download the binary from the [Release page](https://github.com/eumel8/cluster-app/releases) and start the program in the same directory where the metric.json exists.
77

8-
point `PROMETHEUS_URL` env to your Prometheus backend, i.e. `http://prometheus.example.com:9090`
8+
point `PROMETHEUS_URL` env to your Prometheus backend, i.e.
9+
10+
```
11+
export PROMETHEUS_URL=http://prometheus.example.com:9090
12+
```
13+
14+
optional set `PULL_DURATION` to another value in seconds to pull new metrics (default: 60), i.e.
15+
16+
```
17+
export PULL_DURATION=10
18+
```
919

1020
start the program and enjoy
1121

22+
## Vulkan driver in WSL (optional)
23+
24+
```
25+
sudo apt install vulkan-tools
26+
```
27+
28+
## Prometheus Auth
29+
30+
If you protect your Prometheus endpoint with authentication, you can set env var for username and password:
31+
32+
```
33+
export PROM_USER=xxxx
34+
export PROM_PASS=xxxx
35+
```
36+
37+
## Bitwarden feature
38+
39+
Start the programm with `-bw`.
40+
41+
In this version the programm expect an item on a Bitwarden service containing username/password for HTTP Basic Auth on
42+
Prometheus API
43+
44+
```
45+
bw get item "Prometheus Agent RemoteWrite
46+
```
47+
48+
## Tips & Tricks
49+
50+
### no GPU support in WSL
51+
52+
```
53+
export LIBGL_ALWAYS_SOFTWARE=1
54+
```
55+
56+
### Prometheus TLS connection
57+
58+
we skip SSL verification and allow insecure connection by default, take care.
59+
60+
### Proxy Settings
61+
62+
we respect env vars like `http_proxy` or `https_proxy` for Prometheus endpoint connection from your computer.
63+
1264

main.go

Lines changed: 124 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,19 @@
11
package main
22

33
import (
4+
"bytes"
45
"context"
6+
"crypto/tls"
57
"encoding/json"
68
"flag"
79
"fmt"
810
"image/color"
911
"math"
12+
"net/http"
13+
// debug
14+
// "net/http/httputil"
1015
"os"
16+
"os/exec"
1117
"strconv"
1218
"time"
1319

@@ -42,6 +48,73 @@ type myTheme struct {
4248
Config *Config
4349
}
4450

51+
// Custom transport to add Basic Auth to each request
52+
type basicAuthTransport struct {
53+
Username string
54+
Password string
55+
Transport http.RoundTripper
56+
}
57+
58+
// Struct to hold Bitwarden login fields
59+
type BitwardenItem struct {
60+
Login struct {
61+
Username string `json:"username"`
62+
Password string `json:"password"`
63+
} `json:"login"`
64+
}
65+
66+
func (bat *basicAuthTransport) RoundTrip(req *http.Request) (*http.Response, error) {
67+
req.SetBasicAuth(bat.Username, bat.Password)
68+
// Dump request
69+
//dump, err := httputil.DumpRequestOut(req, false) // `true` if you want to include the body
70+
//if err != nil {
71+
// fmt.Printf("Request dump error: %v\n", err)
72+
//} else {
73+
// fmt.Printf("🚀 Outgoing request:\n%s\n", dump)
74+
//}
75+
76+
//resp, err := bat.transport().RoundTrip(req)
77+
//if err != nil {
78+
// return nil, err
79+
//}
80+
81+
// Dump response
82+
//fmt.Printf("HTTP Response requested:\n")
83+
//respDump, err := httputil.DumpResponse(resp, true)
84+
//if err == nil {
85+
// fmt.Printf("📥 HTTP Response:\n%s\n", respDump)
86+
//}
87+
return bat.transport().RoundTrip(req)
88+
}
89+
90+
func (bat *basicAuthTransport) transport() http.RoundTripper {
91+
if bat.Transport != nil {
92+
return bat.Transport
93+
}
94+
return http.DefaultTransport
95+
}
96+
97+
// Get BW_SESSION from env
98+
func getSessionToken() string {
99+
return os.Getenv("BW_SESSION")
100+
}
101+
102+
// Run Bitwarden CLI to get the item JSON
103+
func getBitwardenItemJSON(itemName string) ([]byte, error) {
104+
cmd := exec.Command("bw", "get", "item", itemName)
105+
cmd.Env = append(os.Environ(), "BW_SESSION="+getSessionToken())
106+
107+
var out bytes.Buffer
108+
cmd.Stdout = &out
109+
110+
err := cmd.Run()
111+
if err != nil {
112+
return nil, err
113+
}
114+
115+
return out.Bytes(), nil
116+
}
117+
45118
func loadMetricsFromFile(path string) ([]Metric, error) {
46119
data, err := os.ReadFile(path)
47120
if err != nil {
@@ -70,14 +143,39 @@ func GetConfig() (*Config, error) {
70143
}, nil
71144
}
72145

73-
func (c *Config) getMetricValue(metric string) (int, error) {
74-
client, err := api.NewClient(api.Config{Address: c.PrometheusURL})
146+
func (c *Config) getMetricValue(metric string, username string, password string) (int, error) {
147+
148+
prometheus := c.PrometheusURL
149+
150+
customClient := &http.Client{
151+
Transport: &http.Transport{
152+
Proxy: http.ProxyFromEnvironment,
153+
TLSClientConfig: &tls.Config{
154+
InsecureSkipVerify: true,
155+
},
156+
},
157+
Timeout: 10 * time.Second,
158+
}
159+
160+
// Wrap customClient with basic auth
161+
transportWithAuth := basicAuthTransport{
162+
Username: username,
163+
Password: password,
164+
Transport: customClient.Transport,
165+
}
166+
167+
// Create Prometheus API client
168+
client, err := api.NewClient(api.Config{
169+
Address: prometheus,
170+
RoundTripper: &transportWithAuth,
171+
})
172+
75173
if err != nil {
76174
return 0, err
77175
}
78176

79177
v1api := v1.NewAPI(client)
80-
ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
178+
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
81179
defer cancel()
82180

83181
result, _, err := v1api.Query(ctx, metric, time.Now())
@@ -109,6 +207,7 @@ func (m *myTheme) Icon(name fyne.ThemeIconName) fyne.Resource { return theme.Def
109207
func main() {
110208

111209
verbose := flag.Bool("v", false, "enable verbose logging")
210+
bitwarden := flag.Bool("bw", false, "enable Bitwarden password store")
112211
flag.Parse()
113212

114213
config, err := GetConfig()
@@ -130,6 +229,27 @@ func main() {
130229
w.SetContent(content)
131230
w.Show()
132231

232+
username := os.Getenv("PROM_USER")
233+
password := os.Getenv("PROM_PASS")
234+
235+
if *bitwarden == true {
236+
// doing bitwarden stuff here to get prometheus credentials
237+
itemName := "Prometheus Agent RemoteWrite"
238+
jsonData, err := getBitwardenItemJSON(itemName)
239+
if err != nil {
240+
fmt.Printf("Failed to get item from Bitwarden: %v\n", err)
241+
}
242+
243+
var item BitwardenItem
244+
err = json.Unmarshal(jsonData, &item)
245+
if err != nil {
246+
fmt.Printf("Failed to parse Bitwarden JSON: %v\n", err)
247+
}
248+
249+
username = item.Login.Username
250+
password = item.Login.Password
251+
}
252+
133253
go func() {
134254
for {
135255
var (
@@ -139,7 +259,7 @@ func main() {
139259
)
140260

141261
for _, metric := range config.Metrics {
142-
val, err := config.getMetricValue(metric.Name)
262+
val, err := config.getMetricValue(metric.Name, username, password)
143263

144264
var icon *canvas.Text
145265
var statusText string

0 commit comments

Comments
 (0)