~chris-gondolin/charms/trusty/samhain/trunk

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)