Merge ~peter-sabaini/charm-sudo-pair:add-logging into ~sudo-pair-charmers/charm-sudo-pair:master

Proposed by Peter Sabaini
Status: Superseded
Proposed branch: ~peter-sabaini/charm-sudo-pair:add-logging
Merge into: ~sudo-pair-charmers/charm-sudo-pair:master
Diff against target: 242 lines (+119/-5)
8 files modified
actions/actions.py (+11/-1)
config.yaml (+14/-0)
files/pagerdutyevent.py (+62/-0)
lib/libsudopair.py (+17/-1)
reactive/sudo_pair.py (+4/-0)
templates/sudo_approve.tmpl (+5/-2)
templates/sudo_pair.pagerduty.tmpl (+5/-0)
tests/unit/test_libsudopair.py (+1/-1)
Reviewer Review Type Date Requested Status
Paul Goins Needs Fixing
Giuseppe Petralia Pending
Review via email: mp+394340@code.launchpad.net

This proposal supersedes a proposal from 2020-01-21.

This proposal has been superseded by a proposal from 2021-06-09.

Commit message

Pagerduty alerting, action logging

Ability to have pagerduty alerts triggered on auto-approve. Revamp logging, add logging for remove-sudopair action. Use https proxy to contact the PD events endpoint if set in model config.

To post a comment you must log in.
Revision history for this message
Giuseppe Petralia (peppepetra) wrote : Posted in a previous version of this proposal

Small comment on the pagerduty_proxy that may be removed in favor of the juju model-config if any.

Comments in line.

Other than that looks good to me.

review: Needs Fixing
Revision history for this message
Paul Goins (vultaire) wrote : Posted in a previous version of this proposal

I agree with Giuseppe. I think we should pull the proxy from the env's JUJU_CHARM_HTTPS_PROXY variable to avoid proliferating proxy settings. Other than that I'm +1.

review: Needs Fixing
Revision history for this message
Peter Sabaini (peter-sabaini) wrote :

Thanks -- proxy config updated as requested. I've resubmitted the branch as there was some code reorg interim.

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

LGTM.

Revision history for this message
Paul Goins (vultaire) wrote :

Sorry for the delay here - It looks like this is close to if not identical to https://code.launchpad.net/~peter-sabaini/charm-sudo-pair/+git/sudo-pair-charm/+merge/377884, except that it might be based off newer code. I don't see any changes re: proxy variable use; this code is still using pagerduty_proxy rather than e.g. JUJU_CHARM_HTTPS_PROXY.

review: Needs Fixing

Unmerged commits

55e2099... by Peter Sabaini

Pagerduty alerting, action logging

- Ability to have pagerduty alerts triggered on auto-approve

- Revamp logging, add logging for remove-sudopair action

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
diff --git a/actions/actions.py b/actions/actions.py
index ccf3ffc..9ff1b7f 100755
--- a/actions/actions.py
+++ b/actions/actions.py
@@ -13,21 +13,31 @@
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and14# See the License for the specific language governing permissions and
15# limitations under the License.15# limitations under the License.
16import logging
16import os17import os
18import subprocess
17import sys19import sys
1820
19from charmhelpers.core.hookenv import action_fail, action_set21from charmhelpers.core.hookenv import action_fail, action_set
2022
21
22sys.path.append("lib")23sys.path.append("lib")
2324
24import libsudopair # NOQA25import libsudopair # NOQA
2526
2627
28logger = logging.getLogger('sudopair')
29logger.setLevel(logging.INFO)
30handler = logging.handlers.SysLogHandler(address='/dev/log', facility=logging.handlers.SysLogHandler.LOG_AUTH)
31logger.addHandler(handler)
32
33
27def remove():34def remove():
28 """Remove sudo-pair config and binaries."""35 """Remove sudo-pair config and binaries."""
29 sph = libsudopair.SudoPairHelper()36 sph = libsudopair.SudoPairHelper()
30 sph.deconfigure()37 sph.deconfigure()
38 msg = "WARNING removed sudo-pair via remove-sudopair action"
39 logger.warning(msg)
40 subprocess.run(["/usr/bin/pagerdutyevent.py", msg])
31 action_set({"message": "Successfully removed sudo-pair config and binaries"})41 action_set({"message": "Successfully removed sudo-pair config and binaries"})
3242
3343
diff --git a/config.yaml b/config.yaml
index 795269b..9eca76c 100644
--- a/config.yaml
+++ b/config.yaml
@@ -19,3 +19,17 @@ options:
19 type: boolean19 type: boolean
20 default: true20 default: true
21 description: "If true, auto approval is permitted."21 description: "If true, auto approval is permitted."
22 pagerduty_key:
23 type: string
24 default: ''
25 description: "If set, a pagerduty event will be triggered upon auto-approving"
26 pagerduty_proxy:
27 type: string
28 default: ''
29 description: "If set, will use as a proxy to reach the pagerduty API"
30 pagerduty_context:
31 type: string
32 default: ''
33 description: "Prefix to add to pagerduty events"
34
35
diff --git a/files/pagerdutyevent.py b/files/pagerdutyevent.py
22new file mode 10064436new file mode 100644
index 0000000..e154e04
--- /dev/null
+++ b/files/pagerdutyevent.py
@@ -0,0 +1,62 @@
1#!/usr/bin/env python3
2import argparse
3import configparser
4import json
5import requests
6import logging
7import logging.handlers
8
9
10logger = logging.getLogger('sudopair')
11logger.setLevel(logging.INFO)
12handler = logging.handlers.SysLogHandler(address='/dev/log', facility=logging.handlers.SysLogHandler.LOG_AUTH)
13logger.addHandler(handler)
14
15
16def args():
17 parser = argparse.ArgumentParser()
18 parser.add_argument("summary")
19 return parser.parse_args()
20
21
22def trigger_incident(pd_key, summary, source, proxy):
23
24 header = {
25 "Content-Type": "application/json"
26 }
27
28 payload = {
29 "routing_key": pd_key,
30 "event_action": "trigger",
31 "payload": {
32 "summary": summary,
33 "source": source,
34 "severity": "info"
35 }
36 }
37
38 response = requests.post('https://events.pagerduty.com/v2/enqueue',
39 data=json.dumps(payload),
40 proxies={'https': proxy},
41 headers=header)
42
43 if response.json()["status"] == "success":
44 logger.info("Triggered alert with key {}".format(response.json()['dedup_key']))
45 else:
46 logger.warning("Failed to send pagerduty alert: {}".format(response.text))
47
48
49def get_conf():
50 config = configparser.ConfigParser()
51 config.read("/etc/sudo_pair.pagerduty.ini")
52 sec = config['DEFAULT']
53 return sec['pagerduty_key'], sec['pagerduty_source'], sec['pagerduty_proxy']
54
55
56def main():
57 pd_key, source, proxy = get_conf()
58 trigger_incident(pd_key, args().summary, source, proxy)
59
60
61if __name__ == '__main__':
62 main()
diff --git a/lib/libsudopair.py b/lib/libsudopair.py
index 05d1f9d..4c01bb2 100644
--- a/lib/libsudopair.py
+++ b/lib/libsudopair.py
@@ -64,6 +64,9 @@ class SudoPairHelper(object):
64 self.sudoers_perms = 0o44064 self.sudoers_perms = 0o440
65 self.sudo_conf_perms = 0o64465 self.sudo_conf_perms = 0o644
66 self.sudo_approve_perms = 0o75566 self.sudo_approve_perms = 0o755
67 self.pagerduty_conf_path = '/etc/sudo_pair.pagerduty.ini'
68 self.pagerduty_path = '/usr/bin/pagerdutyevent.py'
69 self.pagerduty_perms = 0o755
6770
68 def get_config(self):71 def get_config(self):
69 """Return config as a dict."""72 """Return config as a dict."""
@@ -74,8 +77,9 @@ class SudoPairHelper(object):
74 'socket_dir': self.socket_dir,77 'socket_dir': self.socket_dir,
75 'gids_enforced': group_names_to_group_ids(self.charm_config['groups_enforced']),78 'gids_enforced': group_names_to_group_ids(self.charm_config['groups_enforced']),
76 'gids_exempted': group_names_to_group_ids(self.charm_config['groups_exempted']),79 'gids_exempted': group_names_to_group_ids(self.charm_config['groups_exempted']),
80 'pagerduty_key': self.charm_config.get('pagerduty_key'),
81 'pagerduty_proxy': self.charm_config.get('pagerduty_proxy'),
77 }82 }
78
79 config.update(self.charm_config)83 config.update(self.charm_config)
80 return config84 return config
8185
@@ -117,6 +121,10 @@ class SudoPairHelper(object):
117 sudoers_file = os.path.join(hookenv.charm_dir(), 'files', 'sudoers')121 sudoers_file = os.path.join(hookenv.charm_dir(), 'files', 'sudoers')
118 copy_file(sudoers_file, self.sudoers_path, self.owner, self.group, self.sudoers_perms)122 copy_file(sudoers_file, self.sudoers_path, self.owner, self.group, self.sudoers_perms)
119123
124 def copy_pagerduty(self):
125 pd_file = os.path.join(hookenv.charm_dir(), 'files', 'pagerdutyevent.py')
126 copy_file(pd_file, self.pagerduty_path, self.owner, self.group, self.pagerduty_perms)
127
120 def render_sudo_approve(self):128 def render_sudo_approve(self):
121 """Render sudo-approve file."""129 """Render sudo-approve file."""
122 hookenv.log("Rendering sudo_approve.tmpl to {}".format(self.binary_path))130 hookenv.log("Rendering sudo_approve.tmpl to {}".format(self.binary_path))
@@ -131,6 +139,14 @@ class SudoPairHelper(object):
131 self.get_config(), perms=0o440, owner=self.owner, group=self.group)139 self.get_config(), perms=0o440, owner=self.owner, group=self.group)
132 return None140 return None
133141
142 def render_pagerduty_conf(self):
143 pd_source = '{}-{}'.format(self.charm_config.get('pagerduty_context', 'juju'), hookenv.principal_unit())
144 cfg = self.get_config()
145 cfg['pagerduty_source'] = pd_source
146 hookenv.log("Rendering pagerduty configuration to {}".format(self.pagerduty_conf_path))
147 return templating.render('sudo_pair.pagerduty.tmpl', self.pagerduty_conf_path, cfg,
148 owner=self.owner, group=self.group)
149
134 def deconfigure(self):150 def deconfigure(self):
135 """Remove sudo-pair configuration."""151 """Remove sudo-pair configuration."""
136 paths = [152 paths = [
diff --git a/reactive/sudo_pair.py b/reactive/sudo_pair.py
index 974dd66..bce23ca 100644
--- a/reactive/sudo_pair.py
+++ b/reactive/sudo_pair.py
@@ -26,6 +26,10 @@ def install_sudo_pair():
26 # Add "Defaults log_output to /etc/sudoers26 # Add "Defaults log_output to /etc/sudoers
27 sph.copy_sudoers()27 sph.copy_sudoers()
2828
29 # Add pagerduty script and config
30 sph.copy_pagerduty()
31 sph.render_pagerduty_conf()
32
29 # If there are cmds to bypass sudo pairing create file unders sudoers.d33 # If there are cmds to bypass sudo pairing create file unders sudoers.d
30 sph.render_bypass_cmds()34 sph.render_bypass_cmds()
3135
diff --git a/templates/sudo_approve.tmpl b/templates/sudo_approve.tmpl
index 7164b5a..49665d3 100755
--- a/templates/sudo_approve.tmpl
+++ b/templates/sudo_approve.tmpl
@@ -88,7 +88,7 @@ main() {
88 declare -r username88 declare -r username
8989
90 declare log_line90 declare log_line
91 log_line="$(date "+[%b %d %H:%M:%S] WARNING: ${username} approved is own sudo session.")"91 log_line="WARNING: ${username} approved own sudo session."
92 declare -r log_line92 declare -r log_line
9393
94 if [[ "${uid}" -eq "${ruid}" ]]; then94 if [[ "${uid}" -eq "${ruid}" ]]; then
@@ -97,7 +97,10 @@ main() {
97 exit 197 exit 1
98 {% else %}98 {% else %}
99 echo "You are approving your own session. The incident will be logged."99 echo "You are approving your own session. The incident will be logged."
100 echo ${log_line} >> /var/log/sudo_pair.log100 logger -p auth.warn $log_line
101 {% if pagerduty_key %}
102 /usr/bin/pagerdutyevent.py "$log_line"
103 {% endif %}
101 {% endif %}104 {% endif %}
102 fi105 fi
103106
diff --git a/templates/sudo_pair.pagerduty.tmpl b/templates/sudo_pair.pagerduty.tmpl
104new file mode 100644107new file mode 100644
index 0000000..d670f23
--- /dev/null
+++ b/templates/sudo_pair.pagerduty.tmpl
@@ -0,0 +1,5 @@
1[DEFAULT]
2pagerduty_key: {{ pagerduty_key }}
3pagerduty_proxy: {{ pagerduty_proxy }}
4pagerduty_source: {{ pagerduty_source }}
5
diff --git a/tests/unit/test_libsudopair.py b/tests/unit/test_libsudopair.py
index 3a598af..9ddff50 100644
--- a/tests/unit/test_libsudopair.py
+++ b/tests/unit/test_libsudopair.py
@@ -131,7 +131,7 @@ class TestSudoPairHelper():
131 def test_render_sudo_approve(self, sph, tmpdir):131 def test_render_sudo_approve(self, sph, tmpdir):
132 """Check that sudo_approve file is rendered correctly."""132 """Check that sudo_approve file is rendered correctly."""
133 # Auto Approve true133 # Auto Approve true
134 expected_content = 'echo ${log_line} >> /var/log/sudo_pair.log'134 expected_content = 'logger -p auth.warn $log_line'
135 socket_dir = tmpdir.join('/var/run/sudo_pair')135 socket_dir = tmpdir.join('/var/run/sudo_pair')
136 expected_content_socket_dir = 'declare -r SUDO_SOCKET_PATH="{}"'.format(socket_dir)136 expected_content_socket_dir = 'declare -r SUDO_SOCKET_PATH="{}"'.format(socket_dir)
137 content = sph.render_sudo_approve()137 content = sph.render_sudo_approve()

Subscribers

People subscribed via source and target branches