Skip to content
Snippets Groups Projects
Commit 2eb5dec3 authored by Timo Teräs's avatar Timo Teräs
Browse files

testing/aaudit: improve email sending, basic cron job skeleton

parent a64571d4
No related merge requests found
# Contributor: Timo Teräs <timo.teras@iki.fi>
# Maintainer: Timo Teräs <timo.teras@iki.fi>
pkgname=aaudit
pkgver=0.5
pkgver=0.6
pkgrel=0
pkgdesc="Alpine Auditor"
url="http://alpinelinux.org"
arch="noarch"
license="GPL"
depends="lua5.2 lua5.2-posix lua5.2-cjson lua5.2-pc"
depends="lua5.2 lua5.2-posix lua5.2-cjson lua5.2-pc lua5.2-socket"
makedepends=""
install=""
subpackages="$pkgname-server"
......@@ -17,6 +17,7 @@ source="aaudit-common.lua
aaudit
aaudit-shell
aaudit-update-keys
aaudit-repo
aaudit.json
aaudit-server.json
"
......@@ -44,28 +45,31 @@ server() {
"$subpkgdir"/usr/share/lua/$_luaver/aaudit
cp aaudit-server.json "$subpkgdir"/etc/aaudit
cp aaudit-server.lua "$subpkgdir"/usr/share/lua/$_luaver/aaudit/server.lua
cp aaudit-update-keys "$subpkgdir"/usr/bin
cp aaudit-update-keys aaudit-repo "$subpkgdir"/usr/bin
cp aaudit-shell "$subpkgdir"/usr/libexec/aaudit
}
md5sums="b81b0707b297a69dacbc1606324de029 aaudit-common.lua
2dfb114024f0f7aa46400a6c81d1d9cc aaudit-server.lua
9b48cc51f9f16989af4290107b2b866d aaudit
aaca139a4d476cadd1e7f8516caafec4 aaudit-server.lua
028ecf2a733387560b15563de1777c68 aaudit
f5de73f12b6df1a751c89f19c92871be aaudit-shell
feef077f56f40002ca11846512d347af aaudit-update-keys
e14ded329626ca1d6dd48e5bef0bc7e2 aaudit.json
f2c15c547701ec71aa4d0c1d0f7b70b1 aaudit-server.json"
9c88f34f561a2d0955b07de467b18a70 aaudit-repo
8a80554c91d9fca8acb82f023de02f11 aaudit.json
f0e97a7a07a472278298b5999a397f58 aaudit-server.json"
sha256sums="ee1998e730356c2de0ff9d5e27d9e0277e3c1f051777146b7c5b820437edfd7f aaudit-common.lua
55f70bb0eb60b33580a6f68f57f0a8b52fef4f2bf4c4851af7a9b96c60f4841a aaudit-server.lua
160a4e2893dfb264a7844d911d7c2781dda4624f2357f3e7950338f8d2ca9c2a aaudit
fb4ae6432f0682fccd6926e0520037cf40bc41e8781c3dfe6e70101926c5588d aaudit-server.lua
91b5247e856b6531796a0ac61c3c82a37880fbabc1afc9bf4793667f03fb3ea0 aaudit
659c755cfca95a76da78f4d28d0ab9a32d55bea0077be7420ceaf9d45c518354 aaudit-shell
660dcf86f02a9d0e3ff47cb359e0291a0921d03215e368552a2878d2d691a9cc aaudit-update-keys
f61efebc04756c8bfb7cb955b7af5db6a3c5dabdd005f690db812c7e77567cf5 aaudit.json
d477ca7714ff43818f8deef3f60d69d118ea899c57b6109cdf4c6c22009cc07c aaudit-server.json"
83868f17e1162e2b621eb2115a36f989c300aeda7cadf82ec1c991ee19d25664 aaudit-repo
ca3d163bab055381827226140568f3bef7eaac187cebd76878e0b63e9e442356 aaudit.json
ab276c9caeaa238c8999c79cd3b016f4ba149e6793475c5cde088597cedd849c aaudit-server.json"
sha512sums="aaa378fd710d17cb3663954648e97dd5128406cc6f37e9834075046aed1912dcc9e448b6c96502350b8d3496e00b7803cae671a4be2c12c584a84dc0b6e843e9 aaudit-common.lua
72fb60c37a54b34feff48d1658ecc341937c9fb9148dafd71e19347c6e1cd2daf4f59dfb443490b14f82f543847a56b04c877eb545dc9ee6b4af310a94e1008d aaudit-server.lua
1c9a78d1d2557353982ae5edbad7fefb2983ba06ff139352d2106dbba00f675cc50050ad5dd7a62153b8c8c8a6ec953872cb5c0289fa999656ade8f8004a3f5e aaudit
a78e3600fbac793040f3ece9171a47e8e624d26860b1f68bfca8812a07592c7c8f70e2806184a2359f81f5fb43d67520eb2de3f3f2145804dd1a8ee1266e1a45 aaudit-server.lua
e12b1623506382e04307f1c7fc361b544b4fbe992d41fcbaa7efd4c8568060fa7fad17e4db7a4ae96ebadc2b95f3c545809b948460a5446608bac6a35d3c35f3 aaudit
18499771d7d425f9305209c562eb8e62ef41910e88b08219baf010cdb472d49087080feb67384c4826c53bddcc0ce92c0c23c78df22dc40c64f1b17bf0ad05ec aaudit-shell
aec728a9a1e4c92baeb94a9d95e1785ea166652a157571fe2e848e71c1246635ecb99512e92435e1314c620b1fa8e4f37400350bed78bd375db4a63828c500f0 aaudit-update-keys
e769f0f77fe54ba1ab35efc80cc6426e34a2ee1d053ac9e7cc5aa316cfcef0c9658d2f0e2c47f7ae282bb9cc07107065fcc13034b2f9125c182378b7c73b7d99 aaudit.json
69f8da6b6057dfb5ab0f36f8b27db3a275cd24e4d45a0e95cf4a79d955c0ebfc88861a0a50d5b6da542587cbe8feca1842c966026f0bf445edea316202d49ee5 aaudit-server.json"
7507dea2b8ec4054e507aa3b42818863b9737402f3a9f967e16a63dd3ea12385463c2bce178a819a5f1fef76e74887a136de665e0b54172f1c1ce5f61b73403f aaudit-repo
ca4b6defb8adcc010050bc8b1bb8f8092c4928b8a0fba32146abcfb256e4d91672f88ca2cdf6210e754e5b8ac5e23fb023806ccd749ac8b701f79a691f03c87a aaudit.json
52da35598b8638a34d5a6352b2ccfd046dc529e0e5e6b541d3111016cbe6b091ff3fb4175e98b4f39e226ca1e6c973b9aa9a7a74fcb49b41862bbe64979d9186 aaudit-server.json"
......@@ -9,17 +9,15 @@ local function usage()
print([[
Usage: aaudit [create|commit] [OPTIONS...]
Valid options for create:
Options for create:
-s SERV Use server SERV
-d DESC Description for repository (default: hostname)
-t ADDR Specify ADDR as target device (default: local source IP)
-m MSG Specify message for the initial commit
-g GRP Add in group GRP (can be specified multiple times)
Valid options for commit:
Options for create and commit:
-m MSG Specify message for the commit
-r RT Related to ticket RT
-c RT Closes ticket RT
-L Local change (use local 'contact' as change author)
]])
os.exit(1)
end
......@@ -28,7 +26,7 @@ local verbose = false
local conf = aac.readconfig() or {}
local req = {}
for ret, optval in posix.getopt(arg, 'vs:d:t:m:g:r:c:') do
for ret, optval in posix.getopt(arg, 'vs:d:t:m:Lg:') do
if ret == 'v' then
verbose = true
elseif ret == 's' then
......@@ -39,14 +37,11 @@ for ret, optval in posix.getopt(arg, 'vs:d:t:m:g:r:c:') do
conf.target_address = optval
elseif ret == 'm' then
req.message = optval
elseif ret == 'L' then
req.local_change = true
elseif ret == 'g' then
req.groups = req.groups or {}
table.insert(req.groups, optval)
elseif ret == 'r' then
req.ticket = optval
elseif ret == 'c' then
req.ticket = optval
req.ticket_action = "close"
else
usage()
end
......
#!/usr/bin/lua5.2
local posix = require 'posix'
local aac = require 'aaudit.common'
local aas = require 'aaudit.server'
local pullafter = aas.serverconfig["pull-after"] or 24*60*60
local warnafter = aas.serverconfig["warn-after"] or 4*24*60*60
local function dorepo(repodir)
-- Check if it's time to update
local repoconf = aas.loadrepoconfig(repodir)
local stampfile = ("%s/lastcheck"):format(repodir)
local mtime = posix.stat(stampfile, "mtime") or 0
if os.time() > mtime + pullafter then
-- Pull for changes
local req = {
command = "commit",
target_address = repoconf.address,
message = "Unexpected configuration change",
local_change = true,
}
local ret, msg = aas.handle(req)
print(("Updating repository %s -> %s: %s"):format(repodir, repoconf.address, msg))
mtime = posix.stat(stampfile, "mtime") or 0
end
return mtime, repoconf.address
end
local home = os.getenv("HOME")
local outdated = {"List of unreachable monitored hosts:"}
for _, repodir in ipairs(posix.glob(("%s/*.git"):format(home))) do
local mtime, address = dorepo(repodir)
if os.time() > mtime + warnafter then
table.insert(outdated, address)
end
end
if #outdated > 1 and aas.serverconfig["notify-unreachables"] then
aas.sendemail {
to = aas.serverconfig["notify-unreachables"],
subject = "aaudit report of unreachable hosts",
message = table.concat(outdated, "\n"),
}
end
{
"smtp_server": "localhost",
"rtqueue": "rtqueue",
"identities": {
"_default": "Alpine Auditor <auditor@alpine.local>"
},
......
......@@ -4,9 +4,12 @@ local posix = require 'posix'
local json = require 'cjson'
local zlib = require 'zlib'
local aac = require 'aaudit.common'
local smtp = require 'socket.smtp'
local HOME = os.getenv("HOME")
M.serverconfig = aac.readconfig(("%s/aaudit-server.json"):format(HOME)) or {}
local function merge_bool(a, b) return a or b end
local function merge_array(a, b) if b then for i=1,#b do a[#a+1] = b[i] end end return a end
......@@ -70,6 +73,85 @@ local function read_header_block(block)
return header
end
local function rfc822_address(id)
local identities = M.serverconfig.identities
if id == nil then id = "_default" end
if identities and identities[id] then id = identities[id] end
local name, email = id:match("^(.-) *(<.*>)$")
if not email then return ("<%s>"):format(id) end
return ("%s %s"):format(name, email)
end
local function rfc822_email(rfc822)
return rfc822:match("(<.*>)$")
end
function M.sendemail(mail)
local to = {}
local m = {
headers = {
["Content-Type"] = 'text/plain; charset=utf8',
["X-RT-Command"] = mail.rtheader,
from = rfc822_address(mail.from),
subject = mail.subject,
},
body = mail.message,
}
local rcpt = {}
for _, addr in ipairs(mail.to) do
local rfc822 = rfc822_address(addr)
table.insert(to, rfc822)
table.insert(rcpt, rfc822_email(rfc822))
end
m.headers.to = table.concat(to, ", ")
return smtp.send{
from = rfc822_email(m.headers.from),
rcpt = rcpt,
source = smtp.message(m)
}
end
local rt_keywords = {
fix = true,
fixes = true,
close = true,
closes = true,
ref = false,
refs = false,
rt = false,
}
local function sendcommitdiff(body, req, R, G)
if not body then return end
if not G.notify_emails then return end
local mail = {
from = req.committer,
to = G.notify_emails,
subject = ("config change - %s (%s)"):format(R.description, R.address),
message = table.concat(body, '\n')
}
-- Set Request Tracker headers if relevant
local rtqueue = M.serverconfig.rtqueue
if rtqueue then
for k,no in req.message:gmatch("(%a+) #(%d+)") do
local action = rt_keywords[k]
if action ~= nil then
mail.subject = ("[%s #%s] %s"):format(rtqueue, no, mail.subject)
if action == true then
mail.rtheader = "Status: resolved"
end
break
end
end
end
-- Send email
return M.sendemail(mail)
end
local function import_tar(TAR, GIT, req, G)
local branch_ref = "refs/heads/import"
local from_ref = "refs/heads/master"
......@@ -107,6 +189,17 @@ local function import_tar(TAR, GIT, req, G)
end
end
if header.name == "etc/aaudit/aaudit.json" then
local success, res = pcall(json.decode, file_data)
if success and res.contact then
local contact = res.contact
G.notify_emails = merge_array(G.notify_emails, {contact})
if req.local_change then
req.author = rfc822_address(res.contact)
end
end
end
if header.typeflag:match("^[0-46]$") and
not match_file(header.name, G.no_track) then
GIT:write('blob\n', 'mark :', nextmark, '\n')
......@@ -140,9 +233,9 @@ data <<END_OF_COMMIT_MESSAGE
END_OF_COMMIT_MESSAGE
]]):format(branch_ref,
req.author.rfc822, author_time,
req.author.rfc822, os.time(),
req.message or "Changes"))
req.author, author_time,
req.committer, os.time(),
req.message))
if not req.initial then GIT:write(("from %s^0\n"):format(from_ref)) end
GIT:write("deleteall\n")
......@@ -191,70 +284,31 @@ local function generate_diff(repodir, commit, G)
return has_changes, text
end
local function resolve_email(identities, id)
if identities and identities[id] then id = identities[id] end
local name, email = id:match("^(.-) *<(.*)>$")
if email then return {name=name, email=email, rfc822=("%s <%s>"):format(name, email) } end
return {name="", email=name, rfc822=("<%s>"):format(name)}
end
local function send_email(body, req, S, R, G)
if not body then return end
if not G.notify_emails then return end
local to_rfc822 = {}
local to_email = {}
for _,r in ipairs(G.notify_emails) do
local id = resolve_email(S.identities, r)
if not to_email[id.email] then
to_email[id.email] = true
table.insert(to_rfc822, id.rfc822)
table.insert(to_email, id.email)
end
end
to_rfc822 = table.concat(to_rfc822, ", ")
to_email = table.concat(to_email, " ")
-- Add busybox sendmail smtp server option
local options=""
if S.smtp_server then options = ('-S "%s"'):format(S.smtp_server) end
local EMAIL = io.popen(('sendmail -f "%s" %s %s'):format(req.author.email, options, to_email), "w")
EMAIL:write(([[
From: %s
To: %s
Subject: apkovl changed - %s (%s)
Date: %s
]]):format(req.author.rfc822, to_rfc822, R.description, R.address, os.date("%a, %d %b %Y %H:%M:%S")))
for _, l in ipairs(body) do EMAIL:write(l,'\n') end
EMAIL:close()
return to_email
function M.loadrepoconfig(repohome)
return aac.readconfig(("%s/aaudit-repo.json"):format(repohome))
end
local function load_repo_configs(repohome)
local S = aac.readconfig(("%s/aaudit-server.json"):format(HOME))
local R = aac.readconfig(("%s/aaudit-repo.json"):format(repohome))
local R = M.loadrepoconfig(repohome)
-- merge global and per-repository group configs
local G = (S.groups or {}).all or {}
local G = (M.serverconfig.groups or {}).all or {}
for _, name in pairs(R.groups or {}) do
local g = S.groups[name] or {}
local g = M.serverconfig.groups[name] or {}
G.notify_emails = merge_array(G.notify_emails, g.notify_emails)
G.track_filemode = merge_bool(G.track_filemode, g.track_filemode)
G.no_track = merge_array(G.no_track, g.no_track)
G.no_notify = merge_array(G.no_notify, g.no_notify)
G.no_diff = merge_array(G.no_diff, g.no_diff)
end
return S, R, G
return R, G
end
function M.repo_update(req,clientstream)
local repodir = req.repositorydir
local S, R, G = load_repo_configs(repodir)
local R, G = load_repo_configs(repodir)
req.author = resolve_email(S.identities, req.identity)
req.committer = rfc822_address(req.identity)
req.author = req.committer
local TAR
if req.apkovl_follows then
......@@ -269,16 +323,26 @@ function M.repo_update(req,clientstream)
TAR:close()
if not rc then return rc, err end
local stampfile = ("%s/lastcheck"):format(repodir)
if posix.utime(stampfile) ~= 0 then
posix.close(posix.open(stampfile, posix.O_CREAT, "0644"))
end
local has_changes, email_body = generate_diff(repodir, "import", G)
if has_changes then
if not req.initial then
local res, err = sendcommitdiff(email_body, req, R, G)
if not res then
os.execute(("git --git-dir='%s' branch --quiet -D import;"..
"git --git-dir='%s' gc --quiet --prune=now")
:format(repodir, repodir))
return false, err
end
end
os.execute(("git --git-dir='%s' branch --quiet --force master import;"..
"git --git-dir='%s' branch --quiet -D import")
:format(repodir, repodir))
local to = nil
if not req.initial then
to = send_email(email_body, req, S, R, G)
end
return true, "Committed", {notified=to}
return true, "Committed"
end
os.execute(("git --git-dir='%s' branch --quiet -D import;"..
......@@ -322,6 +386,7 @@ function M.handle(req,clientstream)
req.command = "commit"
end
if req.command == "commit" then
req.message = req.message or "Configuration change"
if not posix.access(req.repositorydir, "rwx") then
return false, "No such repository"
end
......
{
"user": "aaudit",
"server": "aaudit.alpine.local",
"rtqueue": "alpine.org"
}
{}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment