mirror of https://github.com/Icinga/icinga2.git
Merge pull request #6500 from ajaffie/feature/cli-ca-remove-6049
Implemented `ca remove` cli command + documentation.
This commit is contained in:
commit
ee4c5c5c2b
|
@ -451,6 +451,16 @@ information/cli: Signed certificate for 'CN = icinga2-client2.localdomain'.
|
|||
> `ca list` cannot be used as historical inventory. Certificate
|
||||
> signing requests older than 1 week are automatically deleted.
|
||||
|
||||
You can also remove an undesired CSR using the `ca remove` command using the
|
||||
syntax as the `ca sign` command.
|
||||
|
||||
```
|
||||
[root@pym ~]# icinga2 ca remove 5c31ca0e2269c10363a97e40e3f2b2cd56493f9194d5b1852541b835970da46e
|
||||
information/cli: Certificate 5c31ca0e2269c10363a97e40e3f2b2cd56493f9194d5b1852541b835970da46e removed.
|
||||
```
|
||||
If you want to restore a certificate you have removed, you can use `ca restore`.
|
||||
|
||||
|
||||
## Client/Satellite Setup <a id="distributed-monitoring-setup-satellite-client"></a>
|
||||
|
||||
This section describes the setup of a satellite and/or client connected to an
|
||||
|
|
|
@ -21,6 +21,8 @@ Usage:
|
|||
Supported commands:
|
||||
* api setup (setup for API)
|
||||
* ca list (lists all certificate signing requests)
|
||||
* ca restore (restores a removed certificate request)
|
||||
* ca remove (removes an outstanding certificate request)
|
||||
* ca sign (signs an outstanding certificate request)
|
||||
* console (Icinga debug console)
|
||||
* daemon (starts Icinga 2)
|
||||
|
@ -185,6 +187,8 @@ Usage:
|
|||
Supported commands:
|
||||
* ca list (lists all certificate signing requests)
|
||||
* ca sign (signs an outstanding certificate request)
|
||||
* ca restore (restores a removed certificate request)
|
||||
* ca remove (removes an outstanding certificate request)
|
||||
|
||||
Global options:
|
||||
-h [ --help ] show this help message
|
||||
|
@ -232,6 +236,7 @@ Command options:
|
|||
--all List all certificate signing requests, including
|
||||
signed. Note: Old requests are automatically
|
||||
cleaned by Icinga after 1 week.
|
||||
--removed List all removed CSRs (for use with 'ca restore')
|
||||
--json encode output as JSON
|
||||
|
||||
Report bugs at <https://github.com/Icinga/icinga2>
|
||||
|
|
|
@ -164,6 +164,16 @@ or with sudo.
|
|||
You can use the new `--all` parameter to show all signing requests.
|
||||
Note that Icinga automatically purges signed requests older than 1 week.
|
||||
|
||||
#### New: CA Remove/Restore <a id="upgrading-to-2-11-cli-commands-ca-remove-restore></a>
|
||||
|
||||
`ca remove` allows you to remove pending signing requests. Once the
|
||||
master receives a CSR, and it is marked as removed, the request is
|
||||
denied.
|
||||
|
||||
`ca restore` allows you to restore a removed signing request. You
|
||||
can list removed signing requests with the new `--removed` parameter
|
||||
for `ca list`.
|
||||
|
||||
### Configuration <a id="upgrading-to-2-11-configuration"></a>
|
||||
|
||||
The deprecated `concurrent_checks` attribute in the [checker feature](09-object-types.md#objecttype-checkercomponent)
|
||||
|
|
|
@ -5,6 +5,8 @@ set(cli_SOURCES
|
|||
apisetupcommand.cpp apisetupcommand.hpp
|
||||
apisetuputility.cpp apisetuputility.hpp
|
||||
calistcommand.cpp calistcommand.hpp
|
||||
caremovecommand.cpp caremovecommand.hpp
|
||||
carestorecommand.cpp carestorecommand.hpp
|
||||
casigncommand.cpp casigncommand.hpp
|
||||
clicommand.cpp clicommand.hpp
|
||||
consolecommand.cpp consolecommand.hpp
|
||||
|
|
|
@ -14,32 +14,49 @@ namespace po = boost::program_options;
|
|||
|
||||
REGISTER_CLICOMMAND("ca/list", CAListCommand);
|
||||
|
||||
/**
|
||||
* Provide a long CLI description sentence.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
String CAListCommand::GetDescription() const
|
||||
{
|
||||
return "Lists pending certificate signing requests.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a short CLI description.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
String CAListCommand::GetShortDescription() const
|
||||
{
|
||||
return "lists pending certificate signing requests";
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize available CLI parameters.
|
||||
*
|
||||
* @param visibleDesc Register visible parameters.
|
||||
* @param hiddenDesc Register hidden parameters.
|
||||
*/
|
||||
void CAListCommand::InitParameters(boost::program_options::options_description& visibleDesc,
|
||||
boost::program_options::options_description& hiddenDesc) const
|
||||
{
|
||||
visibleDesc.add_options()
|
||||
("all", "List all certificate signing requests, including signed. Note: Old requests are automatically cleaned by Icinga after 1 week.")
|
||||
("removed", "List all removed CSRs (for use with 'ca restore')")
|
||||
("json", "encode output as JSON");
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry point for the "ca list" CLI command.
|
||||
*
|
||||
* @returns An exit status.
|
||||
* @return An exit status.
|
||||
*/
|
||||
int CAListCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
|
||||
{
|
||||
Dictionary::Ptr requests = PkiUtility::GetCertificateRequests();
|
||||
Dictionary::Ptr requests = PkiUtility::GetCertificateRequests(vm.count("removed"));
|
||||
|
||||
if (vm.count("json"))
|
||||
std::cout << JsonEncode(requests);
|
||||
|
|
|
@ -0,0 +1,93 @@
|
|||
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
||||
|
||||
#include "cli/caremovecommand.hpp"
|
||||
#include "base/logger.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/tlsutility.hpp"
|
||||
#include "remote/apilistener.hpp"
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
REGISTER_CLICOMMAND("ca/remove", CARemoveCommand);
|
||||
|
||||
/**
|
||||
* Provide a long CLI description sentence.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
String CARemoveCommand::GetDescription() const
|
||||
{
|
||||
return "Removes an outstanding certificate request.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a short CLI description.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
String CARemoveCommand::GetShortDescription() const
|
||||
{
|
||||
return "removes an outstanding certificate request";
|
||||
}
|
||||
|
||||
/**
|
||||
* Define minimum arguments without key parameter.
|
||||
*
|
||||
* @return number of arguments
|
||||
*/
|
||||
int CARemoveCommand::GetMinArguments() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Impersonate as Icinga user.
|
||||
*
|
||||
* @return impersonate level
|
||||
*/
|
||||
ImpersonationLevel CARemoveCommand::GetImpersonationLevel() const
|
||||
{
|
||||
return ImpersonateIcinga;
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry point for the "ca remove" CLI command.
|
||||
*
|
||||
* @returns An exit status.
|
||||
*/
|
||||
int CARemoveCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
|
||||
{
|
||||
String fingerPrint = ap[0];
|
||||
String requestFile = ApiListener::GetCertificateRequestsDir() + "/" + fingerPrint + ".json";
|
||||
|
||||
if (!Utility::PathExists(requestFile)) {
|
||||
Log(LogCritical, "cli")
|
||||
<< "No request exists for fingerprint '" << fingerPrint << "'.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Dictionary::Ptr request = Utility::LoadJsonFile(requestFile);
|
||||
std::shared_ptr<X509> certRequest = StringToCertificate(request->Get("cert_request"));
|
||||
|
||||
if (!certRequest) {
|
||||
Log(LogCritical, "cli", "Certificate request is invalid. Could not parse X.509 certificate for the 'cert_request' attribute.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
String cn = GetCertificateCN(certRequest);
|
||||
|
||||
if (request->Contains("cert_response")) {
|
||||
Log(LogCritical, "cli")
|
||||
<< "Certificate request for CN '" << cn << "' already signed, removal is not possible.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Utility::SaveJsonFile(ApiListener::GetCertificateRequestsDir() + "/" + fingerPrint + ".removed", 0600, request);
|
||||
|
||||
Utility::Remove(requestFile);
|
||||
|
||||
Log(LogInformation, "cli")
|
||||
<< "Certificate request for CN " << cn << " removed.";
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
||||
|
||||
#ifndef CAREMOVECOMMAND_H
|
||||
#define CAREMOVECOMMAND_H
|
||||
|
||||
#include "cli/clicommand.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
/**
|
||||
* The "ca remove" command.
|
||||
*
|
||||
* @ingroup cli
|
||||
*/
|
||||
class CARemoveCommand final : public CLICommand
|
||||
{
|
||||
public:
|
||||
DECLARE_PTR_TYPEDEFS(CARemoveCommand);
|
||||
|
||||
String GetDescription() const override;
|
||||
String GetShortDescription() const override;
|
||||
int GetMinArguments() const override;
|
||||
ImpersonationLevel GetImpersonationLevel() const override;
|
||||
int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CAREMOVECOMMAND_H */
|
|
@ -0,0 +1,88 @@
|
|||
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
||||
|
||||
#include "cli/carestorecommand.hpp"
|
||||
#include "base/logger.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/tlsutility.hpp"
|
||||
#include "remote/apilistener.hpp"
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
REGISTER_CLICOMMAND("ca/restore", CARestoreCommand);
|
||||
|
||||
/**
|
||||
* Provide a long CLI description sentence.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
String CARestoreCommand::GetDescription() const
|
||||
{
|
||||
return "Restores a previously removed certificate request.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a short CLI description.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
String CARestoreCommand::GetShortDescription() const
|
||||
{
|
||||
return "restores a removed certificate request";
|
||||
}
|
||||
|
||||
/**
|
||||
* Define minimum arguments without key parameter.
|
||||
*
|
||||
* @return number of arguments
|
||||
*/
|
||||
int CARestoreCommand::GetMinArguments() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Impersonate as Icinga user.
|
||||
*
|
||||
* @return impersonate level
|
||||
*/
|
||||
ImpersonationLevel CARestoreCommand::GetImpersonationLevel() const
|
||||
{
|
||||
return ImpersonateIcinga;
|
||||
}
|
||||
|
||||
/**
|
||||
* The entry point for the "ca restore" CLI command.
|
||||
*
|
||||
* @returns An exit status.
|
||||
*/
|
||||
int CARestoreCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
|
||||
{
|
||||
String fingerPrint = ap[0];
|
||||
String removedRequestFile = ApiListener::GetCertificateRequestsDir() + "/" + fingerPrint + ".removed";
|
||||
|
||||
if (!Utility::PathExists(removedRequestFile)) {
|
||||
Log(LogCritical, "cli")
|
||||
<< "Cannot find removed fingerprint '" << fingerPrint << "', bailing out.";
|
||||
return 1;
|
||||
}
|
||||
|
||||
Dictionary::Ptr request = Utility::LoadJsonFile(removedRequestFile);
|
||||
std::shared_ptr<X509> certRequest = StringToCertificate(request->Get("cert_request"));
|
||||
|
||||
if (!certRequest) {
|
||||
Log(LogCritical, "cli", "Certificate request is invalid. Could not parse X.509 certificate for the 'cert_request' attribute.");
|
||||
/* Purge the file when we know that it is broken. */
|
||||
Utility::Remove(removedRequestFile);
|
||||
return 1;
|
||||
}
|
||||
|
||||
Utility::SaveJsonFile(ApiListener::GetCertificateRequestsDir() + "/" + fingerPrint + ".json", 0600, request);
|
||||
|
||||
Utility::Remove(removedRequestFile);
|
||||
|
||||
Log(LogInformation, "cli")
|
||||
<< "Restored certificate request for CN '" << GetCertificateCN(certRequest) << "', sign it with:\n"
|
||||
<< "\"icinga2 ca sign " << fingerPrint << "\"";
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
||||
|
||||
#ifndef CARESTORECOMMAND_H
|
||||
#define CARESTORECOMMAND_H
|
||||
|
||||
#include "cli/clicommand.hpp"
|
||||
|
||||
namespace icinga
|
||||
{
|
||||
|
||||
/**
|
||||
* The "ca restore" command.
|
||||
*
|
||||
* @ingroup cli
|
||||
*/
|
||||
class CARestoreCommand final : public CLICommand
|
||||
{
|
||||
public:
|
||||
DECLARE_PTR_TYPEDEFS(CARestoreCommand);
|
||||
|
||||
String GetDescription() const override;
|
||||
String GetShortDescription() const override;
|
||||
int GetMinArguments() const override;
|
||||
ImpersonationLevel GetImpersonationLevel() const override;
|
||||
int Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const override;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif /* CASTORECOMMAND_H */
|
|
@ -1,30 +1,50 @@
|
|||
/* Icinga 2 | (c) 2012 Icinga GmbH | GPLv2+ */
|
||||
|
||||
#include "cli/casigncommand.hpp"
|
||||
#include "remote/apilistener.hpp"
|
||||
#include "base/logger.hpp"
|
||||
#include "base/application.hpp"
|
||||
#include "base/tlsutility.hpp"
|
||||
#include "remote/apilistener.hpp"
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
REGISTER_CLICOMMAND("ca/sign", CASignCommand);
|
||||
|
||||
/**
|
||||
* Provide a long CLI description sentence.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
String CASignCommand::GetDescription() const
|
||||
{
|
||||
return "Signs an outstanding certificate request.";
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a short CLI description.
|
||||
*
|
||||
* @return text
|
||||
*/
|
||||
String CASignCommand::GetShortDescription() const
|
||||
{
|
||||
return "signs an outstanding certificate request";
|
||||
}
|
||||
|
||||
/**
|
||||
* Define minimum arguments without key parameter.
|
||||
*
|
||||
* @return number of arguments
|
||||
*/
|
||||
int CASignCommand::GetMinArguments() const
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Impersonate as Icinga user.
|
||||
*
|
||||
* @return impersonate level
|
||||
*/
|
||||
ImpersonationLevel CASignCommand::GetImpersonationLevel() const
|
||||
{
|
||||
return ImpersonateIcinga;
|
||||
|
@ -33,7 +53,7 @@ ImpersonationLevel CASignCommand::GetImpersonationLevel() const
|
|||
/**
|
||||
* The entry point for the "ca sign" CLI command.
|
||||
*
|
||||
* @returns An exit status.
|
||||
* @return An exit status.
|
||||
*/
|
||||
int CASignCommand::Run(const boost::program_options::variables_map& vm, const std::vector<std::string>& ap) const
|
||||
{
|
||||
|
|
|
@ -129,6 +129,12 @@ Value RequestCertificateHandler(const MessageOrigin::Ptr& origin, const Dictiona
|
|||
|
||||
return result;
|
||||
}
|
||||
} else if (Utility::PathExists(requestDir + "/" + certFingerprint + ".removed")) {
|
||||
Log(LogInformation, "JsonRpcConnection")
|
||||
<< "Certificate for CN " << cn << " has been removed. Ignoring signing request.";
|
||||
result->Set("status_code", 1);
|
||||
result->Set("error", "Ticket for CN " + cn + " declined by administrator.");
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<X509> newcert;
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
|
||||
using namespace icinga;
|
||||
|
||||
|
@ -368,8 +369,9 @@ static void CollectRequestHandler(const Dictionary::Ptr& requests, const String&
|
|||
|
||||
Dictionary::Ptr result = new Dictionary();
|
||||
|
||||
String fingerprint = Utility::BaseName(requestFile);
|
||||
fingerprint = fingerprint.SubStr(0, fingerprint.GetLength() - 5);
|
||||
namespace fs = boost::filesystem;
|
||||
fs::path file(requestFile.Begin(), requestFile.End());
|
||||
String fingerprint = file.stem().string();
|
||||
|
||||
String certRequestText = request->Get("cert_request");
|
||||
result->Set("cert_request", certRequestText);
|
||||
|
@ -414,14 +416,19 @@ static void CollectRequestHandler(const Dictionary::Ptr& requests, const String&
|
|||
requests->Set(fingerprint, result);
|
||||
}
|
||||
|
||||
Dictionary::Ptr PkiUtility::GetCertificateRequests()
|
||||
Dictionary::Ptr PkiUtility::GetCertificateRequests(bool removed)
|
||||
{
|
||||
Dictionary::Ptr requests = new Dictionary();
|
||||
|
||||
String requestDir = ApiListener::GetCertificateRequestsDir();
|
||||
String ext = "json";
|
||||
|
||||
if (removed)
|
||||
ext = "removed";
|
||||
|
||||
if (Utility::PathExists(requestDir))
|
||||
Utility::Glob(requestDir + "/*.json", std::bind(&CollectRequestHandler, requests, _1), GlobFile);
|
||||
Utility::Glob(requestDir + "/*." + ext, std::bind(&CollectRequestHandler, requests, _1), GlobFile);
|
||||
|
||||
return requests;
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ public:
|
|||
const String& certfile, const String& cafile, const std::shared_ptr<X509>& trustedcert,
|
||||
const String& ticket = String());
|
||||
static String GetCertificateInformation(const std::shared_ptr<X509>& certificate);
|
||||
static Dictionary::Ptr GetCertificateRequests();
|
||||
static Dictionary::Ptr GetCertificateRequests(bool removed = false);
|
||||
|
||||
private:
|
||||
PkiUtility();
|
||||
|
|
Loading…
Reference in New Issue