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
1diff --git a/actions/actions.py b/actions/actions.py
2index ccf3ffc..9ff1b7f 100755
3--- a/actions/actions.py
4+++ b/actions/actions.py
5@@ -13,21 +13,31 @@
6 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
7 # See the License for the specific language governing permissions and
8 # limitations under the License.
9+import logging
10 import os
11+import subprocess
12 import sys
13
14 from charmhelpers.core.hookenv import action_fail, action_set
15
16-
17 sys.path.append("lib")
18
19 import libsudopair # NOQA
20
21
22+logger = logging.getLogger('sudopair')
23+logger.setLevel(logging.INFO)
24+handler = logging.handlers.SysLogHandler(address='/dev/log', facility=logging.handlers.SysLogHandler.LOG_AUTH)
25+logger.addHandler(handler)
26+
27+
28 def remove():
29 """Remove sudo-pair config and binaries."""
30 sph = libsudopair.SudoPairHelper()
31 sph.deconfigure()
32+ msg = "WARNING removed sudo-pair via remove-sudopair action"
33+ logger.warning(msg)
34+ subprocess.run(["/usr/bin/pagerdutyevent.py", msg])
35 action_set({"message": "Successfully removed sudo-pair config and binaries"})
36
37
38diff --git a/config.yaml b/config.yaml
39index 795269b..9eca76c 100644
40--- a/config.yaml
41+++ b/config.yaml
42@@ -19,3 +19,17 @@ options:
43 type: boolean
44 default: true
45 description: "If true, auto approval is permitted."
46+ pagerduty_key:
47+ type: string
48+ default: ''
49+ description: "If set, a pagerduty event will be triggered upon auto-approving"
50+ pagerduty_proxy:
51+ type: string
52+ default: ''
53+ description: "If set, will use as a proxy to reach the pagerduty API"
54+ pagerduty_context:
55+ type: string
56+ default: ''
57+ description: "Prefix to add to pagerduty events"
58+
59+
60diff --git a/files/pagerdutyevent.py b/files/pagerdutyevent.py
61new file mode 100644
62index 0000000..e154e04
63--- /dev/null
64+++ b/files/pagerdutyevent.py
65@@ -0,0 +1,62 @@
66+#!/usr/bin/env python3
67+import argparse
68+import configparser
69+import json
70+import requests
71+import logging
72+import logging.handlers
73+
74+
75+logger = logging.getLogger('sudopair')
76+logger.setLevel(logging.INFO)
77+handler = logging.handlers.SysLogHandler(address='/dev/log', facility=logging.handlers.SysLogHandler.LOG_AUTH)
78+logger.addHandler(handler)
79+
80+
81+def args():
82+ parser = argparse.ArgumentParser()
83+ parser.add_argument("summary")
84+ return parser.parse_args()
85+
86+
87+def trigger_incident(pd_key, summary, source, proxy):
88+
89+ header = {
90+ "Content-Type": "application/json"
91+ }
92+
93+ payload = {
94+ "routing_key": pd_key,
95+ "event_action": "trigger",
96+ "payload": {
97+ "summary": summary,
98+ "source": source,
99+ "severity": "info"
100+ }
101+ }
102+
103+ response = requests.post('https://events.pagerduty.com/v2/enqueue',
104+ data=json.dumps(payload),
105+ proxies={'https': proxy},
106+ headers=header)
107+
108+ if response.json()["status"] == "success":
109+ logger.info("Triggered alert with key {}".format(response.json()['dedup_key']))
110+ else:
111+ logger.warning("Failed to send pagerduty alert: {}".format(response.text))
112+
113+
114+def get_conf():
115+ config = configparser.ConfigParser()
116+ config.read("/etc/sudo_pair.pagerduty.ini")
117+ sec = config['DEFAULT']
118+ return sec['pagerduty_key'], sec['pagerduty_source'], sec['pagerduty_proxy']
119+
120+
121+def main():
122+ pd_key, source, proxy = get_conf()
123+ trigger_incident(pd_key, args().summary, source, proxy)
124+
125+
126+if __name__ == '__main__':
127+ main()
128diff --git a/lib/libsudopair.py b/lib/libsudopair.py
129index 05d1f9d..4c01bb2 100644
130--- a/lib/libsudopair.py
131+++ b/lib/libsudopair.py
132@@ -64,6 +64,9 @@ class SudoPairHelper(object):
133 self.sudoers_perms = 0o440
134 self.sudo_conf_perms = 0o644
135 self.sudo_approve_perms = 0o755
136+ self.pagerduty_conf_path = '/etc/sudo_pair.pagerduty.ini'
137+ self.pagerduty_path = '/usr/bin/pagerdutyevent.py'
138+ self.pagerduty_perms = 0o755
139
140 def get_config(self):
141 """Return config as a dict."""
142@@ -74,8 +77,9 @@ class SudoPairHelper(object):
143 'socket_dir': self.socket_dir,
144 'gids_enforced': group_names_to_group_ids(self.charm_config['groups_enforced']),
145 'gids_exempted': group_names_to_group_ids(self.charm_config['groups_exempted']),
146+ 'pagerduty_key': self.charm_config.get('pagerduty_key'),
147+ 'pagerduty_proxy': self.charm_config.get('pagerduty_proxy'),
148 }
149-
150 config.update(self.charm_config)
151 return config
152
153@@ -117,6 +121,10 @@ class SudoPairHelper(object):
154 sudoers_file = os.path.join(hookenv.charm_dir(), 'files', 'sudoers')
155 copy_file(sudoers_file, self.sudoers_path, self.owner, self.group, self.sudoers_perms)
156
157+ def copy_pagerduty(self):
158+ pd_file = os.path.join(hookenv.charm_dir(), 'files', 'pagerdutyevent.py')
159+ copy_file(pd_file, self.pagerduty_path, self.owner, self.group, self.pagerduty_perms)
160+
161 def render_sudo_approve(self):
162 """Render sudo-approve file."""
163 hookenv.log("Rendering sudo_approve.tmpl to {}".format(self.binary_path))
164@@ -131,6 +139,14 @@ class SudoPairHelper(object):
165 self.get_config(), perms=0o440, owner=self.owner, group=self.group)
166 return None
167
168+ def render_pagerduty_conf(self):
169+ pd_source = '{}-{}'.format(self.charm_config.get('pagerduty_context', 'juju'), hookenv.principal_unit())
170+ cfg = self.get_config()
171+ cfg['pagerduty_source'] = pd_source
172+ hookenv.log("Rendering pagerduty configuration to {}".format(self.pagerduty_conf_path))
173+ return templating.render('sudo_pair.pagerduty.tmpl', self.pagerduty_conf_path, cfg,
174+ owner=self.owner, group=self.group)
175+
176 def deconfigure(self):
177 """Remove sudo-pair configuration."""
178 paths = [
179diff --git a/reactive/sudo_pair.py b/reactive/sudo_pair.py
180index 974dd66..bce23ca 100644
181--- a/reactive/sudo_pair.py
182+++ b/reactive/sudo_pair.py
183@@ -26,6 +26,10 @@ def install_sudo_pair():
184 # Add "Defaults log_output to /etc/sudoers
185 sph.copy_sudoers()
186
187+ # Add pagerduty script and config
188+ sph.copy_pagerduty()
189+ sph.render_pagerduty_conf()
190+
191 # If there are cmds to bypass sudo pairing create file unders sudoers.d
192 sph.render_bypass_cmds()
193
194diff --git a/templates/sudo_approve.tmpl b/templates/sudo_approve.tmpl
195index 7164b5a..49665d3 100755
196--- a/templates/sudo_approve.tmpl
197+++ b/templates/sudo_approve.tmpl
198@@ -88,7 +88,7 @@ main() {
199 declare -r username
200
201 declare log_line
202- log_line="$(date "+[%b %d %H:%M:%S] WARNING: ${username} approved is own sudo session.")"
203+ log_line="WARNING: ${username} approved own sudo session."
204 declare -r log_line
205
206 if [[ "${uid}" -eq "${ruid}" ]]; then
207@@ -97,7 +97,10 @@ main() {
208 exit 1
209 {% else %}
210 echo "You are approving your own session. The incident will be logged."
211- echo ${log_line} >> /var/log/sudo_pair.log
212+ logger -p auth.warn $log_line
213+ {% if pagerduty_key %}
214+ /usr/bin/pagerdutyevent.py "$log_line"
215+ {% endif %}
216 {% endif %}
217 fi
218
219diff --git a/templates/sudo_pair.pagerduty.tmpl b/templates/sudo_pair.pagerduty.tmpl
220new file mode 100644
221index 0000000..d670f23
222--- /dev/null
223+++ b/templates/sudo_pair.pagerduty.tmpl
224@@ -0,0 +1,5 @@
225+[DEFAULT]
226+pagerduty_key: {{ pagerduty_key }}
227+pagerduty_proxy: {{ pagerduty_proxy }}
228+pagerduty_source: {{ pagerduty_source }}
229+
230diff --git a/tests/unit/test_libsudopair.py b/tests/unit/test_libsudopair.py
231index 3a598af..9ddff50 100644
232--- a/tests/unit/test_libsudopair.py
233+++ b/tests/unit/test_libsudopair.py
234@@ -131,7 +131,7 @@ class TestSudoPairHelper():
235 def test_render_sudo_approve(self, sph, tmpdir):
236 """Check that sudo_approve file is rendered correctly."""
237 # Auto Approve true
238- expected_content = 'echo ${log_line} >> /var/log/sudo_pair.log'
239+ expected_content = 'logger -p auth.warn $log_line'
240 socket_dir = tmpdir.join('/var/run/sudo_pair')
241 expected_content_socket_dir = 'declare -r SUDO_SOCKET_PATH="{}"'.format(socket_dir)
242 content = sph.render_sudo_approve()

Subscribers

People subscribed via source and target branches