1
by Chris Stratford
[chriss] First pass at a samhain charm |
1 |
#!/usr/bin/python
|
9
by Chris Stratford
[chriss] Added tests, icon. Minor fixes |
2 |
#
|
3 |
# Copyright 2014 Canonical Ltd. All rights reserved
|
|
4 |
# Author: Chris Stratford <chris.stratford@canonical.com>
|
|
1
by Chris Stratford
[chriss] First pass at a samhain charm |
5 |
|
6 |
import os |
|
7 |
import sys |
|
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
8 |
import subprocess |
9 |
import shutil |
|
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
10 |
import tempfile |
1
by Chris Stratford
[chriss] First pass at a samhain charm |
11 |
|
12 |
sys.path.insert(0, os.path.join(os.environ['CHARM_DIR'], 'lib')) |
|
13 |
||
14 |
from charmhelpers.core import hookenv, host |
|
15 |
from charmhelpers.fetch import apt_install, apt_update |
|
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
16 |
from charmhelpers.contrib.charmsupport.nrpe import NRPE |
17 |
||
1
by Chris Stratford
[chriss] First pass at a samhain charm |
18 |
from Cheetah.Template import Template |
19 |
from Config import Config |
|
20 |
||
21 |
hooks = hookenv.Hooks() |
|
22 |
log = hookenv.log |
|
23 |
conf = Config() |
|
24 |
||
25 |
SERVICE = 'samhain' |
|
26 |
||
27 |
requiredPkgs = [ |
|
28 |
"samhain", |
|
29 |
]
|
|
30 |
||
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
31 |
scriptsToCopy = [ |
32 |
"check-safe-for-update.sh", |
|
33 |
"samhain-summary.py", |
|
34 |
]
|
|
1
by Chris Stratford
[chriss] First pass at a samhain charm |
35 |
|
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
36 |
APT_CONF = "/etc/apt/apt.conf.d/91samhain" |
37 |
||
38 |
# This should list any files affected by the charm that may trigger an alert
|
|
39 |
samhainFiles = [ |
|
40 |
"/etc", |
|
41 |
"/etc/samhain", |
|
42 |
"/etc/samhain/samhainrc", |
|
43 |
"/etc/apt/apt.conf.d", |
|
44 |
APT_CONF, |
|
45 |
"/etc/cron.d", |
|
46 |
"/etc/cron.d/count-samhain-violations", |
|
47 |
]
|
|
48 |
||
1
by Chris Stratford
[chriss] First pass at a samhain charm |
49 |
def jujuHeader(): |
50 |
header = ("#-------------------------------------------------#\n" |
|
51 |
"# This file is Juju managed - do not edit by hand #\n" |
|
52 |
"#-------------------------------------------------#\n") |
|
53 |
return header |
|
54 |
||
55 |
||
56 |
def fileFromTemplate(tmpl, dest, searchList): |
|
57 |
template_file = os.path.join(hookenv.charm_dir(), "templates", tmpl) |
|
58 |
t = Template(file=template_file, searchList=searchList) |
|
59 |
with open(dest, "w") as f: |
|
60 |
f.write(jujuHeader()) |
|
61 |
f.write(str(t)) |
|
62 |
os.chmod(dest, 0444) |
|
63 |
||
64 |
||
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
65 |
def copyScripts(): |
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
66 |
if not os.path.exists(conf.scriptDir()): |
67 |
host.mkdir(conf.scriptDir()) |
|
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
68 |
for script in scriptsToCopy: |
69 |
src = os.path.join(hookenv.charm_dir(), "files", script) |
|
70 |
dest = os.path.join(conf.scriptDir(), script) |
|
71 |
shutil.copyfile(src, dest) |
|
72 |
os.chmod(dest, 0755) |
|
73 |
||
74 |
||
7
by Chris Stratford
[chriss] Add logrotate - needs config options adding eventually |
75 |
def updateLogrotate(): |
76 |
relation_data = {} |
|
77 |
relation_data["logfiles"] = '{"samhain":{"path":"/var/log/samhain/samhain.log","when":"daily"}}' |
|
9
by Chris Stratford
[chriss] Added tests, icon. Minor fixes |
78 |
if hookenv.relation_id(): |
13
by Chris Stratford
[chriss] relation_set fix |
79 |
hookenv.relation_set(None, relation_data) |
7
by Chris Stratford
[chriss] Add logrotate - needs config options adding eventually |
80 |
else: |
9
by Chris Stratford
[chriss] Added tests, icon. Minor fixes |
81 |
for r in hookenv.relation_ids("logrotate"): |
82 |
hookenv.relation_set(r, relation_data) |
|
7
by Chris Stratford
[chriss] Add logrotate - needs config options adding eventually |
83 |
|
84 |
||
1
by Chris Stratford
[chriss] First pass at a samhain charm |
85 |
def installPackages(): |
86 |
apt_update() |
|
87 |
apt_install(requiredPkgs, options=['--force-yes']) |
|
88 |
||
89 |
||
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
90 |
# Note: We can't enable the check on install, as that prevents other
|
91 |
# charms installed alongside it from installing packages (any config
|
|
92 |
# file changes would make the system unclean), so this has to be set
|
|
93 |
# manually after the system is up and running.
|
|
94 |
# Note also, there is no option to disable it. This is intentional.
|
|
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
95 |
def updateAptConf(): |
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
96 |
if conf.enableAptCheck() == "yes": |
97 |
src = os.path.join(hookenv.charm_dir(), "files", "apt.conf") |
|
98 |
shutil.copyfile(src, APT_CONF) |
|
99 |
os.chmod(APT_CONF, 0644) |
|
100 |
updateSamhain("partial") |
|
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
101 |
|
1
by Chris Stratford
[chriss] First pass at a samhain charm |
102 |
def writeConfig(): |
103 |
tmpl_data = {} |
|
104 |
tmpl_data["attributesDirs"] = conf.attributesDirs() |
|
105 |
tmpl_data["attributesFiles"] = conf.attributesFiles() |
|
106 |
tmpl_data["eventSeverity"] = conf.eventSeverity() |
|
107 |
tmpl_data["growingLogfiles"] = conf.growingLogfiles() |
|
14
by Chris Stratford
[chriss] Added ignore_added, ignore_missing and ignore_modified options, mainly to silence messages about /etc/samba/dhcp.conf coming and going |
108 |
tmpl_data["ignoreAdded"] = conf.ignoreAdded() |
109 |
tmpl_data["ignoreMissing"] = conf.ignoreMissing() |
|
110 |
tmpl_data["ignoreModified"] = conf.ignoreModified() |
|
1
by Chris Stratford
[chriss] First pass at a samhain charm |
111 |
tmpl_data["ignoreAllDirs"] = conf.ignoreAllDirs() |
112 |
tmpl_data["ignoreAllFiles"] = conf.ignoreAllFiles() |
|
113 |
tmpl_data["ignoreNoneDirs"] = conf.ignoreNoneDirs() |
|
114 |
tmpl_data["ignoreNoneFiles"] = conf.ignoreNoneFiles() |
|
115 |
tmpl_data["logging"] = conf.logging() |
|
116 |
tmpl_data["logfiles"] = conf.logfiles() |
|
117 |
tmpl_data["mailRecipient"] = conf.mailRecipient() |
|
118 |
tmpl_data["prelinkDirs"] = conf.prelinkDirs() |
|
119 |
tmpl_data["prelinkFiles"] = conf.prelinkFiles() |
|
120 |
tmpl_data["readOnlyDirs"] = conf.readOnlyDirs() |
|
121 |
tmpl_data["readOnlyFiles"] = conf.readOnlyFiles() |
|
122 |
||
123 |
config_path = "/etc/samhain/samhainrc" |
|
124 |
fileFromTemplate("samhainrc.tmpl", config_path, tmpl_data) |
|
125 |
os.chmod(config_path, 0444) |
|
126 |
||
127 |
||
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
128 |
# The samhain init scripts don't seem to work well
|
129 |
# so we'll control it directly
|
|
130 |
def controlSamhain(cmd): |
|
5
by Chris Stratford
[chriss] Fix incorrect list |
131 |
log("CHARM: samhain {}".format(cmd)) |
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
132 |
validCmds = ["start", "stop", "restart", "reload", "status"] |
133 |
if cmd not in validCmds: |
|
134 |
log("CHARM: {} is not a valid samhain option".format(cmd)) |
|
135 |
sys.exit(1) |
|
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
136 |
|
9
by Chris Stratford
[chriss] Added tests, icon. Minor fixes |
137 |
# Restart doesn't apear to work
|
138 |
if cmd == "restart": |
|
139 |
controlSamhain("stop") |
|
140 |
controlSamhain("start") |
|
141 |
||
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
142 |
# If it's not started when we call reload, start it instead
|
143 |
if cmd == "reload": |
|
5
by Chris Stratford
[chriss] Fix incorrect list |
144 |
if subprocess.call(["samhain", "status"]) != 0: |
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
145 |
cmd = "start" |
146 |
||
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
147 |
command = ["samhain", cmd] |
148 |
if subprocess.call(command, shell=False) != 0: |
|
149 |
log("CHARM: Error running samhain {} command".format(cmd)) |
|
150 |
sys.exit(1) |
|
151 |
||
152 |
||
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
153 |
# Update samhain's DB
|
154 |
def updateSamhain(type="full"): |
|
155 |
command = ['samhain', '-t', 'update', '--foreground', '-m', 'none', '-l', 'none', '-s', 'none'] |
|
156 |
||
157 |
with tempfile.NamedTemporaryFile() as f: |
|
158 |
for filename in samhainFiles: |
|
159 |
f.write(filename + "\n") |
|
160 |
f.flush() |
|
161 |
if type == "partial": |
|
162 |
command += ["--listfile={}".format(f.name)] |
|
163 |
if subprocess.call(command, shell=False) != 0: |
|
164 |
log("CHARM: Error updating samhain") |
|
165 |
sys.exit(1) |
|
166 |
||
167 |
def configureNrpe(): |
|
168 |
checkScript = "samhain-policy-violations.py" |
|
169 |
nrpePath = "/usr/local/lib/nagios/plugins" |
|
170 |
||
171 |
with open("/etc/cron.d/count-samhain-violations", "w") as f: |
|
172 |
f.write("*/{} * * * * root /usr/local/sbin/check-safe-for-update.sh > /var/lib/nagios/samhain-policy-violations.txt\n".format(conf.nagiosCheckFrequency())) |
|
173 |
||
174 |
src = os.path.join(hookenv.charm_dir(), "files", checkScript) |
|
175 |
shutil.copyfile(src, os.path.join(nrpePath, checkScript)) |
|
176 |
os.chmod(os.path.join(nrpePath, checkScript), 0755) |
|
177 |
||
178 |
nrpe_compat = NRPE() |
|
179 |
nrpe_compat.add_check( |
|
180 |
shortname="samhain-violations", |
|
181 |
description="Check for Samhain policy violations", |
|
182 |
check_cmd="{} -w {} -c {}".format( |
|
183 |
os.path.join(nrpePath, checkScript), |
|
184 |
conf.nagiosWarnLevel(), |
|
185 |
conf.nagiosCritLevel()) |
|
186 |
)
|
|
187 |
nrpe_compat.write() |
|
188 |
||
189 |
||
7
by Chris Stratford
[chriss] Add logrotate - needs config options adding eventually |
190 |
@hooks.hook("logrotate-relation-changed") |
191 |
def logrotateRelationChanged(): |
|
192 |
log("CHARM: Changing logrotate relation for {}".format(conf.appName())) |
|
193 |
updateLogrotate() |
|
194 |
||
195 |
||
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
196 |
@hooks.hook("nrpe-external-master-relation-changed") |
197 |
def nrpeExternalMasterRelationChanged(): |
|
8
by Chris Stratford
[chriss] Restart samhain on change instead of reload |
198 |
log("CHARM: Changing nrpe-external-master relation for {}".format(conf.appName())) |
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
199 |
configureNrpe() |
200 |
||
201 |
||
1
by Chris Stratford
[chriss] First pass at a samhain charm |
202 |
@hooks.hook('install') |
203 |
def install(): |
|
8
by Chris Stratford
[chriss] Restart samhain on change instead of reload |
204 |
log("CHARM: Installing {}".format(conf.appName())) |
1
by Chris Stratford
[chriss] First pass at a samhain charm |
205 |
installPackages() |
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
206 |
copyScripts() |
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
207 |
updateSamhain() |
1
by Chris Stratford
[chriss] First pass at a samhain charm |
208 |
|
6
by Chris Stratford
[chriss] Minor fixes |
209 |
# Fix samhain log permissions to be world-readable
|
210 |
# (otherwise logstash can't get to them)
|
|
211 |
if not os.path.exists("/var/log/samhain"): |
|
212 |
mkdir("/var/log/samhain") |
|
213 |
if not os.path.exists("/var/log/samhain/samhain.log"): |
|
214 |
with open("/var/log/samhain/samhain.log", "w") as f: |
|
215 |
os.chmod("/var/log/samhain/samhain.log", 0644) |
|
216 |
||
1
by Chris Stratford
[chriss] First pass at a samhain charm |
217 |
|
218 |
@hooks.hook('config-changed') |
|
219 |
def config_changed(): |
|
220 |
config = hookenv.config() |
|
221 |
||
222 |
for key in config: |
|
223 |
if config.changed(key): |
|
224 |
log("config['{}'] changed from {} to {}".format( |
|
225 |
key, config.previous(key), config[key])) |
|
226 |
||
227 |
config.save() |
|
228 |
writeConfig() |
|
8
by Chris Stratford
[chriss] Restart samhain on change instead of reload |
229 |
controlSamhain("restart") |
4
by Chris Stratford
[chriss] Really added the scripts this time. Added basic nagios monitoring of policy violations |
230 |
updateAptConf() |
1
by Chris Stratford
[chriss] First pass at a samhain charm |
231 |
|
232 |
||
233 |
@hooks.hook('upgrade-charm') |
|
234 |
def upgrade_charm(): |
|
8
by Chris Stratford
[chriss] Restart samhain on change instead of reload |
235 |
log("CHARM: Upgrading {}".format(conf.appName())) |
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
236 |
copyScripts() |
237 |
updateAptConf() |
|
1
by Chris Stratford
[chriss] First pass at a samhain charm |
238 |
|
239 |
@hooks.hook('start') |
|
240 |
def start(): |
|
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
241 |
controlSamhain("start") |
1
by Chris Stratford
[chriss] First pass at a samhain charm |
242 |
|
243 |
@hooks.hook('stop') |
|
244 |
def stop(): |
|
3
by Chris Stratford
[chriss] Deny apt-get update if samhain is unclean. Shut down samhain during updates (and update samhain afterwards). Added samhain-summary.py script to show/update samhain status. |
245 |
controlSamhain("stop") |
1
by Chris Stratford
[chriss] First pass at a samhain charm |
246 |
|
247 |
||
248 |
if __name__ == "__main__": |
|
249 |
# execute a hook based on the name the program is called by
|
|
250 |
hooks.execute(sys.argv) |