confd: Add YANG pattern constraints for string leaves

Add input validation patterns to string-type leaves in container,
firewall, DHCP, and interface YANG models.  Also use mkdtemp() for
temporary key files in keystore.

Signed-off-by: Mattias Walström <lazzer@gmail.com>
This commit is contained in:
Mattias Walström
2026-02-27 08:35:17 +01:00
committed by Joachim Wiberg
parent d7812c2ea5
commit ed4fe58fda
8 changed files with 68 additions and 26 deletions
+1 -1
View File
@@ -2,6 +2,7 @@
.claude .claude
.gdb_history .gdb_history
.claude .claude
AGENTS.md
/.backup /.backup
/.ccache /.ccache
/dl /dl
@@ -11,4 +12,3 @@
/test/.log /test/.log
/local.mk /local.mk
/test/spec/Readme.adoc /test/spec/Readme.adoc
+16 -9
View File
@@ -12,8 +12,6 @@
#define XPATH_KEYSTORE_ASYM "/ietf-keystore:keystore/asymmetric-keys" #define XPATH_KEYSTORE_ASYM "/ietf-keystore:keystore/asymmetric-keys"
#define XPATH_KEYSTORE_SYM "/ietf-keystore:keystore/symmetric-keys" #define XPATH_KEYSTORE_SYM "/ietf-keystore:keystore/symmetric-keys"
#define SSH_PRIVATE_KEY "/tmp/ssh.key"
#define SSH_PUBLIC_KEY "/tmp/ssh.pub"
/* return file size */ /* return file size */
static size_t filesz(const char *fn) static size_t filesz(const char *fn)
@@ -91,6 +89,9 @@ static int keystore_update(sr_session_ctx_t *session, struct lyd_node *config, s
} }
for (size_t i = 0; i < count; i++) { for (size_t i = 0; i < count; i++) {
char tmpdir[] = "/tmp/keystore.XXXXXX";
char priv_path[sizeof(tmpdir) + 16];
char pub_path[sizeof(tmpdir) + 16];
char *name = srx_get_str(session, "%s/name", list[i].xpath); char *name = srx_get_str(session, "%s/name", list[i].xpath);
char *public_key_format, *private_key_format; char *public_key_format, *private_key_format;
char *pub_key = NULL, *priv_key = NULL; char *pub_key = NULL, *priv_key = NULL;
@@ -115,16 +116,24 @@ static int keystore_update(sr_session_ctx_t *session, struct lyd_node *config, s
continue; continue;
NOTE("SSH key (%s) does not exist, generating...", name); NOTE("SSH key (%s) does not exist, generating...", name);
if (systemf("/usr/libexec/infix/mkkeys %s %s", SSH_PRIVATE_KEY, SSH_PUBLIC_KEY)) {
if (!mkdtemp(tmpdir)) {
ERRNO("Failed creating temp dir for SSH key generation");
goto next;
}
snprintf(priv_path, sizeof(priv_path), "%s/ssh.key", tmpdir);
snprintf(pub_path, sizeof(pub_path), "%s/ssh.pub", tmpdir);
if (systemf("/usr/libexec/infix/mkkeys %s %s", priv_path, pub_path)) {
ERROR("Failed generating SSH keys for %s", name); ERROR("Failed generating SSH keys for %s", name);
goto next; goto next;
} }
priv_key = filerd(SSH_PRIVATE_KEY, filesz(SSH_PRIVATE_KEY)); priv_key = filerd(priv_path, filesz(priv_path));
if (!priv_key) if (!priv_key)
goto next; goto next;
pub_key = filerd(SSH_PUBLIC_KEY, filesz(SSH_PUBLIC_KEY)); pub_key = filerd(pub_path, filesz(pub_path));
if (!pub_key) if (!pub_key)
goto next; goto next;
@@ -140,10 +149,8 @@ static int keystore_update(sr_session_ctx_t *session, struct lyd_node *config, s
goto next; goto next;
} }
next: next:
if (erase(SSH_PRIVATE_KEY)) if (rmrf(tmpdir))
ERRNO("Failed removing SSH server private key"); ERRNO("Failed removing temp dir %s", tmpdir);
if (erase(SSH_PUBLIC_KEY))
ERRNO("Failed removing SSH server public key");
if (priv_key) if (priv_key)
free(priv_key); free(priv_key);
+31 -9
View File
@@ -22,6 +22,11 @@ module infix-containers {
prefix infix-sys; prefix infix-sys;
} }
revision 2026-02-23 {
description "Input validation improvements";
reference "internal";
}
revision 2025-12-09 { revision 2025-12-09 {
description "Add resource management: description "Add resource management:
- Add resource-limit container with memory and cpu configuration. - Add resource-limit container with memory and cpu configuration.
@@ -84,6 +89,17 @@ module infix-containers {
} }
} }
typedef image-reference {
description "OCI image reference or transport URI.
Allows registry references (e.g., quay.io/user/image:tag),
transport prefixes (docker://, oci-archive:, dir:), and
download URLs (ftp://, http://, https://).";
type string {
pattern '[a-zA-Z0-9][a-zA-Z0-9_./:@=+%~-]*';
}
}
typedef mount-type { typedef mount-type {
type enumeration { type enumeration {
enum bind { enum bind {
@@ -181,7 +197,7 @@ module infix-containers {
container will be put on a queue that retries pull every time container will be put on a queue that retries pull every time
there is a route change in the host's system."; there is a route change in the host's system.";
mandatory true; mandatory true;
type string; type image-reference;
} }
container checksum { container checksum {
@@ -240,7 +256,9 @@ module infix-containers {
leaf command { leaf command {
description "Override ENTRYPOINT from image and run command + args."; description "Override ENTRYPOINT from image and run command + args.";
type string; type string {
pattern '[a-zA-Z0-9_./ :=@%^,+-]+';
}
} }
leaf hostname { leaf hostname {
@@ -298,7 +316,9 @@ module infix-containers {
NOTE: Some (*) options only work with masquerading container bridges, NOTE: Some (*) options only work with masquerading container bridges,
which automatically create VETH pairs with one end in the host which automatically create VETH pairs with one end in the host
bridge and the other in the container."; bridge and the other in the container.";
type string; type string {
pattern '[a-zA-Z_][a-zA-Z0-9_]*=[a-zA-Z0-9.:_/-]+';
}
} }
} }
@@ -310,7 +330,9 @@ module infix-containers {
Sample: 8080:80 -- forward tcp port 8080 to container port 80 Sample: 8080:80 -- forward tcp port 8080 to container port 80
69:69/udp -- forward udp port 69 to container port 69 69:69/udp -- forward udp port 69 to container port 69
127.0.0.1:8080:80 -- forward only from loopback interface"; 127.0.0.1:8080:80 -- forward only from loopback interface";
type string; type string {
pattern '[0-9.:\[\]]+(/[a-zA-Z]+)?';
}
} }
leaf-list dns { leaf-list dns {
@@ -464,7 +486,7 @@ module infix-containers {
When mounting files, directories (and globs) from the host, When mounting files, directories (and globs) from the host,
the source must be an absolute path."; the source must be an absolute path.";
type string { type string {
pattern '/.*'; pattern '/[a-zA-Z0-9_./*?-]+([ ]?[a-zA-Z0-9_./*?-]+)*';
} }
} }
} }
@@ -493,7 +515,7 @@ module infix-containers {
or import the text file using the 'content' node."; or import the text file using the 'content' node.";
mandatory true; mandatory true;
type string { type string {
pattern '/.*'; pattern '/[a-zA-Z0-9_./-]+(/[a-zA-Z0-9_./-]+)*';
} }
} }
@@ -545,7 +567,7 @@ module infix-containers {
description "Absolute path to target destination directory inside the container."; description "Absolute path to target destination directory inside the container.";
mandatory true; mandatory true;
type string { type string {
pattern '/.*'; pattern '/[a-zA-Z0-9_./-]+(/[a-zA-Z0-9_./-]+)*';
} }
} }
} }
@@ -593,12 +615,12 @@ module infix-containers {
input { input {
leaf uri { leaf uri {
description "The URL or local file path, e.g., /lib/oci/archive.tar.gz"; description "The URL or local file path, e.g., /lib/oci/archive.tar.gz";
type string; type image-reference;
mandatory true; mandatory true;
} }
leaf name { leaf name {
description "Image name[:tag], default: basename of archive dir + :latest"; description "Image name[:tag], default: basename of archive dir + :latest";
type string; type image-reference;
} }
} }
} }
+9 -3
View File
@@ -113,7 +113,9 @@ module infix-dhcp-server {
case default-opt { case default-opt {
leaf string { leaf string {
description "Generic string value."; description "Generic string value.";
type string; type string {
pattern '[^\p{Cc}]*';
}
} }
} }
} }
@@ -150,7 +152,9 @@ module infix-dhcp-server {
leaf description { leaf description {
description "Additional information about this subnet (e.g., purpose, location, or notes)."; description "Additional information about this subnet (e.g., purpose, location, or notes).";
type string; type string {
pattern '[^\p{Cc}]*';
}
} }
leaf enabled { leaf enabled {
@@ -206,7 +210,9 @@ module infix-dhcp-server {
leaf description { leaf description {
description "Additional information about this entry, e.g., owner's name, equipment details, or location."; description "Additional information about this entry, e.g., owner's name, equipment details, or location.";
type string; type string {
pattern '[^\p{Cc}]*';
}
} }
container match { container match {
+9 -3
View File
@@ -201,7 +201,9 @@ module infix-firewall {
leaf description { leaf description {
description "Free-form description of the zone."; description "Free-form description of the zone.";
type string; type string {
pattern '[^\p{Cc}<>&]*';
}
} }
leaf-list interface { leaf-list interface {
@@ -304,7 +306,9 @@ module infix-firewall {
leaf description { leaf description {
description "Free-form description of this policy's purpose and scope."; description "Free-form description of this policy's purpose and scope.";
type string; type string {
pattern '[^\p{Cc}<>&]*';
}
} }
leaf-list ingress { leaf-list ingress {
@@ -421,7 +425,9 @@ module infix-firewall {
leaf description { leaf description {
description "Free-form description of the service."; description "Free-form description of the service.";
type string; type string {
pattern '[^\p{Cc}<>&]*';
}
} }
list port { list port {
@@ -204,6 +204,7 @@ module infix-interfaces {
deviate replace { deviate replace {
type string { type string {
length "0..64"; length "0..64";
pattern '[^\p{Cc}]*';
} }
} }
} }
+1 -1
View File
@@ -1,5 +1,5 @@
# -*- sh -*- # -*- sh -*-
MODULES=( MODULES=(
"infix-interfaces -e containers" "infix-interfaces -e containers"
"infix-containers@2025-12-09.yang" "infix-containers@2026-02-23.yang"
) )