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
45 changes: 33 additions & 12 deletions cloudinit/config/cc_ssh.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,14 @@

import glob
import os
import re
import sys
from logging import Logger
from textwrap import dedent
from typing import List, Optional, Sequence

from cloudinit import ssh_util, subp, util
from cloudinit.cloud import Cloud
from cloudinit.config.schema import MetaSchema, get_meta_doc
from cloudinit.distros import ALL_DISTROS, ug_util
from cloudinit.settings import PER_INSTANCE
Expand Down Expand Up @@ -103,12 +107,22 @@

- dsa
- ecdsa
- ecdsa-sk
- ed25519
- ed25519-sk
- rsa

Unsupported host key types for the ``ssh_keys`` and the ``ssh_genkeytypes``
config flags are:

- ecdsa-sk
- ed25519-sk
"""

# Note: We do not support *-sk key types because:
# 1) In the autogeneration case user interaction with the device is needed
# which does not fit with a cloud-context.
# 2) This type of keys are user-based, not hostkeys.


meta: MetaSchema = {
"id": "cc_ssh",
"name": "SSH",
Expand Down Expand Up @@ -156,6 +170,9 @@
__doc__ = get_meta_doc(meta)

GENERATE_KEY_NAMES = ["rsa", "dsa", "ecdsa", "ed25519"]
pattern_unsupported_config_keys = re.compile(
"^(ecdsa-sk|ed25519-sk)_(private|public|certificate)$"
)
KEY_FILE_TPL = "/etc/ssh/ssh_host_%s_key"
PUBLISH_HOST_KEYS = True
# Don't publish the dsa hostkey by default since OpenSSH recommends not using
Expand All @@ -165,19 +182,19 @@
CONFIG_KEY_TO_FILE = {}
PRIV_TO_PUB = {}
for k in GENERATE_KEY_NAMES:
CONFIG_KEY_TO_FILE.update({"%s_private" % k: (KEY_FILE_TPL % k, 0o600)})
CONFIG_KEY_TO_FILE.update(
{"%s_public" % k: (KEY_FILE_TPL % k + ".pub", 0o600)}
)
CONFIG_KEY_TO_FILE.update(
{"%s_certificate" % k: (KEY_FILE_TPL % k + "-cert.pub", 0o600)}
{
f"{k}_private": (KEY_FILE_TPL % k, 0o600),
f"{k}_public": (f"{KEY_FILE_TPL % k}.pub", 0o600),
f"{k}_certificate": (f"{KEY_FILE_TPL % k}-cert.pub", 0o600),
}
)
PRIV_TO_PUB["%s_private" % k] = "%s_public" % k
PRIV_TO_PUB[f"{k}_private"] = f"{k}_public"

KEY_GEN_TPL = 'o=$(ssh-keygen -yf "%s") && echo "$o" root@localhost > "%s"'


def handle(_name, cfg, cloud, log, _args):
def handle(_name, cfg, cloud: Cloud, log: Logger, _args):

# remove the static keys from the pristine image
if cfg.get("ssh_deletekeys", True):
Expand All @@ -191,8 +208,12 @@ def handle(_name, cfg, cloud, log, _args):
if "ssh_keys" in cfg:
# if there are keys and/or certificates in cloud-config, use them
for (key, val) in cfg["ssh_keys"].items():
# skip entry if unrecognized
if key not in CONFIG_KEY_TO_FILE:
if pattern_unsupported_config_keys.match(key):
reason = "unsupported"
else:
reason = "unrecognized"
log.warning("Skipping %s ssh_keys" ' entry: "%s"', reason, key)
continue
tgt_fn = CONFIG_KEY_TO_FILE[key][0]
tgt_perms = CONFIG_KEY_TO_FILE[key][1]
Expand Down Expand Up @@ -297,7 +318,7 @@ def handle(_name, cfg, cloud, log, _args):
cfg, "disable_root_opts", ssh_util.DISABLE_USER_OPTS
)

keys = []
keys: List[str] = []
if util.get_cfg_option_bool(cfg, "allow_public_ssh_keys", True):
keys = cloud.get_public_ssh_keys() or []
else:
Expand Down Expand Up @@ -332,7 +353,7 @@ def apply_credentials(keys, user, disable_root, disable_root_opts):
ssh_util.setup_user_keys(keys, "root", options=key_prefix)


def get_public_host_keys(blacklist=None):
def get_public_host_keys(blacklist: Optional[Sequence[str]] = None):
"""Read host keys from /etc/ssh/*.pub files and return them as a list.

@param blacklist: List of key types to ignore. e.g. ['dsa', 'rsa']
Expand Down
6 changes: 3 additions & 3 deletions cloudinit/config/cloud-init-schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -1886,7 +1886,7 @@
"type": "object",
"description": "A dictionary entries for the public and private host keys of each desired key type. Entries in the ``ssh_keys`` config dict should have keys in the format ``<key type>_private``, ``<key type>_public``, and, optionally, ``<key type>_certificate``, e.g. ``rsa_private: <key>``, ``rsa_public: <key>``, and ``rsa_certificate: <key>``. Not all key types have to be specified, ones left unspecified will not be used. If this config option is used, then separate keys will not be automatically generated. In order to specify multiline private host keys and certificates, use yaml multiline syntax.",
"patternProperties": {
"^(dsa|ecdsa|ecdsa-sk|ed25519|ed25519-sk|rsa)_(public|private|certificate)$": {
"^(dsa|ecdsa|ed25519|rsa)_(public|private|certificate)$": {
"label": "<key_type>",
"type": "string"
}
Expand All @@ -1909,11 +1909,11 @@
"ssh_genkeytypes": {
"type": "array",
"description": "The SSH key types to generate. Default: ``[rsa, dsa, ecdsa, ed25519]``",
"default": ["dsa", "ecdsa", "ecdsa-sk", "ed25519", "ed25519-sk", "rsa"],
"default": ["dsa", "ecdsa", "ed25519", "rsa"],
"minItems": 1,
"items": {
"type": "string",
"enum": ["dsa", "ecdsa", "ecdsa-sk", "ed25519", "ed25519-sk", "rsa"]
"enum": ["dsa", "ecdsa", "ed25519", "rsa"]
}
},
"disable_root": {
Expand Down
Loading