1 
2 /**
3  *    Copyright (C) 2018-present MongoDB, Inc.
4  *
5  *    This program is free software: you can redistribute it and/or modify
6  *    it under the terms of the Server Side Public License, version 1,
7  *    as published by MongoDB, Inc.
8  *
9  *    This program is distributed in the hope that it will be useful,
10  *    but WITHOUT ANY WARRANTY; without even the implied warranty of
11  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  *    Server Side Public License for more details.
13  *
14  *    You should have received a copy of the Server Side Public License
15  *    along with this program. If not, see
16  *    <http://www.mongodb.com/licensing/server-side-public-license>.
17  *
18  *    As a special exception, the copyright holders give permission to link the
19  *    code of portions of this program with the OpenSSL library under certain
20  *    conditions as described in each individual source file and distribute
21  *    linked combinations including the program with the OpenSSL library. You
22  *    must comply with the Server Side Public License in all respects for
23  *    all of the code used other than as permitted herein. If you modify file(s)
24  *    with this exception, you may extend this exception to your version of the
25  *    file(s), but you are not obligated to do so. If you do not wish to do so,
26  *    delete this exception statement from your version. If you delete this
27  *    exception statement from all source files in the program, then also delete
28  *    it in the license file.
29  */
30 
31 #pragma once
32 
33 #include <memory>
34 
35 #include "mongo/base/status_with.h"
36 #include "mongo/db/keys_collection_cache_reader.h"
37 #include "mongo/db/keys_collection_cache_reader_and_updater.h"
38 #include "mongo/db/keys_collection_document.h"
39 #include "mongo/db/keys_collection_manager.h"
40 #include "mongo/stdx/functional.h"
41 #include "mongo/stdx/mutex.h"
42 #include "mongo/stdx/thread.h"
43 #include "mongo/util/concurrency/notification.h"
44 #include "mongo/util/duration.h"
45 
46 namespace mongo {
47 
48 class OperationContext;
49 class LogicalTime;
50 class ServiceContext;
51 class KeysCollectionClient;
52 
53 namespace keys_collection_manager_util {
54 
55 /**
56  * Returns the amount of time to wait until the monitoring thread should attempt to refresh again.
57  */
58 Milliseconds howMuchSleepNeedFor(const LogicalTime& currentTime,
59                                  const LogicalTime& latestExpiredAt,
60                                  const Milliseconds& interval);
61 
62 }  // namespace keys_collection_manager_util
63 
64 /**
65  * This implementation of the KeysCollectionManager queries the config servers for keys.
66  * It maintains in internal background thread that is used to periodically refresh
67  * the local key cache against the keys collection stored on the config servers.
68  */
69 class KeysCollectionManagerSharding : public KeysCollectionManager {
70 public:
71     static const Seconds kKeyValidInterval;
72 
73     KeysCollectionManagerSharding(std::string purpose,
74                                   std::unique_ptr<KeysCollectionClient> client,
75                                   Seconds keyValidForInterval);
76 
77     /**
78      * Return a key that is valid for the given time and also matches the keyId. Note that this call
79      * can block if it will need to do a refresh.
80      *
81      * Throws ErrorCode::ExceededTimeLimit if it times out.
82      */
83     StatusWith<KeysCollectionDocument> getKeyForValidation(OperationContext* opCtx,
84                                                            long long keyId,
85                                                            const LogicalTime& forThisTime) override;
86 
87     /**
88      * Returns a key that is valid for the given time. Note that unlike getKeyForValidation, this
89      * will never do a refresh.
90      *
91      * Throws ErrorCode::ExceededTimeLimit if it times out.
92      */
93     StatusWith<KeysCollectionDocument> getKeyForSigning(OperationContext* opCtx,
94                                                         const LogicalTime& forThisTime) override;
95 
96     /**
97      * Request this manager to perform a refresh.
98      */
99     void refreshNow(OperationContext* opCtx);
100 
101     /**
102      * Starts a background thread that will constantly update the internal cache of keys.
103      *
104      * Cannot call this after stopMonitoring was called at least once.
105      */
106     void startMonitoring(ServiceContext* service);
107 
108     /**
109      * Stops the background thread updating the cache.
110      */
111     void stopMonitoring();
112 
113     /**
114      * Enable writing new keys.
115      */
116     void enableKeyGenerator(OperationContext* opCtx, bool doEnable);
117 
118     /**
119      * Returns true if the refresher has ever successfully returned keys from the config server.
120      */
121     bool hasSeenKeys();
122 
123     void clearCache() override;
124 
125 private:
126     /**
127      * This is responsible for periodically performing refresh in the background.
128      */
129     class PeriodicRunner {
130     public:
131         using RefreshFunc = stdx::function<StatusWith<KeysCollectionDocument>(OperationContext*)>;
132 
133         /**
134          * Preemptively inform the monitoring thread it needs to perform a refresh. Returns an
135          * object
136          * that gets notified after the current round of refresh is over. Note that being notified
137          * can
138          * mean either of these things:
139          *
140          * 1. An error occurred and refresh was not performed.
141          * 2. No error occurred but no new key was found.
142          * 3. No error occurred and new keys were found.
143          */
144         void refreshNow(OperationContext* opCtx);
145 
146         /**
147          * Sets the refresh function to use.
148          *
149          * Should only be used to bootstrap this refresher with initial strategy. Also waits to make
150          * sure that the old strategy is not being used and will no longer be used after this call.
151          */
152         void setFunc(RefreshFunc newRefreshStrategy);
153 
154         /**
155          * Starts the refresh thread.
156          */
157         void start(ServiceContext* service,
158                    const std::string& threadName,
159                    Milliseconds refreshInterval);
160 
161         /**
162          * Stops the refresh thread.
163          */
164         void stop();
165 
166         /**
167          * Returns true if keys have ever successfully been returned from the config server.
168          */
169         bool hasSeenKeys();
170 
171     private:
172         void _doPeriodicRefresh(ServiceContext* service,
173                                 std::string threadName,
174                                 Milliseconds refreshInterval);
175 
176         stdx::mutex _mutex;  // protects all the member variables below.
177         std::shared_ptr<Notification<void>> _refreshRequest;
178         stdx::condition_variable _refreshNeededCV;
179 
180         stdx::thread _backgroundThread;
181         std::shared_ptr<RefreshFunc> _doRefresh;
182 
183         bool _hasSeenKeys = false;
184         bool _inShutdown = false;
185     };
186 
187     /**
188      * Return a key that is valid for the given time and also matches the keyId.
189      */
190     StatusWith<KeysCollectionDocument> _getKeyWithKeyIdCheck(long long keyId,
191                                                              const LogicalTime& forThisTime);
192 
193     /**
194      * Return a key that is valid for the given time.
195      */
196     StatusWith<KeysCollectionDocument> _getKey(const LogicalTime& forThisTime);
197 
198     std::unique_ptr<KeysCollectionClient> _client;
199     const std::string _purpose;
200     const Seconds _keyValidForInterval;
201 
202     // No mutex needed since the members below have their own mutexes.
203     KeysCollectionCacheReader _keysCache;
204     PeriodicRunner _refresher;
205 };
206 
207 }  // namespace mongo
208