summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorTakeru Hayasaka <hayatake396@gmail.com>2024-12-12 02:27:02 +0900
committerTakeru Hayasaka <hayatake396@gmail.com>2024-12-23 09:13:14 +0000
commite7cab89f9f81b2eeb456657d26dda8bd7d7fc428 (patch)
tree69054364573f7b86674c7b2899809fe10cf146dc /src
parent3168305a0474573ad0f36fee399baa423cbce5e4 (diff)
downloadvyos-1x-e7cab89f9f81b2eeb456657d26dda8bd7d7fc428.tar.gz
vyos-1x-e7cab89f9f81b2eeb456657d26dda8bd7d7fc428.zip
T6013: Add support for configuring TrustedUserCAKeys in SSH service with local and remote CA keys
Diffstat (limited to 'src')
-rwxr-xr-xsrc/conf_mode/service_ssh.py54
1 files changed, 48 insertions, 6 deletions
diff --git a/src/conf_mode/service_ssh.py b/src/conf_mode/service_ssh.py
index 9abdd33dc..6c8aa20d0 100755
--- a/src/conf_mode/service_ssh.py
+++ b/src/conf_mode/service_ssh.py
@@ -23,10 +23,16 @@ from syslog import LOG_INFO
from vyos.config import Config
from vyos.configdict import is_node_changed
from vyos.configverify import verify_vrf
+from vyos.configverify import verify_pki_ca_certificate
from vyos.utils.process import call
from vyos.template import render
from vyos import ConfigError
from vyos import airbag
+from vyos.pki import find_chain
+from vyos.pki import encode_certificate
+from vyos.pki import load_certificate
+from vyos.utils.file import write_file
+
airbag.enable()
config_file = r'/run/sshd/sshd_config'
@@ -38,6 +44,9 @@ key_rsa = '/etc/ssh/ssh_host_rsa_key'
key_dsa = '/etc/ssh/ssh_host_dsa_key'
key_ed25519 = '/etc/ssh/ssh_host_ed25519_key'
+trusted_user_ca_key = '/etc/ssh/trusted_user_ca_key'
+
+
def get_config(config=None):
if config:
conf = config
@@ -47,10 +56,13 @@ def get_config(config=None):
if not conf.exists(base):
return None
- ssh = conf.get_config_dict(base, key_mangling=('-', '_'), get_first_key=True)
+ ssh = conf.get_config_dict(
+ base, key_mangling=('-', '_'), get_first_key=True, with_pki=True
+ )
tmp = is_node_changed(conf, base + ['vrf'])
- if tmp: ssh.update({'restart_required': {}})
+ if tmp:
+ ssh.update({'restart_required': {}})
# We have gathered the dict representation of the CLI, but there are default
# options which we need to update into the dictionary retrived.
@@ -62,20 +74,32 @@ def get_config(config=None):
# Ignore default XML values if config doesn't exists
# Delete key from dict
if not conf.exists(base + ['dynamic-protection']):
- del ssh['dynamic_protection']
+ del ssh['dynamic_protection']
return ssh
+
def verify(ssh):
if not ssh:
return None
if 'rekey' in ssh and 'data' not in ssh['rekey']:
- raise ConfigError(f'Rekey data is required!')
+ raise ConfigError('Rekey data is required!')
+
+ if 'trusted_user_ca_key' in ssh:
+ if 'ca_certificate' not in ssh['trusted_user_ca_key']:
+ raise ConfigError('CA certificate is required for TrustedUserCAKey')
+
+ ca_key_name = ssh['trusted_user_ca_key']['ca_certificate']
+ verify_pki_ca_certificate(ssh, ca_key_name)
+ pki_ca_cert = ssh['pki']['ca'][ca_key_name]
+ if 'certificate' not in pki_ca_cert or not pki_ca_cert['certificate']:
+ raise ConfigError(f"CA certificate '{ca_key_name}' is not valid or missing")
verify_vrf(ssh)
return None
+
def generate(ssh):
if not ssh:
if os.path.isfile(config_file):
@@ -95,6 +119,22 @@ def generate(ssh):
syslog(LOG_INFO, 'SSH ed25519 host key not found, generating new key!')
call(f'ssh-keygen -q -N "" -t ed25519 -f {key_ed25519}')
+ if 'trusted_user_ca_key' in ssh:
+ ca_key_name = ssh['trusted_user_ca_key']['ca_certificate']
+ pki_ca_cert = ssh['pki']['ca'][ca_key_name]
+
+ loaded_ca_cert = load_certificate(pki_ca_cert['certificate'])
+ loaded_ca_certs = {
+ load_certificate(c['certificate'])
+ for c in ssh['pki']['ca'].values()
+ if 'certificate' in c
+ }
+
+ ca_full_chain = find_chain(loaded_ca_cert, loaded_ca_certs)
+ write_file(
+ trusted_user_ca_key, '\n'.join(encode_certificate(c) for c in ca_full_chain)
+ )
+
render(config_file, 'ssh/sshd_config.j2', ssh)
if 'dynamic_protection' in ssh:
@@ -103,12 +143,13 @@ def generate(ssh):
return None
+
def apply(ssh):
systemd_service_ssh = 'ssh.service'
systemd_service_sshguard = 'sshguard.service'
if not ssh:
# SSH access is removed in the commit
- call(f'systemctl stop ssh@*.service')
+ call('systemctl stop ssh@*.service')
call(f'systemctl stop {systemd_service_sshguard}')
return None
@@ -122,13 +163,14 @@ def apply(ssh):
if 'restart_required' in ssh:
# this is only true if something for the VRFs changed, thus we
# stop all VRF services and only restart then new ones
- call(f'systemctl stop ssh@*.service')
+ call('systemctl stop ssh@*.service')
systemd_action = 'restart'
for vrf in ssh['vrf']:
call(f'systemctl {systemd_action} ssh@{vrf}.service')
return None
+
if __name__ == '__main__':
try:
c = get_config()