]> granicus.if.org Git - icinga2/commitdiff
Implement support for migrating certificates to /var/lib/icinga2/certs 5682/head
authorGunnar Beutner <gunnar.beutner@icinga.com>
Mon, 16 Oct 2017 13:32:57 +0000 (15:32 +0200)
committerMichael Friedrich <michael.friedrich@icinga.com>
Fri, 20 Oct 2017 12:06:02 +0000 (14:06 +0200)
This commit includes documentation too.

Signed-off-by: Michael Friedrich <michael.friedrich@icinga.com>
18 files changed:
doc/02-getting-started.md
doc/06-distributed-monitoring.md
doc/09-object-types.md
doc/16-upgrading-icinga-2.md
etc/icinga2/features-available/api.conf
lib/base/function.ti
lib/base/type.hpp
lib/base/utility.cpp
lib/cli/nodesetupcommand.cpp
lib/cli/nodewizardcommand.cpp
lib/remote/apilistener.cpp
lib/remote/apilistener.hpp
lib/remote/apilistener.ti
lib/remote/jsonrpcconnection-pki.cpp
lib/remote/typequeryhandler.cpp
tools/mkclass/class_lexer.ll
tools/mkclass/classcompiler.cpp
tools/mkclass/classcompiler.hpp

index 4f4af739cfd93ea93a74a58d4c59a82f5ef151f9..30cc38f829d4bca7e30df397960af2217a31570a 100644 (file)
@@ -175,11 +175,11 @@ By default Icinga 2 uses the following files and directories:
   /usr/lib\*/icinga2                           | Libraries and the Icinga 2 binary (use `find /usr -type f -name icinga2` to locate the binary path).
   /usr/share/doc/icinga2                       | Documentation files that come with Icinga 2.
   /usr/share/icinga2/include                   | The Icinga Template Library and plugin command configuration.
+  /var/lib/icinga2                             | Icinga 2 state file, cluster log, master CA, node certificates and configuration files (cluster, api).
   /var/run/icinga2                             | PID file.
   /var/run/icinga2/cmd                         | Command pipe and Livestatus socket.
   /var/cache/icinga2                           | status.dat/objects.cache, icinga2.debug files
   /var/spool/icinga2                           | Used for performance data spool files.
-  /var/lib/icinga2                             | Icinga 2 state file, cluster log, local CA and configuration files (cluster, api).
   /var/log/icinga2                             | Log file location and compat/ directory for the CompatLogger feature.
 
 FreeBSD uses slightly different paths:
@@ -194,11 +194,11 @@ By default Icinga 2 uses the following files and directories:
   /usr/local/lib/icinga2              | Libraries and the Icinga 2 binary.
   /usr/local/share/doc/icinga2        | Documentation files that come with Icinga 2.
   /usr/local/share/icinga2/include    | The Icinga Template Library and plugin command configuration.
+  /var/lib/icinga2                    | Icinga 2 state file, cluster log, master CA, node certificates and configuration files (cluster, api).
   /var/run/icinga2                    | PID file.
   /var/run/icinga2/cmd                | Command pipe and Livestatus socket.
   /var/cache/icinga2                  | status.dat/objects.cache, icinga2.debug files
   /var/spool/icinga2                  | Used for performance data spool files.
-  /var/lib/icinga2                    | Icinga 2 state file, cluster log, local CA and configuration files (cluster, api).
   /var/log/icinga2                    | Log file location and compat/ directory for the CompatLogger feature.
 
 ## Setting up Check Plugins <a id="setting-up-check-plugins"></a>
@@ -877,5 +877,6 @@ popular addons is available in the
 Ensure to include the following in your backups:
 
 * Configuration files in `/etc/icinga2`
-* Runtime files in `/var/lib/icinga2` (the master's CA is stored here as well)
+* Certificate files in `/var/lib/icinga2/ca` (Master CA key pair) and `/var/lib/icinga2/certs` (node certificates)
+* Runtime files in `/var/lib/icinga2`
 * Optional: IDO database backup
index 3ef8e1e57c149cdd6568b4c4c98f8370d3d5f727..a789ebcd0c3a077a0157a66cc5e7427b8061453c 100644 (file)
@@ -553,6 +553,11 @@ The setup wizard will ensure that the following steps are taken:
 
 You can verify that the certificate files are stored in the `/var/lib/icinga2/certs` directory.
 
+> **Note**
+>
+> The certificate location changed in v2.8 to `/var/lib/icinga2/certs`. Please read the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths)
+> for more details.
+
 > **Note**
 >
 > If the client is not directly connected to the certificate signing master,
@@ -2358,6 +2363,11 @@ Sign the CSR with the previously created CA:
 
     [root@icinga2-master1.localdomain /root]# icinga2 pki sign-csr --csr icinga2-master1.localdomain.csr --cert icinga2-master1.localdomain
 
+> **Note**
+>
+> The certificate location changed in v2.8 to `/var/lib/icinga2/certs`. Please read the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths)
+> for more details.
+
 Copy the host's certificate files and the public CA certificate to `/var/lib/icinga2/certs`:
 
     [root@icinga2-master1.localdomain /root]# mkdir -p /var/lib/icinga2/certs
@@ -2444,7 +2454,12 @@ host/port you can specify it like this:
 
 #### Node Setup with Satellites/Clients <a id="distributed-monitoring-automation-cli-node-setup-satellite-client"></a>
 
-Make sure that the `/var/lib/icinga2/certs` exists and is owned by the `icinga`
+> **Note**
+>
+> The certificate location changed in v2.8 to `/var/lib/icinga2/certs`. Please read the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths)
+> for more details.
+
+Make sure that the `/var/lib/icinga2/certs` directory exists and is owned by the `icinga`
 user (or the user Icinga 2 is running as).
 
     [root@icinga2-client1.localdomain /]# mkdir -p /var/lib/icinga2/certs
@@ -2499,7 +2514,7 @@ Pass the following details to the `node setup` CLI command:
   Accept config       | **Optional.** Whether this node accepts configuration sync from the master node (required for [config sync mode](06-distributed-monitoring.md#distributed-monitoring-top-down-config-sync)).
   Accept commands     | **Optional.** Whether this node accepts command execution messages from the master node (required for [command endpoint mode](06-distributed-monitoring.md#distributed-monitoring-top-down-command-endpoint)).
 
-Example:
+Example for Icinga 2 v2.8:
 
     [root@icinga2-client1.localdomain /]# icinga2 node setup --ticket ead2d570e18c78abf285d6b85524970a0f69c22d \
     --cn icinga2-client1.localdomain \
index 4a94f2534f5ebf31454cb0873e216407d8029a10..036534344a8cecf342636a1ed4ffc52bdbf7a962 100644 (file)
@@ -41,9 +41,8 @@ Example:
 
 ```
 object ApiListener "api" {
-  cert_path = LocalStateDir + "/lib/icinga2/certs/" + NodeName + ".crt"
-  key_path = LocalStateDir + "/lib/icinga2/certs/" + NodeName + ".key"
-  ca_path = LocalStateDir + "/lib/icinga2/certs/ca.crt"
+  accept_commands = true
+  accept_config = true
 
   ticket_salt = TicketSalt
 }
@@ -53,9 +52,9 @@ Configuration Attributes:
 
   Name                                  | Type                  | Description
   --------------------------------------|-----------------------|----------------------------------
-  cert\_path                            | String                | **Required.** Path to the public key.
-  key\_path                             | String                | **Required.** Path to the private key.
-  ca\_path                              | String                | **Required.** Path to the CA certificate file.
+  cert\_path                            | String                | **Deprecated.** Path to the public key.
+  key\_path                             | String                | **Deprecated.** Path to the private key.
+  ca\_path                              | String                | **Deprecated.** Path to the CA certificate file.
   ticket\_salt                          | String                | **Optional.** Private key for [CSR auto-signing](06-distributed-monitoring.md#distributed-monitoring-setup-csr-auto-signing). **Required** for a signing master instance.
   crl\_path                             | String                | **Optional.** Path to the CRL file.
   bind\_host                            | String                | **Optional.** The IP address the api listener should be bound to. Defaults to `0.0.0.0`.
@@ -69,6 +68,20 @@ Configuration Attributes:
   access\_control\_allow\_headers       | String                | **Optional.** Used in response to a preflight request to indicate which HTTP headers can be used when making the actual request. Defaults to `Authorization`. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Headers)
   access\_control\_allow\_methods       | String                | **Optional.** Used in response to a preflight request to indicate which HTTP methods can be used when making the actual request. Defaults to `GET, POST, PUT, DELETE`. [(MDN docs)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Access-Control-Allow-Methods)
 
+The ApiListener type expects its certificate files to be in the following locations:
+
+  Type                 | Location
+  ---------------------|-------------------------------------
+  Private key          | `LocalStateDir + "/lib/icinga2/certs/" + NodeName + ".key"`
+  Certificate file     | `LocalStateDir + "/lib/icinga2/certs/" + NodeName + ".crt"`
+  CA certificate file  | `LocalStateDir + "/lib/icinga2/certs/ca.crt"`
+
+If the deprecated attributes `cert_path`, `key_path` and/or `ca_path` are specified Icinga 2
+copies those files to the new location in `LocalStateDir + "/lib/icinga2/certs"` unless the
+file(s) there are newer.
+
+Please check the [upgrading chapter](16-upgrading-icinga-2.md#upgrading-to-2-8-certificate-paths) for more details.
+
 ## ApiUser <a id="objecttype-apiuser"></a>
 
 ApiUser objects are used for authentication against the [Icinga 2 API](12-icinga2-api.md#icinga2-api-authentication).
index c5c85402d9976b39d6d23666579e02b9cca28f66..99962280aa5f5a76c52a3b671082f734faf302b6 100644 (file)
@@ -10,19 +10,103 @@ are scheme updates for the IDO database.
 The default certificate path was changed from `/etc/icinga2/pki` to
 `/var/lib/icinga2/certs`.
 
+  Old Path                                           | New Path
+  ---------------------------------------------------|---------------------------------------------------
+  `/etc/icinga2/pki/icinga2-client1.localdomain.crt` | `/var/lib/icinga2/certs/icinga2-client1.localdomain.crt`
+  `/etc/icinga2/pki/icinga2-client1.localdomain.key` | `/var/lib/icinga2/certs/icinga2-client1.localdomain.key`
+  `/etc/icinga2/pki/ca.crt`                          | `/var/lib/icinga2/certs/ca.crt`
+
 This applies to Windows clients in the same way: `%ProgramData%\etc\icinga2\pki`
-was moved to `%ProgramData%`\var\lib\icinga2\certs`.
+was moved to `%ProgramData%\var\lib\icinga2\certs`.
+
+  Old Path                                                        | New Path
+  ----------------------------------------------------------------|----------------------------------------------------------------
+  `%ProgramData%\etc\icinga2\pki\icinga2-client1.localdomain.crt` | `%ProgramData%\var\lib\icinga2\certs\icinga2-client1.localdomain.crt`
+  `%ProgramData%\etc\icinga2\pki\icinga2-client1.localdomain.key` | `%ProgramData%\var\lib\icinga2\certs\icinga2-client1.localdomain.key`
+  `%ProgramData%\etc\icinga2\pki\ca.crt`                          | `%ProgramData%\var\lib\icinga2\certs\ca.crt`
+
+
+> **Note**
+>
+> The default expected path for client certificates is `/var/lib/icinga2/certs/ + NodeName + {.crt,.key}`.
+> The `NodeName` constant is usually the FQDN and certificate common name (CN). Check the [conventions](06-distributed-monitoring.md#distributed-monitoring-conventions)
+> section inside the Distributed Monitoring chapter.
 
 The [setup CLI commands](06-distributed-monitoring.md#distributed-monitoring-setup-master) and the
 default [ApiListener configuration](06-distributed-monitoring.md#distributed-monitoring-apilistener)
 have been adjusted to these paths too.
 
+The [ApiListener](09-object-types.md#objecttype-apilistener) object attributes `cert_path`, `key_path`
+and `ca_path` have been deprecated and removed from the example configuration.
+
+#### Migration Path <a id="upgrading-to-2-8-certificate-paths-migration-path"></a>
+
+> **Note**
+>
+> Icinga 2 automatically migrates the certificates to the new default location if they
+> are configured and detected in `/etc/icinga2/pki`.
+
+During startup, the migration kicks in and ensures to copy the certificates to the new
+location. This will also happen if someone updates the certificate files in `/etc/icinga2/pki`
+to ensure that the new certificate location always has the latest files.
+
+This has been implemented in the Icinga 2 binary to ensure it works on both Linux/Unix
+and the Windows platform.
+
+If you are not using the built-in CLI commands and setup wizards to deploy the client certificates,
+please ensure to update your deployment tools/scripts. This mainly affects
+
+* Puppet modules
+* Ansible playbooks
+* Chef cookbooks
+* Salt recipes
+* Custom scripts, e.g. Windows Powershell or self-made implementations
+
+In order to support a smooth migration between versions older than 2.8 and future releases,
+the built-in certificate migration path is planned to exist as long as the deprecated
+`ApiListener` object attributes exist.
+
+You are safe to use the existing configuration paths inside the `api` feature. If you plan your migration,
+look at the following example taken from the Director Linux deployment script for clients.
+
+* Ensure that the default certificate path is changed from `/etc/icinga2/pki` to `/var/lib/icinga2/certs`.
+
+```
+-ICINGA2_SSL_DIR="${ICINGA2_CONF_DIR}/pki"
++ICINGA2_SSL_DIR="${ICINGA2_STATE_DIR}/lib/icinga2/certs"
+```
+
+* Remove the ApiListener configuration attributes.
+
+```
+object ApiListener "api" {
+-  cert_path = SysconfDir + "/icinga2/pki/${ICINGA2_NODENAME}.crt"
+-  key_path = SysconfDir + "/icinga2/pki/${ICINGA2_NODENAME}.key"
+-  ca_path = SysconfDir + "/icinga2/pki/ca.crt"
+  accept_commands = true
+  accept_config = true
+}
+```
+
+Test the script with a fresh client installation before putting it into production.
+
+> **Tip**
+>
+> Please support module and script developers in their migration. If you find
+> any project which would require these changes, create an issue or a patchset in a PR
+> and help them out. Thanks in advance!
+
+
 ### Removed Bottom Up Client Mode <a id="upgrading-to-2-8-removed-bottom-up-client-mode"></a>
 
 This client mode was deprecated in 2.6 and was removed in 2.8.
 
 The node CLI command does not provide `list` or `update-config` anymore.
 
+> **Note**
+>
+> The old migration guide can be found on [GitHub](https://github.com/Icinga/icinga2/blob/v2.7.0/doc/06-distributed-monitoring.md#bottom-up-migration-to-top-down-).
+
 The clients don't need to have a local `conf.d` directory included.
 The setup wizards for Linux and Windows attempt to disable this by default.
 
@@ -31,6 +115,7 @@ You are advised to [migrate](https://github.com/Icinga/icinga2/issues/4798)
 any existing configuration to the "top down" mode with the help of the
 Icinga Director or config management tools such as Puppet, Ansible, etc.
 
+
 ### Removed Classic UI Config Package <a id="upgrading-to-2-8-removed-classicui-config-package"></a>
 
 The config meta package `classicui-config` and the configuration files
index c24b09a4db5f8c545e785e09b6c600592ed696ba..b072a440687e59970f1d79218df7c49c5b422279 100644 (file)
@@ -3,9 +3,8 @@
  */
 
 object ApiListener "api" {
-  cert_path = LocalStateDir + "/lib/icinga2/certs/" + NodeName + ".crt"
-  key_path = LocalStateDir + "/lib/icinga2/certs/" + NodeName + ".key"
-  ca_path = LocalStateDir + "/lib/icinga2/certs/ca.crt"
+  //accept_config = false
+  //accept_commands = false
 
   ticket_salt = TicketSalt
 }
index b42e90ee824dcb717917875a7eebd4ceb6fda823..be50525c51233963aa166983a7fd5670be0d874c 100644 (file)
@@ -28,7 +28,7 @@ abstract class Function
 {
        [config] String "name";
        [config] bool side_effect_free;
-       [config] bool deprecated;
+       [config] bool "deprecated";
        [config] Array::Ptr arguments;
 };
 
index fdc40c21e0907997d61f6bf96a68048b1dc3064c..52aa753a51284a227961c32c3b732026e7f4c7b6 100644 (file)
@@ -39,7 +39,8 @@ enum FieldAttribute
        FARequired = 256,
        FANavigation = 512,
        FANoUserModify = 1024,
-       FANoUserView = 2048
+       FANoUserView = 2048,
+       FADeprecated = 4096,
 };
 
 class Type;
index 6cb2ed107ed34eef0efb5a27f473727a337a284a..836393b03224778cc1a4000ad82555964ff4756f 100644 (file)
@@ -816,6 +816,10 @@ void Utility::CollectPaths(const String& path, std::vector<String>& paths)
        paths.push_back(path);
 }
 
+/*
+ * Copies a source file to a target location.
+ * Caller must ensure that the target's base directory exists and is writable.
+ */
 void Utility::CopyFile(const String& source, const String& target)
 {
        std::ifstream ifs(source.CStr(), std::ios::binary);
index 302dccae85b1ddb0fc92c75dc336a02dd943c9a5..c452c226799a8bec67086bdda1eaff952cc65e7d 100644 (file)
@@ -173,10 +173,7 @@ int NodeSetupCommand::SetupMaster(const boost::program_options::variables_map& v
        fp << "/**\n"
            << " * The API listener is used for distributed monitoring setups.\n"
            << " */\n"
-           << "object ApiListener \"api\" {\n"
-           << "  cert_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".crt\"\n"
-           << "  key_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".key\"\n"
-           << "  ca_path = LocalStateDir + \"/lib/icinga2/certs/ca.crt\"\n";
+           << "object ApiListener \"api\" {\n";
 
        if (vm.count("listen")) {
                std::vector<String> tokens;
@@ -378,10 +375,7 @@ int NodeSetupCommand::SetupNode(const boost::program_options::variables_map& vm,
        fp << "/**\n"
            << " * The API listener is used for distributed monitoring setups.\n"
            << " */\n"
-           << "object ApiListener \"api\" {\n"
-           << "  cert_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".crt\"\n"
-           << "  key_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".key\"\n"
-           << "  ca_path = LocalStateDir + \"/lib/icinga2/certs/ca.crt\"\n";
+           << "object ApiListener \"api\" {\n";
 
        if (vm.count("listen")) {
                std::vector<String> tokens;
index 0cf4636ff54a52dfefe7a5fe1014d7f440fc9d7a..c3c003204e7ff1a5003b34fabbddbe5f85ba5389 100644 (file)
@@ -472,10 +472,6 @@ wizard_ticket:
            << " * The API listener is used for distributed monitoring setups.\n"
            << " */\n"
            << "object ApiListener \"api\" {\n"
-           << "  cert_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".crt\"\n"
-           << "  key_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".key\"\n"
-           << "  ca_path = LocalStateDir + \"/lib/icinga2/certs/ca.crt\"\n"
-           << "\n"
            << "  accept_config = " << acceptConfig << "\n"
            << "  accept_commands = " << acceptCommands << "\n";
 
@@ -629,10 +625,7 @@ int NodeWizardCommand::MasterSetup(void) const
        fp << "/**\n"
            << " * The API listener is used for distributed monitoring setups.\n"
            << " */\n"
-           << "object ApiListener \"api\" {\n"
-           << "  cert_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".crt\"\n"
-           << "  key_path = LocalStateDir + \"/lib/icinga2/certs/\" + NodeName + \".key\"\n"
-           << "  ca_path = LocalStateDir + \"/lib/icinga2/certs/ca.crt\"\n";
+           << "object ApiListener \"api\" {\n";
 
        if (!bindHost.IsEmpty())
                fp << "  bind_host = \"" << bindHost << "\"\n";
index 45605dd6121cec4cbf092106d961cbc932e2b0c4..86cc797594f30b0fa41c01591f660747d1572415 100644 (file)
@@ -75,6 +75,34 @@ String ApiListener::GetCertificateRequestsDir(void)
        return Application::GetLocalStateDir() + "/lib/icinga2/certificate-requests/";
 }
 
+String ApiListener::GetDefaultCertPath(void)
+{
+       return GetCertsDir() + "/" + ScriptGlobal::Get("NodeName") + ".crt";
+}
+
+String ApiListener::GetDefaultKeyPath(void)
+{
+       return GetCertsDir() + "/" + ScriptGlobal::Get("NodeName") + ".key";
+}
+
+String ApiListener::GetDefaultCaPath(void)
+{
+       return GetCertsDir() + "/ca.crt";
+}
+
+void ApiListener::CopyCertificateFile(const String& oldCertPath, const String& newCertPath)
+{
+       struct stat st1, st2;
+
+       if (!oldCertPath.IsEmpty() && stat(oldCertPath.CStr(), &st1) >= 0 && (stat(newCertPath.CStr(), &st2) < 0 || st1.st_mtime > st2.st_mtime)) {
+               Log(LogWarning, "ApiListener")
+                   << "Copying '" << oldCertPath << "' certificate file to '" << newCertPath << "'";
+
+               Utility::MkDirP(Utility::DirName(newCertPath), 0700);
+               Utility::CopyFile(oldCertPath, newCertPath);
+       }
+}
+
 void ApiListener::OnConfigLoaded(void)
 {
        if (m_Instance)
@@ -82,20 +110,37 @@ void ApiListener::OnConfigLoaded(void)
 
        m_Instance = this;
 
+       String defaultCertPath = GetDefaultCertPath();
+       String defaultKeyPath = GetDefaultKeyPath();
+       String defaultCaPath = GetDefaultCaPath();
+
+       /* Migrate certificate location < 2.8 to the new default path. */
+       String oldCertPath = GetCertPath();
+       String oldKeyPath = GetKeyPath();
+       String oldCaPath = GetCaPath();
+
+       CopyCertificateFile(oldCertPath, defaultCertPath);
+       CopyCertificateFile(oldKeyPath, defaultKeyPath);
+       CopyCertificateFile(oldCaPath, defaultCaPath);
+
+       if (!oldCertPath.IsEmpty() && !oldKeyPath.IsEmpty() && !oldCaPath.IsEmpty()) {
+               Log(LogWarning, "ApiListener", "Please read the upgrading documentation for v2.8: https://www.icinga.com/docs/icinga2/latest/doc/16-upgrading-icinga-2/");
+       }
+
        /* set up SSL context */
        boost::shared_ptr<X509> cert;
        try {
-               cert = GetX509Certificate(GetCertPath());
+               cert = GetX509Certificate(defaultCertPath);
        } catch (const std::exception&) {
                BOOST_THROW_EXCEPTION(ScriptError("Cannot get certificate from cert path: '"
-                   + GetCertPath() + "'.", GetDebugInfo()));
+                   + defaultCertPath + "'.", GetDebugInfo()));
        }
 
        try {
                SetIdentity(GetCertificateCN(cert));
        } catch (const std::exception&) {
                BOOST_THROW_EXCEPTION(ScriptError("Cannot get certificate common name from cert path: '"
-                   + GetCertPath() + "'.", GetDebugInfo()));
+                   + defaultCertPath + "'.", GetDebugInfo()));
        }
 
        Log(LogInformation, "ApiListener")
@@ -109,10 +154,10 @@ void ApiListener::UpdateSSLContext(void)
        boost::shared_ptr<SSL_CTX> context;
 
        try {
-               context = MakeSSLContext(GetCertPath(), GetKeyPath(), GetCaPath());
+               context = MakeSSLContext(GetDefaultCertPath(), GetDefaultKeyPath(), GetDefaultCaPath());
        } catch (const std::exception&) {
                BOOST_THROW_EXCEPTION(ScriptError("Cannot make SSL context for cert path: '"
-                   + GetCertPath() + "' key path: '" + GetKeyPath() + "' ca path: '" + GetCaPath() + "'.", GetDebugInfo()));
+                   + GetDefaultCertPath() + "' key path: '" + GetDefaultKeyPath() + "' ca path: '" + GetDefaultCaPath() + "'.", GetDebugInfo()));
        }
 
        if (!GetCrlPath().IsEmpty()) {
@@ -420,7 +465,7 @@ void ApiListener::NewClientHandlerInternal(const Socket::Ptr& client, const Stri
                        identity = GetCertificateCN(cert);
                } catch (const std::exception&) {
                        Log(LogCritical, "ApiListener")
-                           << "Cannot get certificate common name from cert path: '" << GetCertPath() << "'.";
+                           << "Cannot get certificate common name from cert path: '" << GetDefaultCertPath() << "'.";
                        return;
                }
 
index e4d244f35903bd8145d26b1e0d675064af522710..1ce70316c7c4510df2ab21587fa911001a7f1c30 100644 (file)
@@ -104,6 +104,10 @@ public:
        static bool IsHACluster(void);
        static String GetFromZoneName(const Zone::Ptr& fromZone);
 
+       static String GetDefaultCertPath(void);
+       static String GetDefaultKeyPath(void);
+       static String GetDefaultCaPath(void);
+
 protected:
        virtual void OnConfigLoaded(void) override;
        virtual void OnAllConfigLoaded(void) override;
@@ -157,6 +161,8 @@ private:
        static void LogGlobHandler(std::vector<int>& files, const String& file);
        void ReplayLog(const JsonRpcConnection::Ptr& client);
 
+       static void CopyCertificateFile(const String& oldCertPath, const String& newCertPath);
+
        /* filesync */
        static ConfigDirInformation LoadConfigDir(const String& dir);
        static Dictionary::Ptr MergeConfigUpdate(const ConfigDirInformation& config);
index 41ec9770596441c4e22c383dce5be194290ea4a4..41f372992de8c34a47bd08eb77b4b593772d3dbe 100644 (file)
@@ -28,9 +28,9 @@ namespace icinga
 
 class ApiListener : ConfigObject
 {
-       [config, required] String cert_path;
-       [config, required] String key_path;
-       [config, required] String ca_path;
+       [config, deprecated] String cert_path;
+       [config, deprecated] String key_path;
+       [config, deprecated] String ca_path;
        [config] String crl_path;
        [config] String cipher_list {
                default {{{ return "ALL:!LOW:!WEAK:!MEDIUM:!EXP:!NULL"; }}}
index 722239bfcc18bf2bd1dc6918319ed48f12e7d70e..048d6c1c1e5cd933d43fc17533c6464a34dd512c 100644 (file)
@@ -56,7 +56,7 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
                cert = StringToCertificate(certText);
 
        ApiListener::Ptr listener = ApiListener::GetInstance();
-       boost::shared_ptr<X509> cacert = GetX509Certificate(listener->GetCaPath());
+       boost::shared_ptr<X509> cacert = GetX509Certificate(listener->GetDefaultCaPath());
 
        String cn = GetCertificateCN(cert);
 
@@ -183,7 +183,7 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
         * a certificate it wouldn't be able to use to connect to us anyway) */
        if (!VerifyCertificate(cacert, newcert)) {
                Log(LogWarning, "JsonRpcConnection")
-                   << "The CA in '" << listener->GetCaPath() << "' does not match the CA which Icinga uses "
+                   << "The CA in '" << listener->GetDefaultCaPath() << "' does not match the CA which Icinga uses "
                    << "for its own cluster connections. This is most likely a configuration problem.";
                goto delayed_request;
        }
@@ -285,7 +285,7 @@ Value UpdateCertificateHandler(const MessageOrigin::Ptr& origin, const Dictionar
        if (!listener)
                return Empty;
 
-       boost::shared_ptr<X509> oldCert = GetX509Certificate(listener->GetCertPath());
+       boost::shared_ptr<X509> oldCert = GetX509Certificate(listener->GetDefaultCertPath());
        boost::shared_ptr<X509> newCert = StringToCertificate(cert);
 
        String cn = GetCertificateCN(newCert);
@@ -328,7 +328,7 @@ Value UpdateCertificateHandler(const MessageOrigin::Ptr& origin, const Dictionar
        }
 
        /* Update CA certificate. */
-       String caPath = listener->GetCaPath();
+       String caPath = listener->GetDefaultCaPath();
 
        Log(LogInformation, "JsonRpcConnection")
            << "Updating CA certificate in '" << caPath << "'.";
@@ -350,7 +350,7 @@ Value UpdateCertificateHandler(const MessageOrigin::Ptr& origin, const Dictionar
        }
 
        /* Update signed certificate. */
-       String certPath = listener->GetCertPath();
+       String certPath = listener->GetDefaultCertPath();
 
        Log(LogInformation, "JsonRpcConnection")
            << "Updating client certificate for CN '" << cn << "' in '" << certPath << "'.";
index cc2004470ed26348ebad9cbf48af92d675fbfaa3..73dde3678e1ac9a082be35a367ffd781f1c6e95e 100644 (file)
@@ -149,6 +149,7 @@ bool TypeQueryHandler::HandleRequest(const ApiUser::Ptr& user, HttpRequest& requ
                        attributeInfo->Set("navigation", static_cast<bool>(field.Attributes & FANavigation));
                        attributeInfo->Set("no_user_modify", static_cast<bool>(field.Attributes & FANoUserModify));
                        attributeInfo->Set("no_user_view", static_cast<bool>(field.Attributes & FANoUserView));
+                       attributeInfo->Set("deprecated", static_cast<bool>(field.Attributes & FADeprecated));
                }
        }
 
index 287ee9bd7990c9af73b51c7c0f17a234a41d625b..0b919546466474f9a4fda7484beaa2a0766a430e 100644 (file)
@@ -147,6 +147,7 @@ protected                   { yylval->num = FAGetProtected | FASetProtected; return T_FIELD_ATTR
 no_storage                     { yylval->num = FANoStorage; return T_FIELD_ATTRIBUTE; }
 no_user_modify                 { yylval->num = FANoUserModify; return T_FIELD_ATTRIBUTE; }
 no_user_view                   { yylval->num = FANoUserView; return T_FIELD_ATTRIBUTE; }
+deprecated                     { yylval->num = FADeprecated; return T_FIELD_ATTRIBUTE; }
 navigation                     { return T_NAVIGATION; }
 validator                      { return T_VALIDATOR; }
 required                       { return T_REQUIRED; }
index bff7b0290254d612335ff47dbf3c2ebe2f367ea7..eb44de809649c18cd2bfcd65344ce4a4792c7df9 100644 (file)
@@ -510,6 +510,15 @@ void ClassCompiler::HandleClass(const Klass& klass, const ClassDebugInfo&)
                        m_Impl << "\t\t" << "BOOST_THROW_EXCEPTION(ValidationError(dynamic_cast<ConfigObject *>(this), boost::assign::list_of(\"" << field.Name << "\"), \"Attribute must not be empty.\"));" << std::endl << std::endl;
                }
 
+               if (field.Attributes & FADeprecated) {
+                       if (field.Type.GetRealType().find("::Ptr") != std::string::npos)
+                               m_Impl << "\t" << "if (" << argName << ")" << std::endl;
+                       else
+                               m_Impl << "\t" << "if (!" << argName << ".IsEmpty())" << std::endl;
+
+                       m_Impl << "\t\t" << "Log(LogWarning, \"" << klass.Name << "\") << \"Attribute '" << field.Name << "' for object '\" << dynamic_cast<ConfigObject *>(this)->GetName() << \"' of type '\" << dynamic_cast<ConfigObject *>(this)->GetReflectionType()->GetName() << \"' is deprecated and should not be used.\";" << std::endl;
+               }
+
                if (field.Type.ArrayRank > 0) {
                        m_Impl << "\t" << "if (avalue) {" << std::endl
                               << "\t\t" << "ObjectLock olock(avalue);" << std::endl
index df1a3660ce76b45a3d4e6cfb845e1063c652dbd3..06bf100d3f990951e91d98b3dff0311099b2663c 100644 (file)
@@ -72,7 +72,8 @@ enum FieldAttribute
        FARequired = 256,
        FANavigation = 512,
        FANoUserModify = 1024,
-       FANoUserView = 2048
+       FANoUserView = 2048,
+       FADeprecated = 4096
 };
 
 struct FieldType