Skip to content
Merged
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
5 changes: 5 additions & 0 deletions cloudinit/config/cc_keys_to_console.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ def _get_helper_tool_path(distro):


def handle(name, cfg, cloud, log, _args):
if util.is_false(cfg.get("ssh", {}).get("emit_keys_to_console", True)):
log.debug(("Skipping module named %s, "
"logging of SSH host keys disabled"), name)
return

helper_path = _get_helper_tool_path(cloud.distro)
if not os.path.exists(helper_path):
log.warning(("Unable to activate module %s,"
Expand Down
34 changes: 34 additions & 0 deletions cloudinit/config/tests/test_keys_to_console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
"""Tests for cc_keys_to_console."""
from unittest import mock

import pytest

from cloudinit.config import cc_keys_to_console


class TestHandle:
"""Tests for cloudinit.config.cc_keys_to_console.handle.

TODO: These tests only cover the emit_keys_to_console config option, they
should be expanded to cover the full functionality.
"""

@mock.patch("cloudinit.config.cc_keys_to_console.util.multi_log")
@mock.patch("cloudinit.config.cc_keys_to_console.os.path.exists")
@mock.patch("cloudinit.config.cc_keys_to_console.subp.subp")
@pytest.mark.parametrize("cfg,subp_called", [
({}, True), # Default to emitting keys
({"ssh": {}}, True), # Default even if we have the parent key
({"ssh": {"emit_keys_to_console": True}}, True), # Explicitly enabled
({"ssh": {"emit_keys_to_console": False}}, False), # Disabled
])
def test_emit_keys_to_console_config(
self, m_subp, m_path_exists, _m_multi_log, cfg, subp_called
):
# Ensure we always find the helper
m_path_exists.return_value = True
m_subp.return_value = ("", "")

cc_keys_to_console.handle("name", cfg, mock.Mock(), mock.Mock(), ())

assert subp_called == (m_subp.call_count == 1)
10 changes: 10 additions & 0 deletions doc/examples/cloud-config-ssh-keys.txt
Original file line number Diff line number Diff line change
Expand Up @@ -42,3 +42,13 @@ ssh_keys:
-----END DSA PRIVATE KEY-----

dsa_public: ssh-dss AAAAB3NzaC1kc3MAAACBAM/Ycu7ulMTEvz1RLIzTbrhELJZf8Iwua6TFfQl1ubb1rHwUElOkus7xMhdVjms8AmbV1Meem7ImE69T0bszy09QAG3NImHgZVIeXBoJ/JzByku/1NcOBYilKP7oSIcLJpGUHX8IGn1GJoH7XRBwVub6Vqm4RP78C7q9IOn0hG2VAAAAFQCDEfCrnL1GGzhCPsr/uS1vbt8/wQAAAIEAjSrok/4m8mbBkVp4IwxXFdRuqJKSj8/WWxos00Ednn/ww5QibysHYULrOKJ1+54mmpMyp5CZICUQELCfCt5ScZ9GsqgmnI80Q1h3Xkwbo3kn7PzWwRwcV6muvJn4PcZ71WM+rdN/c2EorAINDTbjRo97NueM94WbiYdtjHFxn0YAAACAXmLIFSQgiAPu459rCKxT46tHJtM0QfnNiEnQLbFluefZ/yiI4DI38UzTCOXLhUA7ybmZha+D/csj15Y9/BNFuO7unzVhikCQV9DTeXX46pG4s1o23JKC/QaYWNMZ7kTRv+wWow9MhGiVdML4ZN4XnifuO5krqAybngIy66PMEoQ= smoser@localhost

# By default, the fingerprints of the authorized keys for the users
# cloud-init adds are printed to the console. Setting
# no_ssh_fingerprints to true suppresses this output.
no_ssh_fingerprints: false
Comment thread
OddBloke marked this conversation as resolved.

# By default, (most) ssh host keys are printed to the console. Setting
# emit_keys_to_console to false suppresses this output.
ssh:
emit_keys_to_console: false
48 changes: 48 additions & 0 deletions tests/integration_tests/modules/test_keys_to_console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""Integration tests for the cc_keys_to_console module.

(This is ported from
``tests/cloud_tests/testcases/modules/keys_to_console.yaml``.)"""
import pytest

BLACKLIST_USER_DATA = """\
#cloud-config
ssh_fp_console_blacklist: [ssh-dss, ssh-dsa, ecdsa-sha2-nistp256]
ssh_key_console_blacklist: [ssh-dss, ssh-dsa, ecdsa-sha2-nistp256]
"""

DISABLED_USER_DATA = """\
#cloud-config
ssh:
emit_keys_to_console: false
"""


@pytest.mark.user_data(BLACKLIST_USER_DATA)
class TestKeysToConsoleBlacklist:
"""Test that the blacklist options work as expected."""
@pytest.mark.parametrize("key_type", ["DSA", "ECDSA"])
def test_excluded_keys(self, class_client, key_type):
syslog = class_client.read_from_file("/var/log/syslog")
assert "({})".format(key_type) not in syslog

@pytest.mark.parametrize("key_type", ["ED25519", "RSA"])
def test_included_keys(self, class_client, key_type):
syslog = class_client.read_from_file("/var/log/syslog")
assert "({})".format(key_type) in syslog


@pytest.mark.user_data(DISABLED_USER_DATA)
class TestKeysToConsoleDisabled:
"""Test that output can be fully disabled."""
@pytest.mark.parametrize("key_type", ["DSA", "ECDSA", "ED25519", "RSA"])
def test_keys_excluded(self, class_client, key_type):
syslog = class_client.read_from_file("/var/log/syslog")
assert "({})".format(key_type) not in syslog

def test_header_excluded(self, class_client):
syslog = class_client.read_from_file("/var/log/syslog")
assert "BEGIN SSH HOST KEY FINGERPRINTS" not in syslog

def test_footer_excluded(self, class_client):
syslog = class_client.read_from_file("/var/log/syslog")
assert "END SSH HOST KEY FINGERPRINTS" not in syslog