Skip to content
Closed
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
2 changes: 2 additions & 0 deletions doc/ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ All notable changes to the project are documented in this file.
- Update documentation for use of VETH pairs in containers
- Issue #454: create bridges in `factory-config` with IGMP/MLD snooping
enabled by default
- Add support for optionally running user scripts from
`/cfg/user-scripts.d`
Comment on lines +25 to +26
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I liked the old name better, /cfg/start.d was short and sweet and also illustrative of when the scripts run (at startup-up).

Propose changing to that.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree. I was trying to find a common name that would work both for the directory name and the service name.

Should we also change the name of the service in that case? Suggestions?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, good question. Maybe just plain start scripts?

Considering the Finit runparts supports SXXfoo and KXXbar style notation (appends start and stop to the script), maybe we want to add support for stop scripts later, i.e., scripts that run at shutdown/reboot?


### Fixes
- Add missing LICENSE hash for factory reset tool
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
task [2] run-parts /cfg/user-scripts.d -- Running user startup scripts
2 changes: 1 addition & 1 deletion src/confd/bin/bootstrap
Original file line number Diff line number Diff line change
Expand Up @@ -226,7 +226,7 @@ sysrepoctl -s $SEARCH \
-i infix-dhcp-client@2024-04-12.yang -g wheel -p 0660 \
-i infix-shell-type@2023-08-21.yang -g wheel -p 0660 \
-i infix-system@2024-04-12.yang -g wheel -p 0660 \
-i infix-services@2024-04-08.yang -g wheel -p 0660 \
-i infix-services@2024-05-21.yang -g wheel -p 0660 \
-i ieee802-ethernet-interface@2019-06-21.yang -g wheel -p 0660 \
-i infix-ethernet-interface@2024-02-27.yang -g wheel -p 0660 \
-I "${INIT_DATA}"
Expand Down
20 changes: 19 additions & 1 deletion src/confd/src/infix-services.c
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
SVC(web) \
SVC(ttyd) \
SVC(netbrowse) \
SVC(userscripts) \
SVC(all) /* must be last entry */

typedef enum {
Expand Down Expand Up @@ -52,7 +53,7 @@ struct mdns_svc {
};

static const struct srx_module_requirement reqs[] = {
{ .dir = YANG_PATH_, .name = "infix-services", .rev = "2024-04-08" },
{ .dir = YANG_PATH_, .name = "infix-services", .rev = "2024-05-21" },
{ .dir = YANG_PATH_, .name = "ieee802-dot1ab-lldp", .rev = "2022-03-15" },
{ .dir = YANG_PATH_, .name = "infix-lldp", .rev = "2023-08-23" },
{ NULL }
Expand Down Expand Up @@ -291,6 +292,21 @@ static int web_change(sr_session_ctx_t *session, uint32_t sub_id, const char *mo
return put(cfg, srv);
}

static int userscripts_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 *srv = NULL;
sr_data_t *cfg;

cfg = get(session, event, xpath, &srv, "user-scripts", NULL);
if (!cfg)
return SR_ERR_OK;

svc_enadis(lydx_is_enabled(srv, "enabled"), none, "user-scripts");

return put(cfg, srv);
}

int infix_services_init(struct confd *confd)
{
int rc;
Expand All @@ -312,6 +328,8 @@ int infix_services_init(struct confd *confd)
0, netbrowse_change, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "ieee802-dot1ab-lldp", "/ieee802-dot1ab-lldp:lldp",
0, lldp_change, confd, &confd->sub);
REGISTER_CHANGE(confd->session, "infix-services", "/infix-services:user-scripts",
0, userscripts_change, confd, &confd->sub);

return SR_ERR_OK;
fail:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,10 @@ module infix-services {
contact "kernelkit@googlegroups.com";
description "Infix services, generic.";

revision 2024-05-21 {
description "Add support for user-scripts.";
reference "internal";
}
revision 2024-04-08 {
description "Initial support for web services.";
reference "internal";
Expand All @@ -33,6 +37,19 @@ module infix-services {
}
}

container user-scripts {
description "Run scripts in /cfg/user-scripts.d on system startup

CAUTION! Anyone who can write to this directory will be able to
install a permanent backdoor on the system. This could include
a remote attacker with brief window of access.";

leaf enabled {
description "Enable or disable user script execution";
type boolean;
}
}

container web {
description "Web services";

Expand Down
63 changes: 63 additions & 0 deletions test/case/infix_services/user_scripts.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/usr/bin/env python3
#

import random
import string

import infamy

def attach(env):
return (env.attach("target", "mgmt"), env.attach("target", "mgmt", "ssh"))

with infamy.Test() as test:
with test.step("Initialize"):
env = infamy.Env(infamy.std_topology("1x1"))
target, tgtssh = attach(env)

cookie = "".join((random.choices(string.ascii_lowercase, k=16)))
script = f"/cfg/user-scripts.d/infamy-user-script-test.sh"
output = f"/tmp/{cookie}.cookie"

with test.step(f"Install {script}"):
tgtssh.runsh(f"""
set -e

mkdir -p $(dirname {script})
printf '#!/bin/sh\necho -n {cookie} >{output}\n' >{script}
chmod +x {script}
""")

with test.step("Enable user scripts and reboot"):
target.put_config_dict("infix-services", {
"user-scripts": {
"enabled": True
}
})
target.copy("running", "startup")
target.reboot()
infamy.util.wait_boot(target) or test.fail()
target, tgtssh = attach(env)

with test.step(f"Verify that {script} has run"):
cat = tgtssh.runsh(f"cat {output}").stdout
assert cat == cookie, f"Read back {repr(cat)}, expected {repr(cookie)}"

with test.step("Disable user scripts and reboot"):
target.put_config_dict("infix-services", {
"user-scripts": {
"enabled": False
}
})
target.copy("running", "startup")
target.reboot()
infamy.util.wait_boot(target) or test.fail()
target, tgtssh = attach(env)

with test.step(f"Verify that {script} has not run"):
exists = tgtssh.run(["test", "-f", f"{output}"]).returncode == 0
assert not exists, f"Unexpectedly found {output}"

with test.step(f"Remove {script}"):
tgtssh.runsh(f"rm {script}")

test.succeed()