Merge ~tiago.pasqualini/charm-nagios:livestatus into charm-nagios:master

Proposed by Tiago Pasqualini da Silva
Status: Superseded
Proposed branch: ~tiago.pasqualini/charm-nagios:livestatus
Merge into: charm-nagios:master
Diff against target: 150 lines (+89/-0)
4 files modified
config.yaml (+19/-0)
hooks/templates/livestatus.tmpl (+19/-0)
hooks/upgrade_charm.py (+33/-0)
tests/functional/test_config.py (+18/-0)
Reviewer Review Type Date Requested Status
James Troup (community) Needs Fixing
Drew Freiberger (community) Approve
Xav Paice (community) Needs Information
Review via email: mp+392572@code.launchpad.net

This proposal has been superseded by a proposal from 2021-05-21.

To post a comment you must log in.
Revision history for this message
Drew Freiberger (afreiberger) wrote :

I've left a few nit-picks in the comments, but specifically, this will require functional test coverage before it can be approved.

I would also suggest filing a bug to state the requirement and help us understand urgency of the need.

I can guess why it may be needed/useful for customers to be able to monitor livestatus externally, but we're not generally spending effort on new features in nagios as it's deprecated for focal.

review: Needs Fixing
Revision history for this message
Xav Paice (xavpaice) wrote :

I'd be keen to know the reason for this change - exposing livestatus to the network increases the attack surface. If there's a bug to link to this change, please do so.

review: Needs Information
Revision history for this message
Drew Freiberger (afreiberger) wrote :

I spoke with Thiago, and this has been requested by a customer. I think with an update to default to secure and closed from the outside (only_from default to 127.0.0.1) would help a lot to ensure operators must intervene to set specific endpoints and consider the security implication.

Revision history for this message
Tiago Pasqualini da Silva (tiago.pasqualini) wrote :

Hi Drew and Xav, I have updated the patch with your requirements. Let me know if there's anything else missing

Revision history for this message
Drew Freiberger (afreiberger) wrote :

Thank you for this contribution. We will review this for appropriateness and inclusion for the 21.04 release. I see the items requested in the prior reviews have been addressed.

Revision history for this message
Celia Wang (ziyiwang) wrote :

code LGTM

Revision history for this message
Linda Guo (lihuiguo) wrote :

Hi Tiago

I understood this is a feature requested by customer. For easily tracking, can you please create a bug and link the bug to this change. Thanks.

Revision history for this message
Tiago Pasqualini da Silva (tiago.pasqualini) wrote :

Hi Linda,

Thanks for the review. I created the bug and linked on the commit message.

Thanks,
Tiago

Revision history for this message
Tiago Pasqualini da Silva (tiago.pasqualini) wrote :

Hi Folks,

Any updates on this?

Thanks,
Tiago

Revision history for this message
Drew Freiberger (afreiberger) wrote :

This LGTM now. Thank you for adding in the default least access necessary configs I'd requested. Still suggest a user story to ensure we have proper context for this change to be added to the bug.

review: Approve
Revision history for this message
James Troup (elmo) wrote :

The Juju config needs to document the security considerations of enabling this since, last I looked, livestatus supports neither Authentication nor Authorization. I also agree that some sort of user story in the bug would really help people doing archeology on this.

review: Needs Fixing
Revision history for this message
Tiago Pasqualini da Silva (tiago.pasqualini) wrote :

Done. Added the user story to the bug and added some security recommendations to the juju config.

Unmerged commits

12d8b6f... by Tiago Pasqualini da Silva

Added livestatus through xinetd

This patch adds the possibility to expose the livestatus socket to
the network by using xinetd. If enable_livestatus and
livestatus_enable_xinetd are both True, it will be exposed to the
network.

Closes-bug: #1916530

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/config.yaml b/config.yaml
index 1b6d739..765d70b 100644
--- a/config.yaml
+++ b/config.yaml
@@ -58,6 +58,25 @@ options:
58 default: ""58 default: ""
59 description: |59 description: |
60 Arguments to be passed to the livestatus module, defaults to empty.60 Arguments to be passed to the livestatus module, defaults to empty.
61 livestatus_enable_xinetd:
62 type: boolean
63 default: false
64 description: |
65 Expose livestatus socket through a network port using xinetd.
66 NOTE: this socket does not support authentication nor authorization, so exposing it to the
67 network will enable everyone to query it, so it is recommended to limit the hosts that
68 can send queries by listing those hosts in the the livestatus_xinetd_only_from option.
69 livestatus_xinetd_port:
70 type: string
71 default: '6557'
72 description: |
73 Port to be used for livestatus through xinetd.
74 livestatus_xinetd_only_from:
75 type: string
76 default: '127.0.0.1'
77 description: |
78 List of space-separated IPs that will be able to access livestatus through xinetd. If left empty,
79 only localhost will be able to access it.
61 nagios_user:80 nagios_user:
62 type: string81 type: string
63 default: nagios82 default: nagios
diff --git a/hooks/templates/livestatus.tmpl b/hooks/templates/livestatus.tmpl
64new file mode 10064483new file mode 100644
index 0000000..0480573
--- /dev/null
+++ b/hooks/templates/livestatus.tmpl
@@ -0,0 +1,19 @@
1service livestatus
2{
3 type = UNLISTED
4 socket_type = stream
5 protocol = tcp
6 wait = no
7 cps = 100 3
8 instances = 500
9 per_source = 250
10 flags = NODELAY
11 {%- if xinetd_only_from %}
12 only_from = {{ xinetd_only_from }}
13 {%- endif %}
14 disable = no
15 port = {{ xinetd_port }}
16 user = nagios
17 server = /usr/bin/unixcat
18 server_args = {{ livestatus_path }}
19}
0\ No newline at end of file20\ No newline at end of file
diff --git a/hooks/upgrade_charm.py b/hooks/upgrade_charm.py
index 03f0845..6338561 100755
--- a/hooks/upgrade_charm.py
+++ b/hooks/upgrade_charm.py
@@ -54,6 +54,11 @@ ro_password = hookenv.config("ro-password")
54nagiosadmin = hookenv.config("nagiosadmin") or "nagiosadmin"54nagiosadmin = hookenv.config("nagiosadmin") or "nagiosadmin"
55contactgroup_members = hookenv.config("contactgroup-members")55contactgroup_members = hookenv.config("contactgroup-members")
5656
57livestatus_xinetd_path = "/etc/xinetd.d/livestatus"
58livestatus_enable_xinetd = hookenv.config("livestatus_enable_xinetd")
59livestatus_xinetd_port = hookenv.config("livestatus_xinetd_port")
60livestatus_xinetd_only_from = hookenv.config("livestatus_xinetd_only_from")
61
57# this global var will collect contactgroup members that must be forced62# this global var will collect contactgroup members that must be forced
58# it will be changed by functions63# it will be changed by functions
59forced_contactgroup_members = []64forced_contactgroup_members = []
@@ -626,6 +631,33 @@ def update_password(account, password):
626 subprocess.call(["htpasswd", "-D", "/etc/nagios3/htpasswd.users", account])631 subprocess.call(["htpasswd", "-D", "/etc/nagios3/htpasswd.users", account])
627632
628633
634def configure_livestatus_xinetd():
635 if not enable_livestatus or not livestatus_enable_xinetd:
636 hookenv.log("Livestatus xinetd not enabled, skipping...", "INFO")
637 return
638
639 hookenv.log("Configuring livestatus xinetd...", "INFO")
640
641 fetch.apt_update()
642 fetch.apt_install("xinetd")
643
644 template_values = {
645 "livestatus_path": livestatus_path,
646 "xinetd_port": livestatus_xinetd_port,
647 "xinetd_only_from": livestatus_xinetd_only_from,
648 }
649
650 with open("hooks/templates/livestatus.tmpl", "r") as f:
651 template_def = f.read()
652
653 t = Template(template_def)
654 with open(livestatus_xinetd_path, "w") as f:
655 f.write(t.render(template_values))
656
657 host.service_reload("xinetd")
658 hookenv.log("Livestatus xinetd configured.", "INFO")
659
660
629warn_legacy_relations()661warn_legacy_relations()
630write_extra_config()662write_extra_config()
631# enable_traps_config and enable_pagerduty_config modify forced_contactgroup_members663# enable_traps_config and enable_pagerduty_config modify forced_contactgroup_members
@@ -642,6 +674,7 @@ update_localhost()
642update_cgi_config()674update_cgi_config()
643update_contacts()675update_contacts()
644update_password("nagiosro", ro_password)676update_password("nagiosro", ro_password)
677configure_livestatus_xinetd()
645678
646if password:679if password:
647 update_password(nagiosadmin, password)680 update_password(nagiosadmin, password)
diff --git a/tests/functional/test_config.py b/tests/functional/test_config.py
index 5b5423f..a607878 100644
--- a/tests/functional/test_config.py
+++ b/tests/functional/test_config.py
@@ -57,6 +57,17 @@ async def livestatus_path(unit):
57 yield app_config["livestatus_path"]["value"]57 yield app_config["livestatus_path"]["value"]
5858
5959
60@pytest.fixture
61async def livestatus_socket(unit):
62 """Enable livestatus socket before a test, then disable after test.
63
64 :param Agent unit: unit from the fixture
65 """
66 async with config(unit, "livestatus_enable_xinetd", "true", "false"):
67 app_config = await unit.application.get_config()
68 yield app_config["livestatus_enable_xinetd"]["value"]
69
70
60@pytest.fixture()71@pytest.fixture()
61async def enable_pagerduty(unit):72async def enable_pagerduty(unit):
62 """Enable enable_pagerduty before first test, then disable after last test.73 """Enable enable_pagerduty before first test, then disable after last test.
@@ -133,6 +144,13 @@ async def test_live_status(unit, livestatus_path, file_stat):
133 assert stat["size"] == 0, "File %s didn't match expected size" % livestatus_path144 assert stat["size"] == 0, "File %s didn't match expected size" % livestatus_path
134145
135146
147async def test_livestatus_xinetd(unit, livestatus_path, livestatus_socket, run_command):
148 assert livestatus_socket is True, "Livestatus xinetd is not enabled"
149 out = await run_command(
150 "echo -e 'GET hosts\nColumns: name\n' | nc 127.0.0.1 6557", unit.u)
151 assert "nagios" in out["Stdout"], "Livestatus output is not expected"
152
153
136async def test_pager_duty(unit, enable_pagerduty, file_stat):154async def test_pager_duty(unit, enable_pagerduty, file_stat):
137 stat = await file_stat(enable_pagerduty, unit.u)155 stat = await file_stat(enable_pagerduty, unit.u)
138 assert stat["size"] != 0, "Directory %s wasn't a non-zero size" % enable_pagerduty156 assert stat["size"] != 0, "Directory %s wasn't a non-zero size" % enable_pagerduty

Subscribers

People subscribed via source and target branches

to all changes: