Skip to content

Commit

Permalink
Add support for configurable SSH server
Browse files Browse the repository at this point in the history
  • Loading branch information
mattiaswal committed Nov 28, 2024
1 parent 1074b86 commit 6ee5432
Show file tree
Hide file tree
Showing 11 changed files with 291 additions and 18 deletions.
3 changes: 0 additions & 3 deletions board/common/rootfs/etc/ssh/sshd_config.d/var-keys.conf

This file was deleted.

24 changes: 24 additions & 0 deletions board/common/rootfs/usr/libexec/infix/mksshkey
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
#!/bin/bash
# Store and convert RSA PUBLIC/PRIVATE KEYs to be able to use them in
# OpenSSHd.
set -e

NAME="$1"
DIR="$2"
PUBLIC="$3"
PRIVATE="$4"
TMP="$(mktemp)"

echo -e '-----BEGIN RSA PRIVATE KEY-----' > "$DIR/$NAME"
echo "$PRIVATE" >> "$DIR/$NAME"
echo -e '-----END RSA PRIVATE KEY-----' >> "$DIR/$NAME"

echo -e "-----BEGIN RSA PUBLIC KEY-----" > "$TMP"
echo -e "$PUBLIC" >> "$TMP"
echo -e "-----END RSA PUBLIC KEY-----" >> "$TMP"

ssh-keygen -i -m PKCS8 -f "$TMP" > "$DIR/$NAME.pub"
chmod 0600 "$DIR/$NAME.pub"
chmod 0600 "$DIR/$NAME"
chown sshd:sshd "$DIR/$NAME.pub"
chown sshd:sshd "$DIR/$NAME"
9 changes: 0 additions & 9 deletions package/skeleton-init-finit/skeleton-init-finit.mk
Original file line number Diff line number Diff line change
Expand Up @@ -146,15 +146,6 @@ endef
SKELETON_INIT_FINIT_POST_INSTALL_TARGET_HOOKS += SKELETON_INIT_FINIT_SET_MINI_SNMPD
endif

# OpenSSH
ifeq ($(BR2_PACKAGE_OPENSSH),y)
define SKELETON_INIT_FINIT_SET_OPENSSH
cp $(SKELETON_INIT_FINIT_AVAILABLE)/sshd.conf $(FINIT_D)/available/
ln -sf ../available/sshd.conf $(FINIT_D)/enabled/sshd.conf
endef
SKELETON_INIT_FINIT_POST_INSTALL_TARGET_HOOKS += SKELETON_INIT_FINIT_SET_OPENSSH
endif

ifeq ($(BR2_PACKAGE_QUAGGA),y)
define SKELETON_INIT_FINIT_SET_QUAGGA
cp $(SKELETON_INIT_FINIT_AVAILABLE)/quagga/zebra.conf $(FINIT_D)/available/
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
task <pid/syslogd> \
[S] /usr/bin/ssh-hostkeys -- Verifying SSH host keys
service <task/ssh-hostkeys/success> env:-/etc/default/sshd \
service <> env:-/etc/default/sshd \
[2345] /usr/sbin/sshd -D $SSHD_OPTS -- OpenSSH daemon
16 changes: 16 additions & 0 deletions src/confd/share/factory.d/10-infix-services.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,22 @@
"infix-services:mdns": {
"enabled": true
},
"infix-services:ssh": {
"enabled": true,
"listen": [
{
"name": "ipv4",
"address": "0.0.0.0",
"port": 22
},
{
"name": "ipv6",
"address": "::1",
"port": 22
}
],
"hostkey": [ "genkey" ]
},
"infix-services:web": {
"enabled": true,
"console": {
Expand Down
16 changes: 16 additions & 0 deletions src/confd/share/failure.d/10-infix-services.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,21 @@
"restconf": {
"enabled": true
}
},
"infix-services:ssh": {
"enabled": true,
"listen": [
{
"name": "ipv4",
"address": "0.0.0.0",
"port": 22
},
{
"name": "ipv6",
"address": "::1",
"port": 22
}
],
"hostkey": [ "genkey" ]
}
}
16 changes: 16 additions & 0 deletions src/confd/share/test.d/10-infix-services.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,21 @@
"restconf": {
"enabled": true
}
},
"infix-services:ssh": {
"enabled": true,
"listen": [
{
"name": "ipv4",
"address": "0.0.0.0",
"port": 22
},
{
"name": "ipv6",
"address": "::",
"port": 22
}
],
"hostkey": [ "genkey" ]
}
}
149 changes: 148 additions & 1 deletion src/confd/src/infix-services.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <ctype.h>
#include <pwd.h>
#include <stdarg.h>
#include <sys/param.h>
#include <sys/utsname.h>
#include <sys/sysinfo.h>
#include <sys/types.h>
Expand All @@ -13,10 +14,14 @@
#include <srx/srx_val.h>

#include "core.h"
#include <sysrepo_types.h>

#define GENERATE_ENUM(ENUM) ENUM,
#define GENERATE_STRING(STRING) #STRING,

#define SSH_HOSTKEYS "/etc/ssh/hostkeys"
#define SSH_HOSTKEYS_NEXT SSH_HOSTKEYS"+"

#define FOREACH_SVC(SVC) \
SVC(none) \
SVC(ssh) \
Expand All @@ -27,6 +32,11 @@
SVC(netbrowse) \
SVC(all) /* must be last entry */

#define SSH_BASE "/etc/ssh"
#define SSHD_CONFIG_BASE SSH_BASE "/sshd_config.d"
#define SSHD_CONFIG_LISTEN SSHD_CONFIG_BASE "/listen.conf"
#define SSHD_CONFIG_HOSTKEY SSHD_CONFIG_BASE "/host-keys.conf"

typedef enum {
FOREACH_SVC(GENERATE_ENUM)
} svc;
Expand Down Expand Up @@ -135,6 +145,8 @@ static int svc_change(sr_session_ctx_t *session, sr_event_t event, const char *x
ena = lydx_is_enabled(srv, "enabled");
if (systemf("initctl -nbq %s %s", ena ? "enable" : "disable", svc))
ERROR("Failed %s %s", ena ? "enabling" : "disabling", name);
if (ena)
systemf("initctl -nbq touch %s", svc); /* in case already enabled */

return put(cfg, srv);
}
Expand Down Expand Up @@ -279,6 +291,72 @@ static int restconf_change(sr_session_ctx_t *session, uint32_t sub_id, const cha
return put(cfg, srv);
}

static int ssh_change(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *_confd)
{
struct lyd_node *ssh = NULL, *listen, *host_key;
sr_error_t rc = SR_ERR_OK;
sr_data_t *cfg;
FILE *fp;

switch (event) {
case SR_EV_DONE:
return svc_change(session, event, xpath, "ssh", "sshd");
case SR_EV_ENABLED:
case SR_EV_CHANGE:
break;

case SR_EV_ABORT:
default:
return SR_ERR_OK;
}

if (sr_get_data(session, xpath, 0, 0, 0, &cfg) || !cfg) {
return SR_ERR_OK;
}
ssh = cfg->tree;

if (!lydx_is_enabled(ssh, "enabled")) {
goto out;
}

fp = fopen(SSHD_CONFIG_HOSTKEY, "w");
if (!fp) {
rc = SR_ERR_INTERNAL;
goto out;
}

LY_LIST_FOR(lydx_get_child(ssh, "hostkey"), host_key) {
const char *keyname = lyd_get_value(host_key);
if (!keyname)
continue;
fprintf(fp, "HostKey %s/hostkeys/%s\n", SSH_BASE, keyname);
}

fclose(fp);

fp = fopen(SSHD_CONFIG_LISTEN, "w");
if (!fp) {
rc = SR_ERR_INTERNAL;
goto out;
}

LY_LIST_FOR(lydx_get_child(ssh, "listen"), listen) {
const char *address, *port;
int ipv6;
address = lydx_get_cattr(listen, "address");
ipv6 = !!strchr(address, ':');
port = lydx_get_cattr(listen, "port");
fprintf(fp, "ListenAddress %s%s%s:%s\n", ipv6 ? "[" : "", address, ipv6 ? "]" : "", port);
}
fclose(fp);

out:
sr_release_data(cfg);

return rc;
}

static int web_change(sr_session_ctx_t *session, uint32_t sub_id, const char *module,
const char *xpath, sr_event_t event, unsigned request_id, void *_confd)
{
Expand Down Expand Up @@ -307,6 +385,70 @@ static int web_change(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
return put(cfg, srv);
}

/* Store SSH public/private keys */
static int change_keystore_cb(sr_session_ctx_t *session, uint32_t sub_id, const char *module_name,
const char *xpath, sr_event_t event, uint32_t request_id, void *_)
{
int rc = SR_ERR_OK;
sr_data_t *cfg;
struct lyd_node *changes, *change;
switch (event) {
case SR_EV_CHANGE:
case SR_EV_ENABLED:
break;
case SR_EV_ABORT:
/* Remove */
if(fexist(SSH_HOSTKEYS_NEXT))
remove(SSH_HOSTKEYS_NEXT);
return SR_ERR_OK;
case SR_EV_DONE:
if(fexist(SSH_HOSTKEYS_NEXT)) {
if(fexist(SSH_HOSTKEYS))
remove(SSH_HOSTKEYS);
rename(SSH_HOSTKEYS_NEXT, SSH_HOSTKEYS);
svc_change(session, event, "/infix-services:ssh", "ssh", "sshd");
}
return SR_ERR_OK;

default:
return SR_ERR_OK;
}

if (sr_get_data(session, "/ietf-keystore:keystore/asymmetric-keys//.", 0, 0, 0, &cfg) || !cfg) {
return SR_ERR_OK;
}
changes = lydx_get_descendant(cfg->tree, "keystore", "asymmetric-keys", "asymmetric-key", NULL);

LYX_LIST_FOR_EACH(changes, change, "asymmetric-key") {
const char *name, *private_key_type, *public_key_type;
const char *private_key, *public_key;

name = lydx_get_cattr(change, "name");
private_key_type = lydx_get_cattr(change, "private-key-format");
public_key_type = lydx_get_cattr(change, "public-key-format");

if (strcmp(private_key_type, "ietf-crypto-types:rsa-private-key-format")) {
INFO("Private key %s is not of SSH type", name);
continue;
}

if (strcmp(public_key_type, "ietf-crypto-types:ssh-public-key-format")) {
INFO("Public key %s is not of SSH type", name);
continue;
}
private_key = lydx_get_cattr(change, "cleartext-private-key");
public_key = lydx_get_cattr(change, "public-key");
mkdir(SSH_HOSTKEYS_NEXT, 0600);
systemf("/usr/libexec/infix/mksshkey %s %s %s %s", name, SSH_HOSTKEYS_NEXT, public_key, private_key);
}

if (rc != SR_ERR_OK)
return rc;

sr_release_data(cfg);

return SR_ERR_OK;
}
int infix_services_init(struct confd *confd)
{
int rc;
Expand All @@ -315,7 +457,8 @@ int infix_services_init(struct confd *confd)
0, mdns_change, confd, &confd->sub);
REGISTER_MONITOR(confd->session, "ietf-system", "/ietf-system:system/hostname",
0, hostname_change, confd, &confd->sub);

REGISTER_CHANGE(confd->session, "infix-services", "/infix-services:ssh",
0, ssh_change, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "infix-services", "/infix-services:web",
0, web_change, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "infix-services", "/infix-services:web/infix-services:console",
Expand All @@ -327,6 +470,10 @@ int infix_services_init(struct confd *confd)
REGISTER_CHANGE(confd->session, "ieee802-dot1ab-lldp", "/ieee802-dot1ab-lldp:lldp",
0, lldp_change, confd, &confd->sub);

/* Store SSH keys */
REGISTER_CHANGE(confd->session, "ietf-keystore", "/ietf-keystore:keystore//.",
0, change_keystore_cb, confd, &confd->sub);

return SR_ERR_OK;
fail:
ERROR("init failed: %s", sr_strerror(rc));
Expand Down
4 changes: 2 additions & 2 deletions src/confd/yang/confd.inc
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ MODULES=(
"[email protected]"
"[email protected]"
"[email protected]"
"infix-services@2024-05-30.yang"
"infix-services@2024-11-26.yang"
"[email protected]"
"[email protected]"
"[email protected]"
Expand All @@ -53,7 +53,7 @@ MODULES=(
"[email protected]"
"[email protected] -e writable-running -e candidate -e rollback-on-error -e validate -e startup -e url -e xpath -e confirmed-commit"
"[email protected] -e central-truststore-supported -e certificates"
"[email protected] -e central-keystore-supported -e inline-definitions-supported -e asymmetric-keys -e symmetric-keys"
"[email protected] -e central-keystore-supported -e asymmetric-keys -e symmetric-keys"
"[email protected] -e server-ident-raw-public-key -e server-ident-x509-cert"

)
Loading

0 comments on commit 6ee5432

Please sign in to comment.