@@ -17,10 +17,14 @@ import (
1717
1818 "github.com/prometheus/client_golang/prometheus"
1919 "github.com/prometheus/client_golang/prometheus/promhttp"
20+ authenticationv1 "k8s.io/api/authentication/v1"
2021 corev1 "k8s.io/api/core/v1"
2122 apierrors "k8s.io/apimachinery/pkg/api/errors"
23+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2224 "k8s.io/apimachinery/pkg/labels"
2325 "k8s.io/apimachinery/pkg/util/sets"
26+ authenticationclientsetv1 "k8s.io/client-go/kubernetes/typed/authentication/v1"
27+ "k8s.io/client-go/rest"
2428 "k8s.io/client-go/tools/cache"
2529 "k8s.io/klog/v2"
2630
@@ -128,8 +132,8 @@ type asyncResult struct {
128132 error error
129133}
130134
131- func createHttpServer () * http.Server {
132- auth := authHandler {downstream : promhttp .Handler ()}
135+ func createHttpServer (ctx context. Context , client * authenticationclientsetv1. AuthenticationV1Client ) * http.Server {
136+ auth := authHandler {downstream : promhttp .Handler (), ctx : ctx , client : client . TokenReviews () }
133137 handler := http .NewServeMux ()
134138 handler .Handle ("/metrics" , & auth )
135139 server := & http.Server {
@@ -138,8 +142,32 @@ func createHttpServer() *http.Server {
138142 return server
139143}
140144
145+ type TokenReviewInterface interface {
146+ Create (ctx context.Context , tokenReview * authenticationv1.TokenReview , opts metav1.CreateOptions ) (* authenticationv1.TokenReview , error )
147+ }
148+
141149type authHandler struct {
142150 downstream http.Handler
151+ ctx context.Context
152+ client TokenReviewInterface
153+ }
154+
155+ func (a * authHandler ) authorize (token string ) (bool , error ) {
156+ tr := & authenticationv1.TokenReview {
157+ Spec : authenticationv1.TokenReviewSpec {
158+ Token : token ,
159+ },
160+ }
161+ result , err := a .client .Create (a .ctx , tr , metav1.CreateOptions {})
162+ if err != nil {
163+ return false , fmt .Errorf ("failed to check token: %w" , err )
164+ }
165+ if ! result .Status .Authenticated {
166+ klog .V (2 ).Info ("The token cannot be authenticated." )
167+ } else if user := result .Status .User .Username ; user != "system:serviceaccount:openshift-monitoring:prometheus-k8s" {
168+ klog .V (2 ).Infof ("Access the metrics from he unexpected user %s is denied." , user )
169+ }
170+ return result .Status .Authenticated && result .Status .User .Username == "system:serviceaccount:openshift-monitoring:prometheus-k8s" , nil
143171}
144172
145173func (a * authHandler ) ServeHTTP (w http.ResponseWriter , r * http.Request ) {
@@ -154,7 +182,16 @@ func (a *authHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
154182 return
155183 }
156184
157- // TODO use the token
185+ authorized , err := a .authorize (token )
186+ if err != nil {
187+ klog .Warningf ("Failed to authorize token: %v" , err )
188+ http .Error (w , "failed to authorize due to an internal error" , http .StatusInternalServerError )
189+ return
190+ }
191+ if ! authorized {
192+ http .Error (w , "failed to authorize" , http .StatusUnauthorized )
193+ return
194+ }
158195 a .downstream .ServeHTTP (w , r )
159196}
160197
@@ -203,7 +240,7 @@ func handleServerResult(result asyncResult, lastLoopError error) error {
203240// Also detects changes to metrics certificate files upon which
204241// the metrics HTTP server is shutdown and recreated with a new
205242// TLS configuration.
206- func RunMetrics (runContext context.Context , shutdownContext context.Context , listenAddress , certFile , keyFile string ) error {
243+ func RunMetrics (runContext context.Context , shutdownContext context.Context , listenAddress , certFile , keyFile string , restConfig * rest. Config ) error {
207244 var tlsConfig * tls.Config
208245 if listenAddress != "" {
209246 var err error
@@ -214,7 +251,13 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis
214251 } else {
215252 return errors .New ("TLS configuration is required to serve metrics" )
216253 }
217- server := createHttpServer ()
254+
255+ client , err := authenticationclientsetv1 .NewForConfig (restConfig )
256+ if err != nil {
257+ return fmt .Errorf ("failed to create config: %w" , err )
258+ }
259+
260+ server := createHttpServer (runContext , client )
218261
219262 resultChannel := make (chan asyncResult , 1 )
220263 resultChannelCount := 1
@@ -268,7 +311,7 @@ func RunMetrics(runContext context.Context, shutdownContext context.Context, lis
268311 case result := <- resultChannel : // crashed before a shutdown was requested or metrics server recreated
269312 if restartServer {
270313 klog .Info ("Creating metrics server with updated TLS configuration." )
271- server = createHttpServer ()
314+ server = createHttpServer (runContext , client )
272315 go startListening (server , tlsConfig , listenAddress , resultChannel )
273316 restartServer = false
274317 continue
0 commit comments