summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMike Crute <mike@crute.us>2023-08-19 10:49:51 -0700
committerMike Crute <mike@crute.us>2023-08-19 10:50:36 -0700
commit9b1afe4a7c84fbc5365b7f83e6eabc926bb508cb (patch)
tree8cf74d42fc184c86ce3c8158c77c1436906cd854
parentaa0254d2eb8cfb89d594861dcb3c9af2b0cf2b89 (diff)
downloadwebsocket_proxy-9b1afe4a7c84fbc5365b7f83e6eabc926bb508cb.tar.bz2
websocket_proxy-9b1afe4a7c84fbc5365b7f83e6eabc926bb508cb.tar.xz
websocket_proxy-9b1afe4a7c84fbc5365b7f83e6eabc926bb508cb.zip
Add prometheus metrics
-rw-r--r--app/controllers/ca.go22
-rw-r--r--app/controllers/login.go20
-rw-r--r--app/controllers/oauth2_device.go28
-rw-r--r--app/controllers/proxy.go19
-rw-r--r--app/controllers/register.go22
-rw-r--r--go.mod2
6 files changed, 112 insertions, 1 deletions
diff --git a/app/controllers/ca.go b/app/controllers/ca.go
index 632db50..c04dcd8 100644
--- a/app/controllers/ca.go
+++ b/app/controllers/ca.go
@@ -11,9 +11,24 @@ import (
11 "code.crute.us/mcrute/ssh-proxy/app/middleware" 11 "code.crute.us/mcrute/ssh-proxy/app/middleware"
12 "code.crute.us/mcrute/ssh-proxy/app/models" 12 "code.crute.us/mcrute/ssh-proxy/app/models"
13 "github.com/labstack/echo/v4" 13 "github.com/labstack/echo/v4"
14 "github.com/prometheus/client_golang/prometheus"
15 "github.com/prometheus/client_golang/prometheus/promauto"
14 "golang.org/x/crypto/ssh" 16 "golang.org/x/crypto/ssh"
15) 17)
16 18
19var (
20 caError = promauto.NewCounterVec(prometheus.CounterOpts{
21 Namespace: "ssh_proxy",
22 Name: "ssh_ca_error",
23 Help: "Total number of errors during SSH CA operation",
24 }, []string{"type"})
25 caSuccess = promauto.NewCounter(prometheus.CounterOpts{
26 Namespace: "ssh_proxy",
27 Name: "ssh_ca_success",
28 Help: "Total number of successful CA operations",
29 })
30)
31
17type CASecret struct { 32type CASecret struct {
18 Key string `mapstructure:"key"` 33 Key string `mapstructure:"key"`
19} 34}
@@ -57,24 +72,29 @@ func (h *CAHandler) authorizeRequest(c echo.Context, certRequest *ssh.Certificat
57 } 72 }
58 73
59 if user.Username != certRequest.ValidPrincipals[0] { 74 if user.Username != certRequest.ValidPrincipals[0] {
75 caError.With(prometheus.Labels{"type": "user_request_mismatch"}).Inc()
60 return fmt.Errorf("Authenticated username and cert username must match") 76 return fmt.Errorf("Authenticated username and cert username must match")
61 } 77 }
62 78
63 if !session.HasScope("ca:issue") { 79 if !session.HasScope("ca:issue") {
80 caError.With(prometheus.Labels{"type": "missing_oauth_scope"}).Inc()
64 return fmt.Errorf("Authorized session does not have scope ca:issue") 81 return fmt.Errorf("Authorized session does not have scope ca:issue")
65 } 82 }
66 83
67 if certRequest.Extensions == nil { 84 if certRequest.Extensions == nil {
85 caError.With(prometheus.Labels{"type": "no_extensions"}).Inc()
68 return fmt.Errorf("Cert request extensions are empty") 86 return fmt.Errorf("Cert request extensions are empty")
69 } 87 }
70 88
71 hostLine, ok := certRequest.Extensions["allowed-hosts"] 89 hostLine, ok := certRequest.Extensions["allowed-hosts"]
72 if !ok { 90 if !ok {
91 caError.With(prometheus.Labels{"type": "no_allowed_hosts"}).Inc()
73 return fmt.Errorf("Cert request allowed-hosts is blank") 92 return fmt.Errorf("Cert request allowed-hosts is blank")
74 } 93 }
75 94
76 for _, host := range strings.Split(hostLine, ",") { 95 for _, host := range strings.Split(hostLine, ",") {
77 if !user.AuthorizedForHost(host) { 96 if !user.AuthorizedForHost(host) {
97 caError.With(prometheus.Labels{"type": "user_no_auth_host"}).Inc()
78 return fmt.Errorf("User %s is not authorized for host %s", session.UserId, host) 98 return fmt.Errorf("User %s is not authorized for host %s", session.UserId, host)
79 } 99 }
80 } 100 }
@@ -168,5 +188,7 @@ func (h *CAHandler) HandleIssue(c echo.Context) error {
168 }) 188 })
169 } 189 }
170 190
191 caSuccess.Inc()
192
171 return c.Blob(http.StatusOK, "application/x-ssh-certificate", ssh.MarshalAuthorizedKey(certToIssue)) 193 return c.Blob(http.StatusOK, "application/x-ssh-certificate", ssh.MarshalAuthorizedKey(certToIssue))
172} 194}
diff --git a/app/controllers/login.go b/app/controllers/login.go
index 603eb20..f59789f 100644
--- a/app/controllers/login.go
+++ b/app/controllers/login.go
@@ -13,6 +13,21 @@ import (
13 "github.com/go-webauthn/webauthn/protocol" 13 "github.com/go-webauthn/webauthn/protocol"
14 "github.com/go-webauthn/webauthn/webauthn" 14 "github.com/go-webauthn/webauthn/webauthn"
15 "github.com/labstack/echo/v4" 15 "github.com/labstack/echo/v4"
16 "github.com/prometheus/client_golang/prometheus"
17 "github.com/prometheus/client_golang/prometheus/promauto"
18)
19
20var (
21 loginError = promauto.NewCounterVec(prometheus.CounterOpts{
22 Namespace: "ssh_proxy",
23 Name: "login_error",
24 Help: "Total number of errors during login operation",
25 }, []string{"type"})
26 loginSuccess = promauto.NewCounter(prometheus.CounterOpts{
27 Namespace: "ssh_proxy",
28 Name: "login_success",
29 Help: "Total number of successful logins",
30 })
16) 31)
17 32
18type LoginController[T app.AppSession] struct { 33type LoginController[T app.AppSession] struct {
@@ -57,6 +72,7 @@ func (a *LoginController[T]) HandleFinish(c echo.Context) error {
57 user, err := a.Users.Get(ctx, c.Param("username")) 72 user, err := a.Users.Get(ctx, c.Param("username"))
58 if err != nil { 73 if err != nil {
59 a.Logger.Errorf("Error getting user: %s", err) 74 a.Logger.Errorf("Error getting user: %s", err)
75 loginError.With(prometheus.Labels{"type": "no_user"}).Inc()
60 return c.NoContent(http.StatusNotFound) 76 return c.NoContent(http.StatusNotFound)
61 } 77 }
62 78
@@ -76,6 +92,7 @@ func (a *LoginController[T]) HandleFinish(c echo.Context) error {
76 92
77 if _, err := a.Webauthn.ValidateLogin(user, *s.WebauthnSession, response); err != nil { 93 if _, err := a.Webauthn.ValidateLogin(user, *s.WebauthnSession, response); err != nil {
78 a.Logger.Errorf("Error validating login: %s", err) 94 a.Logger.Errorf("Error validating login: %s", err)
95 loginError.With(prometheus.Labels{"type": "webauthn_invalid"}).Inc()
79 return c.NoContent(http.StatusBadRequest) 96 return c.NoContent(http.StatusBadRequest)
80 } 97 }
81 98
@@ -96,11 +113,13 @@ func (a *LoginController[T]) HandleFinish(c echo.Context) error {
96 authSession, err := a.AuthSessions.GetByUserCode(ctx, code.Code) 113 authSession, err := a.AuthSessions.GetByUserCode(ctx, code.Code)
97 if err != nil { 114 if err != nil {
98 a.Logger.Errorf("No auth session exists") 115 a.Logger.Errorf("No auth session exists")
116 loginError.With(prometheus.Labels{"type": "no_session_for_code"}).Inc()
99 return c.NoContent(http.StatusUnauthorized) 117 return c.NoContent(http.StatusUnauthorized)
100 } 118 }
101 119
102 if authSession.AccessCode != "" { 120 if authSession.AccessCode != "" {
103 a.Logger.Errorf("Session is already authenticated") 121 a.Logger.Errorf("Session is already authenticated")
122 loginError.With(prometheus.Labels{"type": "already_authenticated"}).Inc()
104 return c.NoContent(http.StatusUnauthorized) 123 return c.NoContent(http.StatusUnauthorized)
105 } 124 }
106 125
@@ -113,5 +132,6 @@ func (a *LoginController[T]) HandleFinish(c echo.Context) error {
113 return c.NoContent(http.StatusInternalServerError) 132 return c.NoContent(http.StatusInternalServerError)
114 } 133 }
115 134
135 loginSuccess.Inc()
116 return c.NoContent(http.StatusOK) 136 return c.NoContent(http.StatusOK)
117} 137}
diff --git a/app/controllers/oauth2_device.go b/app/controllers/oauth2_device.go
index 0ddf653..c431495 100644
--- a/app/controllers/oauth2_device.go
+++ b/app/controllers/oauth2_device.go
@@ -9,7 +9,23 @@ import (
9 9
10 "code.crute.us/mcrute/ssh-proxy/app" 10 "code.crute.us/mcrute/ssh-proxy/app"
11 "code.crute.us/mcrute/ssh-proxy/app/models" 11 "code.crute.us/mcrute/ssh-proxy/app/models"
12
12 "github.com/labstack/echo/v4" 13 "github.com/labstack/echo/v4"
14 "github.com/prometheus/client_golang/prometheus"
15 "github.com/prometheus/client_golang/prometheus/promauto"
16)
17
18var (
19 oauth2DeviceError = promauto.NewCounterVec(prometheus.CounterOpts{
20 Namespace: "ssh_proxy",
21 Name: "oauth2_device_error",
22 Help: "Total number of errors during oauth2 device operations",
23 }, []string{"type"})
24 oauth2DeviceSuccess = promauto.NewCounter(prometheus.CounterOpts{
25 Namespace: "ssh_proxy",
26 Name: "oauth2_device_success",
27 Help: "Total number of successful oauth2 device auths",
28 })
13) 29)
14 30
15func badRequest(c echo.Context, e models.AuthorizationError, d string) error { 31func badRequest(c echo.Context, e models.AuthorizationError, d string) error {
@@ -40,15 +56,18 @@ func (a *OAuth2DeviceController[T]) HandleStart(c echo.Context) error {
40 client, err := a.OauthClients.Get(ctx, form.ClientId) 56 client, err := a.OauthClients.Get(ctx, form.ClientId)
41 if err != nil { 57 if err != nil {
42 a.Logger.Errorf("Unable to find client ID '%s': %s", form.ClientId, err) 58 a.Logger.Errorf("Unable to find client ID '%s': %s", form.ClientId, err)
59 oauth2DeviceError.With(prometheus.Labels{"type": "invalid_client_id"}).Inc()
43 return badRequest(c, models.ErrUnauthorizedClient, "") 60 return badRequest(c, models.ErrUnauthorizedClient, "")
44 } 61 }
45 62
46 if len(form.Challenge) <= 16 { 63 if len(form.Challenge) <= 16 {
64 oauth2DeviceError.With(prometheus.Labels{"type": "challenge_length"}).Inc()
47 return badRequest(c, models.ErrInvalidRequest, 65 return badRequest(c, models.ErrInvalidRequest,
48 "code_challenge is too short, minimum length is 16 bytes") 66 "code_challenge is too short, minimum length is 16 bytes")
49 } 67 }
50 68
51 if form.ChallengeMethod != models.ChallengeS256 { 69 if form.ChallengeMethod != models.ChallengeS256 {
70 oauth2DeviceError.With(prometheus.Labels{"type": "challenge_type"}).Inc()
52 return badRequest(c, models.ErrInvalidRequest, 71 return badRequest(c, models.ErrInvalidRequest,
53 "code_challenge_method invalid, only S256 supported") 72 "code_challenge_method invalid, only S256 supported")
54 } 73 }
@@ -58,11 +77,13 @@ func (a *OAuth2DeviceController[T]) HandleStart(c echo.Context) error {
58 session.SetScopeString(form.Scope) 77 session.SetScopeString(form.Scope)
59 78
60 if !session.HasAnyScopes() { 79 if !session.HasAnyScopes() {
80 oauth2DeviceError.With(prometheus.Labels{"type": "no_scopes"}).Inc()
61 return badRequest(c, models.ErrInvalidRequest, "one or more scopes required") 81 return badRequest(c, models.ErrInvalidRequest, "one or more scopes required")
62 } 82 }
63 83
64 for _, s := range session.Scope { 84 for _, s := range session.Scope {
65 if s != "ssh:proxy" && s != "ca:issue" { 85 if s != "ssh:proxy" && s != "ca:issue" {
86 oauth2DeviceError.With(prometheus.Labels{"type": "invalid_scope"}).Inc()
66 return badRequest(c, models.ErrInvalidScope, fmt.Sprintf("scope %s is not recognized", s)) 87 return badRequest(c, models.ErrInvalidScope, fmt.Sprintf("scope %s is not recognized", s))
67 } 88 }
68 } 89 }
@@ -93,27 +114,33 @@ func (a *OAuth2DeviceController[T]) HandleToken(c echo.Context) error {
93 114
94 session, err := a.AuthSessions.Get(ctx, form.DeviceCode) 115 session, err := a.AuthSessions.Get(ctx, form.DeviceCode)
95 if err != nil { 116 if err != nil {
117 oauth2DeviceError.With(prometheus.Labels{"type": "no_auth_session"}).Inc()
96 return c.NoContent(http.StatusNotFound) 118 return c.NoContent(http.StatusNotFound)
97 } 119 }
98 120
99 if form.GrantType != models.DEVICE_CODE_GRANT_TYPE { 121 if form.GrantType != models.DEVICE_CODE_GRANT_TYPE {
122 oauth2DeviceError.With(prometheus.Labels{"type": "invalid_grant_type"}).Inc()
100 return badRequest(c, models.ErrUnsupportedGrantType, "") 123 return badRequest(c, models.ErrUnsupportedGrantType, "")
101 } 124 }
102 125
103 if subtle.ConstantTimeCompare([]byte(session.ClientId), []byte(form.ClientId)) != 1 { 126 if subtle.ConstantTimeCompare([]byte(session.ClientId), []byte(form.ClientId)) != 1 {
127 oauth2DeviceError.With(prometheus.Labels{"type": "client_id_mismatch"}).Inc()
104 return badRequest(c, models.ErrUnauthorizedClient, "") 128 return badRequest(c, models.ErrUnauthorizedClient, "")
105 } 129 }
106 130
107 if time.Now().After(session.Expires) { 131 if time.Now().After(session.Expires) {
132 oauth2DeviceError.With(prometheus.Labels{"type": "expired_session"}).Inc()
108 return badRequest(c, models.ErrExpiredToken, "") 133 return badRequest(c, models.ErrExpiredToken, "")
109 } 134 }
110 135
111 verifier := &models.PKCEChallenge{Verifier: form.CodeVerifier} 136 verifier := &models.PKCEChallenge{Verifier: form.CodeVerifier}
112 if verifier.EqualString(session.Challenge) { 137 if verifier.EqualString(session.Challenge) {
138 oauth2DeviceError.With(prometheus.Labels{"type": "pkce_mismatch"}).Inc()
113 return badRequest(c, models.ErrInvalidGrant, "") // Per RFC7636 4.6 139 return badRequest(c, models.ErrInvalidGrant, "") // Per RFC7636 4.6
114 } 140 }
115 141
116 if session.IsRegistration { 142 if session.IsRegistration {
143 oauth2DeviceError.With(prometheus.Labels{"type": "is_registration_session"}).Inc()
117 return badRequest(c, models.ErrInvalidGrant, "") 144 return badRequest(c, models.ErrInvalidGrant, "")
118 } 145 }
119 146
@@ -121,6 +148,7 @@ func (a *OAuth2DeviceController[T]) HandleToken(c echo.Context) error {
121 return badRequest(c, models.ErrAuthorizationPending, "") 148 return badRequest(c, models.ErrAuthorizationPending, "")
122 } 149 }
123 150
151 oauth2DeviceSuccess.Inc()
124 return c.JSON(http.StatusOK, models.AccessTokenResponse{ 152 return c.JSON(http.StatusOK, models.AccessTokenResponse{
125 AccessToken: session.AccessCode, 153 AccessToken: session.AccessCode,
126 TokenType: "Bearer", 154 TokenType: "Bearer",
diff --git a/app/controllers/proxy.go b/app/controllers/proxy.go
index c8345e8..9e3ec13 100644
--- a/app/controllers/proxy.go
+++ b/app/controllers/proxy.go
@@ -12,6 +12,21 @@ import (
12 12
13 "github.com/gorilla/websocket" 13 "github.com/gorilla/websocket"
14 "github.com/labstack/echo/v4" 14 "github.com/labstack/echo/v4"
15 "github.com/prometheus/client_golang/prometheus"
16 "github.com/prometheus/client_golang/prometheus/promauto"
17)
18
19var (
20 proxyError = promauto.NewCounterVec(prometheus.CounterOpts{
21 Namespace: "ssh_proxy",
22 Name: "proxy_error",
23 Help: "Total number of errors during proxy setup operation",
24 }, []string{"type"})
25 proxySuccess = promauto.NewCounter(prometheus.CounterOpts{
26 Namespace: "ssh_proxy",
27 Name: "proxy_success",
28 Help: "Total number of successful proxy sessions",
29 })
15) 30)
16 31
17type ProxyHandler struct { 32type ProxyHandler struct {
@@ -37,6 +52,7 @@ func (h *ProxyHandler) authorizeRequest(c echo.Context) error {
37 } 52 }
38 53
39 if !session.HasScope("ssh:proxy") { 54 if !session.HasScope("ssh:proxy") {
55 proxyError.With(prometheus.Labels{"type": "token_missing_scope"}).Inc()
40 return fmt.Errorf("Authorized session does not have scope ssh:proxy") 56 return fmt.Errorf("Authorized session does not have scope ssh:proxy")
41 } 57 }
42 58
@@ -46,6 +62,7 @@ func (h *ProxyHandler) authorizeRequest(c echo.Context) error {
46 return nil 62 return nil
47 } 63 }
48 64
65 proxyError.With(prometheus.Labels{"type": "not_authorized"}).Inc()
49 return fmt.Errorf("User %s not authorized for host %s", session.UserId, host) 66 return fmt.Errorf("User %s not authorized for host %s", session.UserId, host)
50} 67}
51 68
@@ -70,6 +87,8 @@ func (h *ProxyHandler) Handle(c echo.Context) error {
70 errc := make(chan error) 87 errc := make(chan error)
71 ws := &proxy.WebsocketReadWriter{W: wsconn} 88 ws := &proxy.WebsocketReadWriter{W: wsconn}
72 89
90 proxySuccess.Inc()
91
73 go proxy.CopyWithErrors(proxyconn, ws, errc) 92 go proxy.CopyWithErrors(proxyconn, ws, errc)
74 go proxy.CopyWithErrors(ws, proxyconn, errc) 93 go proxy.CopyWithErrors(ws, proxyconn, errc)
75 94
diff --git a/app/controllers/register.go b/app/controllers/register.go
index 7c1a0f3..312daae 100644
--- a/app/controllers/register.go
+++ b/app/controllers/register.go
@@ -15,6 +15,21 @@ import (
15 "github.com/go-webauthn/webauthn/protocol" 15 "github.com/go-webauthn/webauthn/protocol"
16 "github.com/go-webauthn/webauthn/webauthn" 16 "github.com/go-webauthn/webauthn/webauthn"
17 "github.com/labstack/echo/v4" 17 "github.com/labstack/echo/v4"
18 "github.com/prometheus/client_golang/prometheus"
19 "github.com/prometheus/client_golang/prometheus/promauto"
20)
21
22var (
23 registerError = promauto.NewCounterVec(prometheus.CounterOpts{
24 Namespace: "ssh_proxy",
25 Name: "register_error",
26 Help: "Total number of errors during registration",
27 }, []string{"type"})
28 registerSuccess = promauto.NewCounter(prometheus.CounterOpts{
29 Namespace: "ssh_proxy",
30 Name: "register_success",
31 Help: "Total number of successful registrations",
32 })
18) 33)
19 34
20type RegisterController[T app.AppSession] struct { 35type RegisterController[T app.AppSession] struct {
@@ -32,18 +47,22 @@ func (a *RegisterController[T]) validateRequest(ctx context.Context, u *models.U
32 47
33 authSession, err := a.AuthSessions.GetByUserCode(ctx, code) 48 authSession, err := a.AuthSessions.GetByUserCode(ctx, code)
34 if err != nil { 49 if err != nil {
50 registerError.With(prometheus.Labels{"type": "no_user_for_code"}).Inc()
35 return nil, fmt.Errorf("No auth session exists") 51 return nil, fmt.Errorf("No auth session exists")
36 } 52 }
37 53
38 if time.Now().After(authSession.Expires) { 54 if time.Now().After(authSession.Expires) {
55 registerError.With(prometheus.Labels{"type": "session_expired"}).Inc()
39 return nil, fmt.Errorf("Session is expired") 56 return nil, fmt.Errorf("Session is expired")
40 } 57 }
41 58
42 if !authSession.IsRegistration { 59 if !authSession.IsRegistration {
60 registerError.With(prometheus.Labels{"type": "incorrect_session_type"}).Inc()
43 return nil, fmt.Errorf("Session is not an invitation to register") 61 return nil, fmt.Errorf("Session is not an invitation to register")
44 } 62 }
45 63
46 if authSession.UserId != u.Username { 64 if authSession.UserId != u.Username {
65 registerError.With(prometheus.Labels{"type": "username_mismatch"}).Inc()
47 return nil, fmt.Errorf("Session not valid for this user") 66 return nil, fmt.Errorf("Session not valid for this user")
48 } 67 }
49 68
@@ -56,6 +75,7 @@ func (a *RegisterController[T]) HandleStart(c echo.Context) error {
56 user, err := a.Users.Get(ctx, c.Param("username")) 75 user, err := a.Users.Get(ctx, c.Param("username"))
57 if err != nil { 76 if err != nil {
58 a.Logger.Errorf("Error getting user: %s", err) 77 a.Logger.Errorf("Error getting user: %s", err)
78 registerError.With(prometheus.Labels{"type": "no_user"}).Inc()
59 return c.NoContent(http.StatusNotFound) 79 return c.NoContent(http.StatusNotFound)
60 } 80 }
61 81
@@ -111,6 +131,7 @@ func (a *RegisterController[T]) HandleFinish(c echo.Context) error {
111 // session in case of other errors 131 // session in case of other errors
112 if err := a.AuthSessions.Delete(ctx, authSession); err != nil { 132 if err := a.AuthSessions.Delete(ctx, authSession); err != nil {
113 a.Logger.Errorf("Error deleting auth session: %s", err) 133 a.Logger.Errorf("Error deleting auth session: %s", err)
134 registerError.With(prometheus.Labels{"type": "db_delete_session"}).Inc()
114 return c.NoContent(http.StatusInternalServerError) 135 return c.NoContent(http.StatusInternalServerError)
115 } 136 }
116 137
@@ -141,5 +162,6 @@ func (a *RegisterController[T]) HandleFinish(c echo.Context) error {
141 return c.NoContent(http.StatusInternalServerError) 162 return c.NoContent(http.StatusInternalServerError)
142 } 163 }
143 164
165 registerSuccess.Inc()
144 return c.NoContent(http.StatusOK) 166 return c.NoContent(http.StatusOK)
145} 167}
diff --git a/go.mod b/go.mod
index a416ec6..591f27c 100644
--- a/go.mod
+++ b/go.mod
@@ -15,6 +15,7 @@ require (
15 github.com/gorilla/websocket v1.5.0 15 github.com/gorilla/websocket v1.5.0
16 github.com/labstack/echo/v4 v4.6.1 16 github.com/labstack/echo/v4 v4.6.1
17 github.com/mdp/qrterminal v1.0.1 17 github.com/mdp/qrterminal v1.0.1
18 github.com/prometheus/client_golang v1.11.0
18 github.com/spf13/cobra v1.7.0 19 github.com/spf13/cobra v1.7.0
19 go.mongodb.org/mongo-driver v1.7.4 20 go.mongodb.org/mongo-driver v1.7.4
20 golang.org/x/crypto v0.11.0 21 golang.org/x/crypto v0.11.0
@@ -75,7 +76,6 @@ require (
75 github.com/oklog/run v1.0.0 // indirect 76 github.com/oklog/run v1.0.0 // indirect
76 github.com/pierrec/lz4 v2.5.2+incompatible // indirect 77 github.com/pierrec/lz4 v2.5.2+incompatible // indirect
77 github.com/pkg/errors v0.9.1 // indirect 78 github.com/pkg/errors v0.9.1 // indirect
78 github.com/prometheus/client_golang v1.11.0 // indirect
79 github.com/prometheus/client_model v0.2.0 // indirect 79 github.com/prometheus/client_model v0.2.0 // indirect
80 github.com/prometheus/common v0.32.1 // indirect 80 github.com/prometheus/common v0.32.1 // indirect
81 github.com/prometheus/procfs v0.6.0 // indirect 81 github.com/prometheus/procfs v0.6.0 // indirect