Kea 2.5.4
ha_config.cc
Go to the documentation of this file.
1// Copyright (C) 2018-2022 Internet Systems Consortium, Inc. ("ISC")
2//
3// This Source Code Form is subject to the terms of the Mozilla Public
4// License, v. 2.0. If a copy of the MPL was not distributed with this
5// file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
7#include <config.h>
8
10#include <asiolink/io_error.h>
11#include <asiolink/crypto_tls.h>
12#include <dhcpsrv/cfgmgr.h>
16#include <util/strutil.h>
17#include <ha_log.h>
18#include <ha_config.h>
19#include <ha_service_states.h>
20#include <sstream>
21
22using namespace isc::asiolink;
23using namespace isc::http;
24using namespace isc::util;
25using namespace isc::dhcp;
26
27namespace isc {
28namespace ha {
29
31 : tls_context_(), name_(), url_(""), trust_anchor_(), cert_file_(),
32 key_file_(), role_(STANDBY), auto_failover_(false), basic_auth_() {
33}
34
35void
36HAConfig::PeerConfig::setName(const std::string& name) {
37 // We want to make sure that someone didn't provide a name that consists of
38 // a single space, tab etc.
39 const std::string& s = util::str::trim(name);
40 if (s.empty()) {
41 isc_throw(BadValue, "peer name must not be empty");
42 }
43 name_ = s;
44}
45
46void
47HAConfig::PeerConfig::setRole(const std::string& role) {
48 role_ = stringToRole(role);
49}
50
51std::string
53 std::ostringstream label;
54 label << getName() << " (" << getUrl().toText() << ")";
55 return (label.str());
56}
57
59HAConfig::PeerConfig::stringToRole(const std::string& role) {
60 if (role == "primary") {
62
63 } else if (role == "secondary") {
65
66 } else if (role == "standby") {
68
69 } else if (role == "backup") {
71
72 }
73
74 // Invalid value specified.
75 isc_throw(BadValue, "unsupported value '" << role << "' for role parameter");
76}
77
78std::string
80 switch (role) {
82 return ("primary");
84 return ("secondary");
86 return ("standby");
88 return ("backup");
89 default:
90 ;
91 }
92 return ("");
93}
94
95void
97 const BasicHttpAuthPtr& auth = getBasicAuth();
98 if (!request || !auth) {
99 return;
100 }
101 request->context()->headers_.push_back(BasicAuthHttpHeaderContext(*auth));
102}
103
105 : state_(state), pausing_(STATE_PAUSE_NEVER) {
106}
107
108void
109HAConfig::StateConfig::setPausing(const std::string& pausing) {
110 pausing_ = stringToPausing(pausing);
111}
112
114HAConfig::StateConfig::stringToPausing(const std::string& pausing) {
115 if (pausing == "always") {
116 return (STATE_PAUSE_ALWAYS);
117
118 } else if (pausing == "never") {
119 return (STATE_PAUSE_NEVER);
120
121 } else if (pausing == "once") {
122 return (STATE_PAUSE_ONCE);
123 }
124
125 isc_throw(BadValue, "unsupported value " << pausing << " of 'pause' parameter");
126}
127
128std::string
130 switch (pausing) {
132 return ("always");
133
135 return ("never");
136
137 case STATE_PAUSE_ONCE:
138 return ("once");
139
140 default:
141 ;
142 }
143
144 isc_throw(BadValue, "unsupported pause enumeration " << static_cast<int>(pausing));
145}
146
149 // Return config for the state if it exists already.
150 auto state_config = states_.find(state);
151 if (state_config != states_.end()) {
152 return (state_config->second);
153 }
154
155 // Create config for the state and store its pointer.
156 StateConfigPtr new_state_config(new StateConfig(state));
157 states_[state] = new_state_config;
158
159 return (new_state_config);
160}
161
164 sync_leases_(true), sync_timeout_(60000), sync_page_limit_(10000),
170 restrict_commands_(false), peers_(),
172}
173
175HAConfig::selectNextPeerConfig(const std::string& name) {
176 // Check if there is a configuration for this server name already. We can't
177 // have two servers with the same name.
178 if (peers_.count(name) > 0) {
179 isc_throw(BadValue, "peer with name '" << name << "' already specified");
180 }
181
182 // Name appears to be unique, so let's add it.
183 PeerConfigPtr cfg(new PeerConfig());
184 cfg->setName(name);
185 peers_[name] = cfg;
186
187 // Return this to the caller so as the caller can set parsed configuration
188 // for this peer.
189 return (cfg);
190}
191
192void
193HAConfig::setThisServerName(const std::string& this_server_name) {
194 // Avoid names consisting of spaces, tabs etc.
195 std::string s = util::str::trim(this_server_name);
196 if (s.empty()) {
197 isc_throw(BadValue, "'this-server-name' value must not be empty");
198 }
199
201}
202
203
204void
205HAConfig::setHAMode(const std::string& ha_mode) {
206 ha_mode_ = stringToHAMode(ha_mode);
207}
208
210HAConfig::stringToHAMode(const std::string& ha_mode) {
211 if (ha_mode == "load-balancing") {
212 return (LOAD_BALANCING);
213
214 } else if (ha_mode == "hot-standby") {
215 return (HOT_STANDBY);
216
217 } else if (ha_mode == "passive-backup") {
218 return (PASSIVE_BACKUP);
219 }
220
221 isc_throw(BadValue, "unsupported value '" << ha_mode << "' for mode parameter");
222}
223
224std::string
226 switch (ha_mode) {
227 case LOAD_BALANCING:
228 return ("load-balancing");
229 case HOT_STANDBY:
230 return ("hot-standby");
231 case PASSIVE_BACKUP:
232 return ("passive-backup");
233 default:
234 ;
235 }
236 return ("");
237}
238
240HAConfig::getPeerConfig(const std::string& name) const {
241 auto peer = peers_.find(name);
242 if (peer == peers_.end()) {
243 isc_throw(InvalidOperation, "no configuration specified for server " << name);
244 }
245
246 return (peer->second);
247}
248
252 for (auto peer = servers.begin(); peer != servers.end(); ++peer) {
253 if (peer->second->getRole() != HAConfig::PeerConfig::BACKUP) {
254 return (peer->second);
255 }
256 }
257
258 isc_throw(InvalidOperation, "no failover partner server found for this"
259 " server " << getThisServerName());
260}
261
265}
266
270 copy.erase(getThisServerName());
271 return (copy);
272}
273
274void
276 // Peers configurations must be provided.
277 if (peers_.count(getThisServerName()) == 0) {
278 isc_throw(HAConfigValidationError, "no peer configuration specified for the '"
279 << getThisServerName() << "'");
280 }
281
282 // Gather all the roles and see how many occurrences of each role we get.
283 std::map<PeerConfig::Role, unsigned> peers_cnt;
284 for (auto p = peers_.begin(); p != peers_.end(); ++p) {
285 if (!p->second->getUrl().isValid()) {
286 isc_throw(HAConfigValidationError, "invalid URL: "
287 << p->second->getUrl().getErrorMessage()
288 << " for server " << p->second->getName());
289 }
290
291 // The hostname must be an address, not a name.
292 IOAddress addr("::");
293 try {
294 addr = IOAddress(p->second->getUrl().getStrippedHostname());
295 } catch (const IOError& ex) {
297 << p->second->getUrl().toText()
298 << "': " << ex.what()
299 << " for server " << p->second->getName());
300 }
301
302 // Check TLS setup.
303 Optional<std::string> ca = p->second->getTrustAnchor();
304 Optional<std::string> cert = p->second->getCertFile();
305 Optional<std::string> key = p->second->getKeyFile();
306 // When not configured get the value from the global level.
307 if (ca.unspecified()) {
308 ca = trust_anchor_;
309 }
310 if (cert.unspecified()) {
311 cert = cert_file_;
312 }
313 if (key.unspecified()) {
314 key = key_file_;
315 }
316 bool have_ca = (!ca.unspecified() && !ca.get().empty());
317 bool have_cert = (!cert.unspecified() && !cert.get().empty());
318 bool have_key = (!key.unspecified() && !key.get().empty());
319 bool use_tls = (have_ca || have_cert || have_key);
320 if (use_tls) {
321 try {
322 // TLS is used: all 3 parameters are required.
323 if (!have_ca) {
324 isc_throw(HAConfigValidationError, "trust-anchor parameter"
325 << " is missing or empty: all or none of"
326 << " TLS parameters must be set");
327 }
328 if (!have_cert) {
329 isc_throw(HAConfigValidationError, "cert-file parameter"
330 << " is missing or empty: all or none of"
331 << " TLS parameters must be set");
332 }
333 if (!have_key) {
334 isc_throw(HAConfigValidationError, "key-file parameter"
335 << " is missing or empty: all or none of"
336 << " TLS parameters must be set");
337 }
338 TlsRole tls_role = TlsRole::CLIENT;
339 bool cert_required = true;
340 // The peer entry for myself will be used for the server side.
341 if (p->second->getName() == getThisServerName()) {
342 tls_role = TlsRole::SERVER;
343 cert_required = getRequireClientCerts();
344 }
345 TlsContext::configure(p->second->tls_context_,
346 tls_role,
347 ca.get(),
348 cert.get(),
349 key.get(),
350 cert_required);
351 } catch (const isc::Exception& ex) {
352 isc_throw(HAConfigValidationError, "bad TLS config for server "
353 << p->second->getName() << ": " << ex.what());
354 }
355 } else {
356 // Refuse HTTPS scheme when TLS is not enabled.
357 if (p->second->getUrl().getScheme() == Url::HTTPS) {
359 << p->second->getUrl().toText()
360 << "': https scheme is not supported"
361 << " for server " << p->second->getName()
362 << " where TLS is disabled");
363 }
364 }
365
366 ++peers_cnt[p->second->getRole()];
367 }
368
369 // Only one primary server allowed.
370 if (peers_cnt.count(PeerConfig::PRIMARY) && (peers_cnt[PeerConfig::PRIMARY] > 1)) {
371 isc_throw(HAConfigValidationError, "multiple primary servers specified");
372 }
373
374 // Only one secondary server allowed.
375 if (peers_cnt.count(PeerConfig::SECONDARY) && (peers_cnt[PeerConfig::SECONDARY] > 1)) {
376 isc_throw(HAConfigValidationError, "multiple secondary servers specified");
377 }
378
379 // Only one standby server allowed.
380 if (peers_cnt.count(PeerConfig::STANDBY) && (peers_cnt[PeerConfig::STANDBY] > 1)) {
381 isc_throw(HAConfigValidationError, "multiple standby servers specified");
382 }
383
384 if (ha_mode_ == LOAD_BALANCING) {
385 // Standby servers not allowed in load balancing configuration.
386 if (peers_cnt.count(PeerConfig::STANDBY) > 0) {
387 isc_throw(HAConfigValidationError, "standby servers not allowed in the load "
388 "balancing configuration");
389 }
390
391 // Require one secondary server in the load balancing configuration.
392 if (peers_cnt.count(PeerConfig::SECONDARY) == 0) {
393 isc_throw(HAConfigValidationError, "secondary server required in the load"
394 " balancing configuration");
395 }
396
397 // Require one primary server in the load balancing configuration.
398 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
399 isc_throw(HAConfigValidationError, "primary server required in the load"
400 " balancing configuration");
401 }
402
403 // In the load-balancing mode the wait-backup-ack must be false.
404 if (wait_backup_ack_) {
405 isc_throw(HAConfigValidationError, "'wait-backup-ack' must be set to false in the"
406 " load balancing configuration");
407 }
408
409 } else if (ha_mode_ == HOT_STANDBY) {
410 // Secondary servers not allowed in the hot standby configuration.
411 if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
412 isc_throw(HAConfigValidationError, "secondary servers not allowed in the hot"
413 " standby configuration");
414 }
415
416 // Require one standby server in the hot standby configuration.
417 if (peers_cnt.count(PeerConfig::STANDBY) == 0) {
418 isc_throw(HAConfigValidationError, "standby server required in the hot"
419 " standby configuration");
420 }
421
422 // Require one primary server in the hot standby configuration.
423 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
424 isc_throw(HAConfigValidationError, "primary server required in the hot"
425 " standby configuration");
426 }
427
428 // In the hot-standby mode the wait-backup-ack must be false.
429 if (wait_backup_ack_) {
430 isc_throw(HAConfigValidationError, "'wait-backup-ack' must be set to false in the"
431 " hot standby configuration");
432 }
433
434 // The server must not transition to communication-recovery state in
435 // hot-standby mode.
436 if (delayed_updates_limit_ > 0) {
437 isc_throw(HAConfigValidationError, "'delayed-updates-limit' must be set to 0 in"
438 " the hot standby configuration");
439 }
440
441 } else if (ha_mode_ == PASSIVE_BACKUP) {
442 if (peers_cnt.count(PeerConfig::SECONDARY) > 0) {
443 isc_throw(HAConfigValidationError, "secondary servers not allowed in the"
444 " passive backup configuration");
445 }
446
447 if (peers_cnt.count(PeerConfig::STANDBY) > 0) {
448 isc_throw(HAConfigValidationError, "standby servers not allowed in the"
449 " passive backup configuration");
450 }
451
452 if (peers_cnt.count(PeerConfig::PRIMARY) == 0) {
453 isc_throw(HAConfigValidationError, "primary server required in the"
454 " passive backup configuration");
455 }
456
457 // The server must not transition to communication-recovery state in
458 // passive-backup mode.
459 if (delayed_updates_limit_ > 0) {
460 isc_throw(HAConfigValidationError, "'delayed-updates-limit' must be set to 0 in"
461 " the passive backup configuration");
462 }
463 }
464
465 // We get it from staging because applying the DHCP multi-threading configuration
466 // occurs after library loading during the (re)configuration process.
467 auto mcfg = CfgMgr::instance().getStagingCfg()->getDHCPMultiThreading();
468 bool dhcp_mt_enabled = false;
469 uint32_t dhcp_threads = 0;
470 uint32_t dummy_queue_size = 0;
471 CfgMultiThreading::extract(mcfg, dhcp_mt_enabled, dhcp_threads, dummy_queue_size);
472
474 if (!dhcp_mt_enabled) {
475 // HA+MT requires DHCP multi-threading.
478 return;
479 }
480
481 // When DHCP threads is configured as zero, we should auto-detect.
482 if (!dhcp_threads) {
483 dhcp_threads = MultiThreadingMgr::detectThreadCount();
484 // If machine says it cannot support threads.
485 if (!dhcp_threads) {
488 return;
489 }
490 }
491
492 // If http_listener_threads_ is 0, then we use the same number of
493 // threads as DHCP does.
494 if (http_listener_threads_ == 0) {
495 http_listener_threads_ = dhcp_threads;
496 }
497
498 // If http_client_threads_ is 0, then we use the same number of
499 // threads as DHCP does.
500 if (http_client_threads_ == 0) {
501 http_client_threads_ = dhcp_threads;
502 }
503 } else {
504 if (dhcp_mt_enabled) {
505 // DHCP multi-threading requires HA+MT for optimal performance.
507 return;
508 }
509 }
510}
511
512} // end of namespace isc::ha
513} // end of namespace isc
A generic exception that is thrown if a parameter given to a method is considered invalid in that con...
This is a base class for exceptions thrown from the DNS library module.
virtual const char * what() const
Returns a C-style character string of the cause of the exception.
A generic exception that is thrown if a function is called in a prohibited way.
Exception thrown when configuration validation fails.
Definition: ha_config.h:26
HA peer configuration.
Definition: ha_config.h:53
std::string getLogLabel() const
Returns a string identifying a server used in logging.
Definition: ha_config.cc:52
void addBasicAuthHttpHeader(http::PostHttpRequestJsonPtr request) const
Adds a basic HTTP authentication header to a request when credentials are specified.
Definition: ha_config.cc:96
Role
Server's role in the High Availability setup.
Definition: ha_config.h:70
void setRole(const std::string &role)
Sets servers role.
Definition: ha_config.cc:47
static std::string roleToString(const HAConfig::PeerConfig::Role &role)
Returns role name.
Definition: ha_config.cc:79
static Role stringToRole(const std::string &role)
Decodes role provided as a string.
Definition: ha_config.cc:59
void setName(const std::string &name)
Sets server name.
Definition: ha_config.cc:36
Configuration specific to a single HA state.
Definition: ha_config.h:235
static util::StatePausing stringToPausing(const std::string &pausing)
Converts pausing mode from the textual form.
Definition: ha_config.cc:114
void setPausing(const std::string &pausing)
Sets pausing mode for the given state.
Definition: ha_config.cc:109
StateConfig(const int state)
Constructor.
Definition: ha_config.cc:104
static std::string pausingToString(const util::StatePausing &pausing)
Returns pausing mode in the textual form.
Definition: ha_config.cc:129
State machine configuration information.
Definition: ha_config.h:287
StateConfigPtr getStateConfig(const int state)
Returns pointer to the state specific configuration.
Definition: ha_config.cc:148
uint32_t max_response_delay_
Max delay in response to heartbeats.
Definition: ha_config.h:801
uint32_t http_listener_threads_
Number of HTTP listener threads.
Definition: ha_config.h:808
uint32_t sync_page_limit_
Page size limit while synchronizing leases.
Definition: ha_config.h:796
std::string getThisServerName() const
Returns name of this server.
Definition: ha_config.h:330
bool require_client_certs_
Require client certs flag.
Definition: ha_config.h:813
bool http_dedicated_listener_
Enable use of own HTTP listener.
Definition: ha_config.h:807
HAMode
Mode of operation.
Definition: ha_config.h:42
void validate()
Validates configuration.
Definition: ha_config.cc:275
uint32_t delayed_updates_limit_
Maximum number of lease updates held for later send in communication-recovery.
Definition: ha_config.h:798
PeerConfigPtr getThisServerConfig() const
Returns configuration of this server.
Definition: ha_config.cc:263
bool getRequireClientCerts() const
Returns require-client-certs.
Definition: ha_config.h:705
uint32_t max_rejected_lease_updates_
Limit of rejected lease updates before termination.
Definition: ha_config.h:804
std::map< std::string, PeerConfigPtr > PeerConfigMap
Map of the servers' configurations.
Definition: ha_config.h:232
void setHAMode(const std::string &ha_mode)
Sets new mode of operation.
Definition: ha_config.cc:205
HAMode ha_mode_
Mode of operation.
Definition: ha_config.h:792
bool send_lease_updates_
Send lease updates to partner?
Definition: ha_config.h:793
uint32_t max_unacked_clients_
Maximum number of unacked clients.
Definition: ha_config.h:803
PeerConfigMap peers_
Map of peers' configurations.
Definition: ha_config.h:815
uint32_t max_ack_delay_
Maximum DHCP message ack delay.
Definition: ha_config.h:802
util::Optional< std::string > cert_file_
Certificate file.
Definition: ha_config.h:811
bool restrict_commands_
Restrict commands to HA flag.
Definition: ha_config.h:814
void setThisServerName(const std::string &this_server_name)
Sets name of this server.
Definition: ha_config.cc:193
PeerConfigMap getOtherServersConfig() const
Returns configuration of other servers.
Definition: ha_config.cc:268
PeerConfigPtr getFailoverPeerConfig() const
Returns configuration of the partner which takes part in failover.
Definition: ha_config.cc:250
PeerConfigPtr getPeerConfig(const std::string &name) const
Returns configuration of the specified server.
Definition: ha_config.cc:240
util::Optional< std::string > key_file_
Private key file.
Definition: ha_config.h:812
PeerConfigPtr selectNextPeerConfig(const std::string &name)
Creates and returns pointer to the new peer's configuration.
Definition: ha_config.cc:175
bool sync_leases_
Synchronize databases on startup?
Definition: ha_config.h:794
bool wait_backup_ack_
Wait for lease update ack from backup?
Definition: ha_config.h:805
StateMachineConfigPtr state_machine_
State machine configuration.
Definition: ha_config.h:816
HAConfig()
Constructor.
Definition: ha_config.cc:162
util::Optional< std::string > trust_anchor_
Trust anchor.
Definition: ha_config.h:810
static HAMode stringToHAMode(const std::string &ha_mode)
Decodes HA mode provided as string.
Definition: ha_config.cc:210
uint32_t http_client_threads_
Number of HTTP client threads.
Definition: ha_config.h:809
uint32_t sync_timeout_
Timeout for syncing lease database (ms)
Definition: ha_config.h:795
boost::shared_ptr< StateConfig > StateConfigPtr
Pointer to the state configuration.
Definition: ha_config.h:280
bool enable_multi_threading_
Enable multi-threading.
Definition: ha_config.h:806
uint32_t heartbeat_delay_
Heartbeat delay in milliseconds.
Definition: ha_config.h:800
static std::string HAModeToString(const HAMode &ha_mode)
Returns HA mode name.
Definition: ha_config.cc:225
std::string this_server_name_
This server name.
Definition: ha_config.h:791
boost::shared_ptr< PeerConfig > PeerConfigPtr
Pointer to the server's configuration.
Definition: ha_config.h:229
T get() const
Retrieves the encapsulated value.
Definition: optional.h:114
void unspecified(bool unspecified)
Modifies the flag that indicates whether the value is specified or unspecified.
Definition: optional.h:136
TLS API.
const Name & name_
Definition: dns/message.cc:701
#define isc_throw(type, stream)
A shortcut macro to insert known values into exception arguments.
#define LOG_INFO(LOGGER, MESSAGE)
Macro to conveniently test info output and log it.
Definition: macros.h:20
#define LOG_WARN(LOGGER, MESSAGE)
Macro to conveniently test warn output and log it.
Definition: macros.h:26
ElementPtr copy(ConstElementPtr from, int level)
Copy the data up to a nesting level.
Definition: data.cc:1414
const isc::log::MessageID HA_CONFIG_DHCP_MT_DISABLED
Definition: ha_messages.h:28
isc::log::Logger ha_logger("ha-hooks")
Definition: ha_log.h:17
const isc::log::MessageID HA_CONFIG_DHCP_MT_DISABLED_AND_KEA_MT_ENABLED
Definition: ha_messages.h:29
const isc::log::MessageID HA_CONFIG_SYSTEM_MT_UNSUPPORTED
Definition: ha_messages.h:35
boost::shared_ptr< BasicHttpAuth > BasicHttpAuthPtr
Type of pointers to basic HTTP authentication objects.
Definition: basic_auth.h:70
boost::shared_ptr< PostHttpRequestJson > PostHttpRequestJsonPtr
Pointer to PostHttpRequestJson.
string trim(const string &instring)
Trim Leading and Trailing Spaces.
Definition: strutil.cc:53
Definition: edns.h:19
StatePausing
State machine pausing modes.
Definition: state_model.h:45
@ STATE_PAUSE_ALWAYS
Definition: state_model.h:46
@ STATE_PAUSE_ONCE
Definition: state_model.h:48
@ STATE_PAUSE_NEVER
Definition: state_model.h:47
Defines the logger used by the top-level component of kea-lfc.
Represents basic HTTP authentication header.
Definition: basic_auth.h:73