Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cts/scheduler/xml/history-1.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="34" num_updates="163" validate-with="pacemaker-3.7" remote-tls-port="9898" remote-clear-port="9999" cib-last-written="Fri Jul 13 13:51:05 2012" have-quorum="1" dc-uuid="pcmk-1">
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="34" num_updates="163" validate-with="pacemaker-3.7" remote-tls-port="9898" cib-last-written="Fri Jul 13 13:51:05 2012" have-quorum="1" dc-uuid="pcmk-1">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
Expand Down
2 changes: 1 addition & 1 deletion cts/scheduler/xml/migrate-fencing.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="46" num_updates="177" validate-with="pacemaker-3.7" remote-tls-port="9898" remote-clear-port="9999" cib-last-written="Fri Jul 13 13:51:09 2012" have-quorum="1" dc-uuid="pcmk-4">
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="46" num_updates="177" validate-with="pacemaker-3.7" remote-tls-port="9898" cib-last-written="Fri Jul 13 13:51:09 2012" have-quorum="1" dc-uuid="pcmk-4">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
Expand Down
2 changes: 1 addition & 1 deletion cts/scheduler/xml/migrate-shutdown.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cib crm_feature_set="3.0.6" admin_epoch="1" epoch="47" num_updates="322" validate-with="pacemaker-3.7" remote-tls-port="9898" remote-clear-port="9999" cib-last-written="Fri Jul 13 13:51:09 2012" have-quorum="1" dc-uuid="101">
<cib crm_feature_set="3.0.6" admin_epoch="1" epoch="47" num_updates="322" validate-with="pacemaker-3.7" remote-tls-port="9898" cib-last-written="Fri Jul 13 13:51:09 2012" have-quorum="1" dc-uuid="101">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
Expand Down
2 changes: 1 addition & 1 deletion cts/scheduler/xml/probe-3.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cib crm_feature_set="3.0.1" admin_epoch="1" epoch="49" num_updates="34" validate-with="pacemaker-3.7" remote-tls-port="9898" remote-clear-port="9999" cib-last-written="Fri Jul 13 13:51:12 2012" have-quorum="1" dc-uuid="pcmk-2">
<cib crm_feature_set="3.0.1" admin_epoch="1" epoch="49" num_updates="34" validate-with="pacemaker-3.7" remote-tls-port="9898" cib-last-written="Fri Jul 13 13:51:12 2012" have-quorum="1" dc-uuid="pcmk-2">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
Expand Down
2 changes: 1 addition & 1 deletion cts/scheduler/xml/probe-4.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cib crm_feature_set="3.0.1" admin_epoch="1" epoch="49" num_updates="34" validate-with="pacemaker-3.7" remote-tls-port="9898" remote-clear-port="9999" cib-last-written="Fri Jul 13 13:51:12 2012" have-quorum="1" dc-uuid="pcmk-2">
<cib crm_feature_set="3.0.1" admin_epoch="1" epoch="49" num_updates="34" validate-with="pacemaker-3.7" remote-tls-port="9898" cib-last-written="Fri Jul 13 13:51:12 2012" have-quorum="1" dc-uuid="pcmk-2">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
Expand Down
2 changes: 1 addition & 1 deletion cts/scheduler/xml/promoted-demote-2.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="125" num_updates="1946" validate-with="pacemaker-3.7" remote-tls-port="9898" remote-clear-port="9999" cib-last-written="Fri Jul 13 13:51:07 2012" have-quorum="1" dc-uuid="pcmk-1">
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="125" num_updates="1946" validate-with="pacemaker-3.7" remote-tls-port="9898" cib-last-written="Fri Jul 13 13:51:07 2012" have-quorum="1" dc-uuid="pcmk-1">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
Expand Down
2 changes: 1 addition & 1 deletion cts/scheduler/xml/promoted-unmanaged-monitor.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="37" num_updates="90" validate-with="pacemaker-3.7" remote-tls-port="9898" remote-clear-port="9999" cib-last-written="Fri Jul 13 13:51:08 2012" have-quorum="1" dc-uuid="pcmk-3">
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="37" num_updates="90" validate-with="pacemaker-3.7" remote-tls-port="9898" cib-last-written="Fri Jul 13 13:51:08 2012" have-quorum="1" dc-uuid="pcmk-3">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
Expand Down
2 changes: 1 addition & 1 deletion cts/scheduler/xml/unmanaged-promoted.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="53" num_updates="19" validate-with="pacemaker-3.7" remote-tls-port="9898" remote-clear-port="9999" cib-last-written="Fri Jul 13 13:51:22 2012" have-quorum="1" dc-uuid="pcmk-1">
<cib crm_feature_set="3.0.5" admin_epoch="1" epoch="53" num_updates="19" validate-with="pacemaker-3.7" remote-tls-port="9898" cib-last-written="Fri Jul 13 13:51:22 2012" have-quorum="1" dc-uuid="pcmk-1">
<configuration>
<crm_config>
<cluster_property_set id="cib-bootstrap-options">
Expand Down
62 changes: 50 additions & 12 deletions daemons/based/based_remote.c
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@
#define HAVE_PAM 1
#endif

#define CREDFILE PACEMAKER_CONFIG_DIR "/cib-credentials"

static pcmk__tls_t *tls = NULL;

int remote_fd = 0;
Expand Down Expand Up @@ -692,29 +694,65 @@ based_remote_init(void)
{
const char *port_s = NULL;
int port = 0;
int rc = pcmk_rc_ok;
bool have_psk = false;
bool anon_fallback = true;

port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_TLS_PORT);

if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) {
// @TODO Implement pre-shared key authentication (see T961)
int rc = pcmk__init_tls(&tls, true, false);
if ((pcmk__scan_port(port_s, &port) != pcmk_rc_ok) || (port <= 0)) {
goto try_clear_port;
}

if (rc != pcmk_rc_ok) {
pcmk__err("Failed to initialize TLS: %s. Not starting TLS listener ",
"on port %d", pcmk_rc_str(rc), port);
remote_tls_fd = -1;
/* X509 certificates take precedence over PSK in pcmk__init_tls,
* so don't perform any of the following (potentially noisy) checks
* if we don't care about their results.
*/
if (!pcmk__x509_enabled()) {
have_psk = pcmk__cred_file_useable(CREDFILE, CRM_DAEMON_USER,
&anon_fallback);
if (!anon_fallback) {
pcmk__err("Not starting TLS listener on port %d", port);
goto try_clear_port;
}

} else {
pcmk__notice("Starting TLS listener on port %d", port);
remote_tls_fd = init_remote_listener(port);
/* @COMPAT Remove fallback to anonymous authentication */
if (!have_psk) {
pcmk__warn("Falling back to anonymous authentication for remote "
"CIB connections");
}
}

/* Now that we know whether to fall back to anonymous authentication
* or not, we can actually initialize TLS support.
*/
rc = pcmk__init_tls(&tls, true, have_psk);
if (rc != pcmk_rc_ok) {
pcmk__err("Failed to initialize TLS: %s. Not starting TLS listener ",
"on port %d", pcmk_rc_str(rc), port);

remote_tls_fd = -1;
goto try_clear_port;
}

if (tls->cred_type == GNUTLS_CRD_PSK) {
gnutls_psk_set_server_credentials_file(tls->credentials.psk_s,
CREDFILE);
}

pcmk__notice("Starting TLS listener on port %d", port);
remote_tls_fd = init_remote_listener(port);

try_clear_port:
/* Regardless of whether or not we successfully enabled remote-tls-port,
* we also want to try to enable remote-clear-port as well.
*/
port_s = pcmk__xe_get(the_cib, PCMK_XA_REMOTE_CLEAR_PORT);

if ((pcmk__scan_port(port_s, &port) == pcmk_rc_ok) && (port > 0)) {
pcmk__warn("Starting clear-text listener on port %d. This is insecure; "
PCMK_XA_REMOTE_TLS_PORT " is recommended instead.", port);
pcmk__warn("Starting clear-text listener on port %d. This is insecure "
"and will be removed in a future release. Use "
PCMK_XA_REMOTE_TLS_PORT " instead.", port);
remote_fd = init_remote_listener(port);
}
}
39 changes: 32 additions & 7 deletions daemons/execd/remoted_tls.c
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,23 @@ tls_server_dropped(gpointer user_data)

// \return 0 on success, -1 on error (gnutls_psk_server_credentials_function)
static int
lrmd_tls_server_key_cb(gnutls_session_t session, const char *username, gnutls_datum_t * key)
lrmd_tls_server_key_cb(gnutls_session_t session, const char *username,
gnutls_datum_t *key)
{
/* First, check that the client's username is valid. For Pacemaker
* Remote node connections, all clients will have the same username so
* we don't need to look it up anywhere.
*/
if (!pcmk__str_eq(DEFAULT_REMOTE_USERNAME, username, pcmk__str_none)) {
pcmk__err("Expected remote username " DEFAULT_REMOTE_USERNAME ", but "
"got %s", username);
return -1;
}

/* All Pacemaker Remote connections use the same key, too, so we don't
* need to do any lookups here either. Just attempt to load the key from
* disk (or cache) and put it in the key variable.
*/
return (lrmd__init_remote_key(key) == pcmk_rc_ok)? 0 : -1;
}

Expand Down Expand Up @@ -379,12 +394,22 @@ lrmd_init_remote_tls_server(void)
if (!pcmk__x509_enabled()) {
gnutls_datum_t psk_key = { NULL, 0 };

pcmk__tls_add_psk_callback(tls, lrmd_tls_server_key_cb);

/* The key callback won't get called until the first client connection
* attempt. Do it once here, so we can warn the user at start-up if we can't
* read the key. We don't error out, though, because it's fine if the key is
* going to be added later.
/* Register the callback function that will be used to load the key
* when a client connects.
*/
gnutls_psk_set_server_credentials_function(tls->credentials.psk_s,
lrmd_tls_server_key_cb);

/* gnutls doesn't need us to load the remote key up front. It will use
* the callback we just registered to load the key for each client when
* it attempts to connect. We do so here (1) to warn the user at start-up
* if we can't read the key, and (2) to cache the key so it's faster to
* authenticate each client.
*
* This also has the side effect of allowing the administrator to start
* the cluster without the Pacemaker Remote node key, then add it later,
* and have clients succeed in connecting. I don't know why this would
* be useful.
*/
if (lrmd__init_remote_key(&psk_key) != pcmk_rc_ok) {
pcmk__warn("A cluster connection will not be possible until the "
Expand Down
81 changes: 71 additions & 10 deletions doc/sphinx/Pacemaker_Administration/configuring.rst
Original file line number Diff line number Diff line change
Expand Up @@ -187,11 +187,22 @@ It is possible to run configuration commands from a machine that is not part of
the cluster.

For security reasons, this capability is disabled by default. If you wish to
allow remote access, set the ``remote-tls-port`` (encrypted) or
``remote-clear-port`` (unencrypted) CIB properties (attributes of the ``cib``
element). Encrypted communication can be performed keyless (which makes it
subject to man-in-the-middle attacks), but a better option is to also use
TLS certificates.
allow remote access, set the ``remote-tls-port`` CIB property (attribute of
the ``cib`` element). Communication can be performed with TLS certificates,
or using pre-shared keys (PSK). If both TLS certificates and PSK are configured,
only TLS certificates will be enabled.


.. note::

Pacemaker must have been built with PAM support for remote access to work.
You can check by running ``pacemakerd --features``. If the output contains
**pam**, remote access is supported. *(since 3.0.0; before 3.0.0, in a build
without PAM support, all remote connections are accepted without any
authentication)*

Configuring TLS Certificates
____________________________

To enable TLS certificates, it is recommended to first set up your own
Certificate Authority (CA) and generate a root CA certificate. Then create a
Expand Down Expand Up @@ -249,13 +260,63 @@ the daemon user's password:
Optionally, :ref:`CIB_crl_file <CIB_crl_file>` may be set to the location of a
Certificate Revocation List in PEM format.

Configuring Pre-Shared Keys (PSK)
_________________________________

To enable pre-shared keys, the first thing you need is a key file on any cluster
nodes that require access. The key file must be named ``cib-credentials`` and
be placed in the |PCMK_CONFIG_DIR| directory. It must be readable only by the
Pacemaker daemon user. Each line in this file is a username:key pair. The
``psktool`` program shipped with gnutls can be used to manage this file.

For instance, to create a new key for an ``admin`` user, do the following:

.. code-block:: none

# psktool -u admin -p /etc/pacemaker/cib-credentials
Generating a random key for user 'admin'
Key stored to cib-credentials
# cat /etc/pacemaker/cib-credentials
admin:c496618a37acb82672d968de46fb6865eca340409ae5b2620e7d2320c6059a7f
# chown hacluster:haclient /etc/pacemaker/cib-credentials
# chmod 600 /etc/pacemaker/cib-credentials

On the client side, set the following environment variables to connect to
the cluster:

* :ref:`CIB_port <CIB_port>` (required)
* :ref:`CIB_server <CIB_server>`
* :ref:`CIB_user <CIB_user>`
* :ref:`CIB_passwd <CIB_passwd>`
* :ref:`CIB_encrypted <CIB_encrypted>`
* :ref:`CIB_key_file <CIB_key_file>`

``CIB_key_file`` should point to a local file whose contents are the key that
was previously generated on the server. It should only be readable by the local
user. For instance, from the above example, it would look like:

.. code-block:: none

# cat ~/cib-credentials
c496618a37acb82672d968de46fb6865eca340409ae5b2620e7d2320c6059a7f

As an example, if **node1** is a cluster node, and the CIB is configured with
``remote-tls-port`` set to 1234, the administrator could read the current
cluster configuration using the following commands, and would be prompted for
the daemon user's password:

.. code-block:: none

# export CIB_server=node1; export CIB_port=1234; export CIB_encrypted=true
# export CIB_key_file=~/cib-credentials
# cibadmin -Q

.. note::

Pacemaker must have been built with PAM support for remote access to work.
You can check by running ``pacemakerd --features``. If the output contains
**pam**, remote access is supported. *(since 3.0.0; before 3.0.0, in a build
without PAM support, all remote connections are accepted without any
authentication)*
As of Pacemaker 3.0.2, even with the use of PSK authentication, a username
and password is additionally required to login to the CIB itself. Due to
the use of the ``CIB_user`` environment variable, the username in the PSK
credentials file and the CIB username must match.

.. rubric:: Footnotes

Expand Down
2 changes: 1 addition & 1 deletion doc/sphinx/Pacemaker_Explained/cluster-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ holds. So the decision was made to place them in an easy-to-find location.
- If set to a TCP port number, the CIB manager will listen for remote
connections on this port, to allow for CIB administration from hosts not
in the cluster. No encryption is used, so this should be used only on a
protected network.
protected network. *(deprecated since 3.0.2)*
* - .. _cib_last_written:

.. index::
Expand Down
2 changes: 0 additions & 2 deletions doc/sphinx/Pacemaker_Explained/local-options.rst
Original file line number Diff line number Diff line change
Expand Up @@ -594,8 +594,6 @@ whose location varies by OS (most commonly ``/etc/sysconfig/pacemaker`` or
or the |CRM_DAEMON_GROUP| group), and its contents must be identical on
all nodes.

This is an alternative to using X509 certificates.

* - .. _pcmk_remote_pid1:

.. index::
Expand Down
2 changes: 0 additions & 2 deletions etc/sysconfig/pacemaker.in
Original file line number Diff line number Diff line change
Expand Up @@ -277,8 +277,6 @@
# permissions to either the @CRM_DAEMON_USER@ user or the @CRM_DAEMON_GROUP@
# group), and its contents must be identical on all nodes.
#
# This is an alternative to using X509 certificates.
#
# Default: PCMK_authkey_location="@PACEMAKER_CONFIG_DIR@/authkey"

# PCMK_remote_pid1 (Advanced Use Only)
Expand Down
50 changes: 30 additions & 20 deletions include/crm/common/tls_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,28 +124,20 @@ int pcmk__tls_get_client_sock(const pcmk__remote_t *remote);

/*!
* \internal
* \brief Add the client PSK key to the TLS environment
* \brief Add a PSK key to the initialized TLS environment
*
* This function must be called for all TLS clients that are using PSK for
* authentication.
* TLS clients that are using PSK for authentication must call this function
* to add a key before the TLS session is established (that is, before
* calling \c pcmk__new_tls_session()).
*
* \param[in,out] tls The TLS environment
* \param[in] key The client's PSK key
* \param[in,out] tls The TLS environment
* \param[in] username The username \p key is valid for
* \param[in] key The client's PSK key
* \param[in] raw \p key is in a raw (or binary) format if \c true,
* and in plain text if \c false
*/
void pcmk__tls_add_psk_key(pcmk__tls_t *tls, gnutls_datum_t *key);

/*!
* \internal
* \brief Register the server's PSK credential fetching callback
*
* This function must be called for all TLS servers that are using PSK for
* authentication.
*
* \param[in,out] tls The TLS environment
* \param[in] cb The server's PSK credential fetching callback
*/
void pcmk__tls_add_psk_callback(pcmk__tls_t *tls,
gnutls_psk_server_credentials_function *cb);
void pcmk__tls_client_add_psk_key(pcmk__tls_t *tls, const char *username,
gnutls_datum_t *key, bool raw);

/*!
* \internal
Expand Down Expand Up @@ -221,10 +213,28 @@ void pcmk__copy_key(gnutls_datum_t *dest, const gnutls_datum_t *source);
*
* \param[in] location The file path to read from
* \param[out] dest Where to store the authentication key
* \param[in] raw \p key is in a raw (or binary) format if \c true,
* and in plain text if \c false
*
* \return Standard Pacemaker return code
*/
int pcmk__load_key(const char *location, gnutls_datum_t *key);
int pcmk__load_key(const char *location, gnutls_datum_t *key, bool raw);

/*!
* \internal
* \brief Check whether a PSK credentials file is useable
*
* This function checks that a PSK credentials file exists and has the
* correct ownership and permissions
*
* \param[in] location The file path to check
* \param[in] user The user the file should be owned by
* \param[out] allow_fallback \c true if the credentials file exists, is
* owned by \p user, and is not accessible by
* the group or world; false otherwise
*/
bool pcmk__cred_file_useable(const char *location, const char *user,
bool *allow_fallback);

#ifdef __cplusplus
}
Expand Down
Loading