• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

mendersoftware / mender-server / 1971916181

07 Aug 2025 10:27AM UTC coverage: 65.495% (-0.007%) from 65.502%
1971916181

push

gitlab-ci

web-flow
Merge pull request #855 from alfrunes/device-limits-auth-middleware-fix

fix(deviceauth): token verify needs to use X-Forwarded-* for ratelimi…

0 of 3 new or added lines in 1 file covered. (0.0%)

2 existing lines in 1 file now uncovered.

32312 of 49335 relevant lines covered (65.5%)

1.39 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.12
/backend/services/deviceauth/server.go
1
// Copyright 2023 Northern.tech AS
2
//
3
//    Licensed under the Apache License, Version 2.0 (the "License");
4
//    you may not use this file except in compliance with the License.
5
//    You may obtain a copy of the License at
6
//
7
//        http://www.apache.org/licenses/LICENSE-2.0
8
//
9
//    Unless required by applicable law or agreed to in writing, software
10
//    distributed under the License is distributed on an "AS IS" BASIS,
11
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
//    See the License for the specific language governing permissions and
13
//    limitations under the License.
14

15
package main
16

17
import (
18
        "context"
19
        "fmt"
20
        "net/http"
21
        "os"
22
        "os/signal"
23
        "time"
24

25
        "github.com/pkg/errors"
26
        "golang.org/x/sys/unix"
27

28
        "github.com/mendersoftware/mender-server/pkg/config"
29
        "github.com/mendersoftware/mender-server/pkg/config/ratelimits"
30
        "github.com/mendersoftware/mender-server/pkg/log"
31
        "github.com/mendersoftware/mender-server/pkg/rate"
32
        "github.com/mendersoftware/mender-server/pkg/redis"
33

34
        api_http "github.com/mendersoftware/mender-server/services/deviceauth/api/http"
35
        "github.com/mendersoftware/mender-server/services/deviceauth/cache"
36
        "github.com/mendersoftware/mender-server/services/deviceauth/client/orchestrator"
37
        "github.com/mendersoftware/mender-server/services/deviceauth/client/tenant"
38
        dconfig "github.com/mendersoftware/mender-server/services/deviceauth/config"
39
        "github.com/mendersoftware/mender-server/services/deviceauth/devauth"
40
        "github.com/mendersoftware/mender-server/services/deviceauth/jwt"
41
        "github.com/mendersoftware/mender-server/services/deviceauth/store/mongo"
42
)
43

44
func RunServer(c config.Reader) error {
2✔
45
        tenantadmAddr := c.GetString(dconfig.SettingTenantAdmAddr)
2✔
46

2✔
47
        l := log.New(log.Ctx{})
2✔
48

2✔
49
        db, err := mongo.NewDataStoreMongo(
2✔
50
                mongo.DataStoreMongoConfig{
2✔
51
                        ConnectionString: c.GetString(dconfig.SettingDb),
2✔
52

2✔
53
                        SSL:           c.GetBool(dconfig.SettingDbSSL),
2✔
54
                        SSLSkipVerify: c.GetBool(dconfig.SettingDbSSLSkipVerify),
2✔
55

2✔
56
                        Username: c.GetString(dconfig.SettingDbUsername),
2✔
57
                        Password: c.GetString(dconfig.SettingDbPassword),
2✔
58
                })
2✔
59
        if err != nil {
2✔
60
                return errors.Wrap(err, "database connection failed")
×
61
        }
×
62

63
        jwtHandler, err := jwt.NewJWTHandler(
2✔
64
                c.GetString(dconfig.SettingServerPrivKeyPath),
2✔
65
        )
2✔
66
        var jwtFallbackHandler jwt.Handler
2✔
67
        fallback := c.GetString(dconfig.SettingServerFallbackPrivKeyPath)
2✔
68
        if err == nil && fallback != "" {
2✔
69
                jwtFallbackHandler, err = jwt.NewJWTHandler(
×
70
                        fallback,
×
71
                )
×
72
        }
×
73
        if err != nil {
2✔
74
                return err
×
75
        }
×
76

77
        orchClientConf := orchestrator.Config{
2✔
78
                OrchestratorAddr: c.GetString(dconfig.SettingOrchestratorAddr),
2✔
79
                Timeout:          time.Duration(30) * time.Second,
2✔
80
        }
2✔
81

2✔
82
        devauth := devauth.NewDevAuth(db,
2✔
83
                orchestrator.NewClient(orchClientConf),
2✔
84
                jwtHandler,
2✔
85
                devauth.Config{
2✔
86
                        Issuer:             c.GetString(dconfig.SettingJWTIssuer),
2✔
87
                        ExpirationTime:     int64(c.GetInt(dconfig.SettingJWTExpirationTimeout)),
2✔
88
                        DefaultTenantToken: c.GetString(dconfig.SettingDefaultTenantToken),
2✔
89
                        InventoryAddr:      config.Config.GetString(dconfig.SettingInventoryAddr),
2✔
90

2✔
91
                        EnableReporting: config.Config.GetBool(dconfig.SettingEnableReporting),
2✔
92
                        HaveAddons: config.Config.GetBool(dconfig.SettingHaveAddons) &&
2✔
93
                                tenantadmAddr != "",
2✔
94
                })
2✔
95

2✔
96
        if jwtFallbackHandler != nil {
2✔
97
                devauth = devauth.WithJWTFallbackHandler(jwtFallbackHandler)
×
98
        }
×
99

100
        if tenantadmAddr != "" {
2✔
101
                tc := tenant.NewClient(tenant.Config{
×
102
                        TenantAdmAddr: tenantadmAddr,
×
103
                })
×
104
                devauth = devauth.WithTenantVerification(tc)
×
105
        }
×
106

107
        var apiOptions []api_http.Option
2✔
108

2✔
109
        cacheConnStr := c.GetString(dconfig.SettingRedisConnectionString)
2✔
110
        if cacheConnStr == "" {
4✔
111
                // for backward compatibility check old redis_addr setting
2✔
112
                cacheConnStr = c.GetString(dconfig.SettingRedisAddr)
2✔
113
        }
2✔
114
        if cacheConnStr != "" {
2✔
115
                srvCache, rateLimits, err := setupRedis(c, cacheConnStr)
×
116
                if err != nil {
×
117
                        return err
×
118
                }
×
119
                devauth = devauth.WithCache(srvCache)
×
120
                if rateLimits != nil {
×
121
                        apiOptions = append(apiOptions,
×
NEW
122
                                api_http.ConfigAuthVerifyRatelimits(rateLimits.
×
NEW
123
                                        WithRewriteRequests(true).
×
NEW
124
                                        MiddlewareGin),
×
125
                        )
×
126
                }
×
127
        }
128
        apiOptions = append(apiOptions, api_http.SetMaxRequestSize(
2✔
129
                int64(c.GetInt(dconfig.SettingMaxRequestSize)),
2✔
130
        ))
2✔
131
        apiHandler := api_http.NewRouter(devauth, db, apiOptions...)
2✔
132

2✔
133
        addr := c.GetString(dconfig.SettingListen)
2✔
134
        l.Printf("listening on %s", addr)
2✔
135

2✔
136
        srv := &http.Server{
2✔
137
                Addr:    addr,
2✔
138
                Handler: apiHandler,
2✔
139
        }
2✔
140

2✔
141
        errChan := make(chan error, 1)
2✔
142
        go func() {
4✔
143
                if err := srv.ListenAndServe(); err != nil && err != http.ErrServerClosed {
2✔
144
                        errChan <- err
×
145
                }
×
146
        }()
147
        quit := make(chan os.Signal, 1)
2✔
148
        signal.Notify(quit, unix.SIGINT, unix.SIGTERM)
2✔
149
        select {
2✔
150
        case sig := <-quit:
2✔
151
                l.Infof("received signal %s: terminating", sig)
2✔
152
        case err = <-errChan:
×
153
                l.Errorf("server terminated unexpectedly: %s", err.Error())
×
154
                return err
×
155
        }
156

157
        l.Info("server shutdown")
2✔
158
        ctxWithTimeout, cancel := context.WithTimeout(context.Background(), 5*time.Second)
2✔
159
        defer cancel()
2✔
160
        if err := srv.Shutdown(ctxWithTimeout); err != nil {
2✔
161
                l.Error("error when shutting down the server ", err)
×
162
        }
×
163
        return nil
2✔
164
}
165

166
func setupRedis(c config.Reader, connStr string) (cache.Cache, *rate.HTTPLimiter, error) {
×
167
        l := log.NewEmpty()
×
168
        l.Infof("setting up redis cache")
×
169

×
170
        ctx, cancel := context.WithTimeout(context.Background(), time.Minute)
×
171
        defer cancel()
×
172
        redisClient, err := redis.ClientFromConnectionString(ctx, connStr)
×
173
        if err != nil {
×
174
                return nil, nil, fmt.Errorf("failed to initialize redis client: %w", err)
×
175
        }
×
176

177
        redisKeyPrefix := c.GetString(dconfig.SettingRedisKeyPrefix)
×
178
        cache := cache.NewRedisCache(
×
179
                redisClient,
×
180
                redisKeyPrefix,
×
181
                c.GetInt(dconfig.SettingRedisLimitsExpSec),
×
182
        )
×
183

×
184
        rateLimiter, err := ratelimits.SetupRedisRateLimits(
×
185
                redisClient, c.GetString(dconfig.SettingRedisKeyPrefix), c,
×
186
        )
×
187
        if err != nil {
×
188
                var configDisabled *ratelimits.ConfigDisabledError
×
189
                if errors.As(err, &configDisabled) {
×
190
                        return cache, nil, nil
×
191
                }
×
192
                return nil, nil, fmt.Errorf("error configuring rate limits: %w", err)
×
193
        }
194
        return cache, rateLimiter, nil
×
195
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc