Merge branch 'master' into master
This commit is contained in:
@@ -117,7 +117,7 @@ RAND_USER=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 16 | head -n 1)
|
||||
RAND_PASS=$(cat /dev/urandom | tr -dc 'a-z0-9' | fold -w 24 | head -n 1)
|
||||
|
||||
echo ${RAND_USER}@mailcow.local:{SHA1}$(echo -n ${RAND_PASS} | sha1sum | awk '{print $1}') > /usr/local/etc/dovecot/dovecot-master.passwd
|
||||
|
||||
echo ${RAND_USER}@mailcow.local::5000:5000:::: > /usr/local/etc/dovecot/dovecot-master.userdb
|
||||
echo ${RAND_USER}@mailcow.local:${RAND_PASS} > /etc/sogo/sieve.creds
|
||||
|
||||
# 401 is user dovecot
|
||||
|
@@ -13,3 +13,6 @@ catch_non_zero "/usr/bin/redis-cli -h redis LTRIM DOVECOT_MAILLOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM SOGO_LOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM NETFILTER_LOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM AUTODISCOVER_LOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM API_LOG 0 LOG_LINES"
|
||||
catch_non_zero "/usr/bin/redis-cli -h redis LTRIM RL_LOG 0 LOG_LINES"
|
||||
|
||||
|
@@ -1,11 +1,11 @@
|
||||
FROM php:7.2-fpm-alpine3.8
|
||||
LABEL maintainer "Andre Peters <andre.peters@servercow.de>"
|
||||
|
||||
ENV APCU_PECL 5.1.12
|
||||
ENV APCU_PECL 5.1.16
|
||||
ENV IMAGICK_PECL 3.4.3
|
||||
ENV MAILPARSE_PECL 3.0.2
|
||||
ENV MEMCACHED_PECL 3.0.4
|
||||
ENV REDIS_PECL 4.1.1
|
||||
ENV MEMCACHED_PECL 3.1.3
|
||||
ENV REDIS_PECL 4.2.0
|
||||
|
||||
RUN apk add -U --no-cache autoconf \
|
||||
bash \
|
||||
|
@@ -23,12 +23,25 @@ fi
|
||||
# Check of mysql_upgrade
|
||||
|
||||
CONTAINER_ID=
|
||||
# Todo: Better check if upgrade failed
|
||||
# This can happen due to a broken sogo_view
|
||||
[ -s /mysql_upgrade_loop ] && SQL_LOOP_C=$(cat /mysql_upgrade_loop)
|
||||
CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"mysql-mailcow\")) | .id")
|
||||
if [[ ! -z ${CONTAINER_ID} ]]; then
|
||||
if [[ ! -z "${CONTAINER_ID}" ]] && [[ "${CONTAINER_ID}" =~ [^a-zA-Z0-9] ]]; then
|
||||
SQL_UPGRADE_RETURN=$(curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/exec -d '{"cmd":"system", "task":"mysql_upgrade"}' --silent -H 'Content-type: application/json' | jq -r .type)
|
||||
if [[ ${SQL_UPGRADE_RETURN} == 'warning' ]]; then
|
||||
echo "MySQL applied an upgrade, restarting PHP-FPM..."
|
||||
exit 1
|
||||
if [ -z ${SQL_LOOP_C} ]; then
|
||||
echo 1 > /mysql_upgrade_loop
|
||||
echo "MySQL applied an upgrade, restarting PHP-FPM..."
|
||||
exit 1
|
||||
else
|
||||
rm /mysql_upgrade_loop
|
||||
echo "MySQL was not applied previously, skipping. Restart php-fpm-mailcow to retry or run mysql_upgrade manually."
|
||||
while ! mysqladmin status --socket=/var/run/mysqld/mysqld.sock -u${DBUSER} -p${DBPASS} --silent; do
|
||||
echo "Waiting for SQL to return..."
|
||||
sleep 2
|
||||
done
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@@ -85,7 +85,17 @@ query = SELECT GROUP_CONCAT(transport SEPARATOR '') AS transport_maps
|
||||
AS transport_view;
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps.cf
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_transport_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = unix:/var/run/mysqld/mysqld.sock
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT CONCAT('smtp_via_transport_maps:', nexthop) AS transport FROM transports
|
||||
WHERE active = '1'
|
||||
AND destination = '%s';
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps_sender_dependent.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = unix:/var/run/mysqld/mysqld.sock
|
||||
@@ -98,9 +108,22 @@ query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM relayhosts
|
||||
SELECT CONCAT('@', alias_domain) FROM alias_domain
|
||||
)
|
||||
)
|
||||
AND active = '1'
|
||||
AND username != '';
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_sasl_passwd_maps_transport_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
hosts = unix:/var/run/mysqld/mysqld.sock
|
||||
dbname = ${DBNAME}
|
||||
query = SELECT CONCAT_WS(':', username, password) AS auth_data FROM transports
|
||||
WHERE nexthop = '%s'
|
||||
AND active = '1'
|
||||
AND username != ''
|
||||
LIMIT 1;
|
||||
EOF
|
||||
|
||||
cat <<EOF > /opt/postfix/conf/sql/mysql_virtual_alias_domain_catchall_maps.cf
|
||||
user = ${DBUSER}
|
||||
password = ${DBPASS}
|
||||
|
@@ -21,6 +21,7 @@ RUN apt-get update && apt-get install -y \
|
||||
|
||||
COPY settings.conf /etc/rspamd/settings.conf
|
||||
COPY docker-entrypoint.sh /docker-entrypoint.sh
|
||||
COPY ratelimit.lua /usr/share/rspamd/lua/ratelimit.lua
|
||||
|
||||
ENTRYPOINT ["/docker-entrypoint.sh"]
|
||||
|
||||
|
864
data/Dockerfiles/rspamd/ratelimit.lua
Normal file
864
data/Dockerfiles/rspamd/ratelimit.lua
Normal file
@@ -0,0 +1,864 @@
|
||||
--[[
|
||||
Copyright (c) 2011-2017, Vsevolod Stakhov <vsevolod@highsecure.ru>
|
||||
Copyright (c) 2016-2017, Andrew Lewis <nerf@judo.za.org>
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
]]--
|
||||
|
||||
if confighelp then
|
||||
return
|
||||
end
|
||||
|
||||
local rspamd_logger = require "rspamd_logger"
|
||||
local rspamd_util = require "rspamd_util"
|
||||
local rspamd_lua_utils = require "lua_util"
|
||||
local lua_redis = require "lua_redis"
|
||||
local fun = require "fun"
|
||||
local lua_maps = require "lua_maps"
|
||||
local lua_util = require "lua_util"
|
||||
local rspamd_hash = require "rspamd_cryptobox_hash"
|
||||
local lua_selectors = require "lua_selectors"
|
||||
local ts = require("tableshape").types
|
||||
|
||||
-- A plugin that implements ratelimits using redis
|
||||
|
||||
local E = {}
|
||||
local N = 'ratelimit'
|
||||
local redis_params
|
||||
-- Senders that are considered as bounce
|
||||
local settings = {
|
||||
bounce_senders = { 'postmaster', 'mailer-daemon', '', 'null', 'fetchmail-daemon', 'mdaemon' },
|
||||
-- Do not check ratelimits for these recipients
|
||||
whitelisted_rcpts = { 'postmaster', 'mailer-daemon' },
|
||||
prefix = 'RL',
|
||||
ham_factor_rate = 1.01,
|
||||
spam_factor_rate = 0.99,
|
||||
ham_factor_burst = 1.02,
|
||||
spam_factor_burst = 0.98,
|
||||
max_rate_mult = 5,
|
||||
max_bucket_mult = 10,
|
||||
expire = 60 * 60 * 24 * 2, -- 2 days by default
|
||||
limits = {},
|
||||
allow_local = false,
|
||||
}
|
||||
|
||||
-- Checks bucket, updating it if needed
|
||||
-- KEYS[1] - prefix to update, e.g. RL_<triplet>_<seconds>
|
||||
-- KEYS[2] - current time in milliseconds
|
||||
-- KEYS[3] - bucket leak rate (messages per millisecond)
|
||||
-- KEYS[4] - bucket burst
|
||||
-- KEYS[5] - expire for a bucket
|
||||
-- return 1 if message should be ratelimited and 0 if not
|
||||
-- Redis keys used:
|
||||
-- l - last hit
|
||||
-- b - current burst
|
||||
-- dr - current dynamic rate multiplier (*10000)
|
||||
-- db - current dynamic burst multiplier (*10000)
|
||||
local bucket_check_script = [[
|
||||
local last = redis.call('HGET', KEYS[1], 'l')
|
||||
local now = tonumber(KEYS[2])
|
||||
local dynr, dynb, leaked = 0, 0, 0
|
||||
if not last then
|
||||
-- New bucket
|
||||
redis.call('HSET', KEYS[1], 'l', KEYS[2])
|
||||
redis.call('HSET', KEYS[1], 'b', '0')
|
||||
redis.call('HSET', KEYS[1], 'dr', '10000')
|
||||
redis.call('HSET', KEYS[1], 'db', '10000')
|
||||
redis.call('EXPIRE', KEYS[1], KEYS[5])
|
||||
return {0, '0', '1', '1', '0'}
|
||||
end
|
||||
|
||||
last = tonumber(last)
|
||||
local burst = tonumber(redis.call('HGET', KEYS[1], 'b'))
|
||||
-- Perform leak
|
||||
if burst > 0 then
|
||||
if last < tonumber(KEYS[2]) then
|
||||
local rate = tonumber(KEYS[3])
|
||||
dynr = tonumber(redis.call('HGET', KEYS[1], 'dr')) / 10000.0
|
||||
if dynr == 0 then dynr = 0.0001 end
|
||||
rate = rate * dynr
|
||||
leaked = ((now - last) * rate)
|
||||
if leaked > burst then leaked = burst end
|
||||
burst = burst - leaked
|
||||
redis.call('HINCRBYFLOAT', KEYS[1], 'b', -(leaked))
|
||||
redis.call('HSET', KEYS[1], 'l', KEYS[2])
|
||||
end
|
||||
|
||||
dynb = tonumber(redis.call('HGET', KEYS[1], 'db')) / 10000.0
|
||||
if dynb == 0 then dynb = 0.0001 end
|
||||
|
||||
if burst > 0 and (burst + 1) > tonumber(KEYS[4]) * dynb then
|
||||
return {1, tostring(burst), tostring(dynr), tostring(dynb), tostring(leaked)}
|
||||
end
|
||||
else
|
||||
burst = 0
|
||||
redis.call('HSET', KEYS[1], 'b', '0')
|
||||
end
|
||||
|
||||
return {0, tostring(burst), tostring(dynr), tostring(dynb), tostring(leaked)}
|
||||
]]
|
||||
local bucket_check_id
|
||||
|
||||
|
||||
-- Updates a bucket
|
||||
-- KEYS[1] - prefix to update, e.g. RL_<triplet>_<seconds>
|
||||
-- KEYS[2] - current time in milliseconds
|
||||
-- KEYS[3] - dynamic rate multiplier
|
||||
-- KEYS[4] - dynamic burst multiplier
|
||||
-- KEYS[5] - max dyn rate (min: 1/x)
|
||||
-- KEYS[6] - max burst rate (min: 1/x)
|
||||
-- KEYS[7] - expire for a bucket
|
||||
-- Redis keys used:
|
||||
-- l - last hit
|
||||
-- b - current burst
|
||||
-- dr - current dynamic rate multiplier
|
||||
-- db - current dynamic burst multiplier
|
||||
local bucket_update_script = [[
|
||||
local last = redis.call('HGET', KEYS[1], 'l')
|
||||
local now = tonumber(KEYS[2])
|
||||
if not last then
|
||||
-- New bucket
|
||||
redis.call('HSET', KEYS[1], 'l', KEYS[2])
|
||||
redis.call('HSET', KEYS[1], 'b', '1')
|
||||
redis.call('HSET', KEYS[1], 'dr', '10000')
|
||||
redis.call('HSET', KEYS[1], 'db', '10000')
|
||||
redis.call('EXPIRE', KEYS[1], KEYS[7])
|
||||
return {1, 1, 1}
|
||||
end
|
||||
|
||||
local dr, db = 1.0, 1.0
|
||||
|
||||
if tonumber(KEYS[5]) > 1 then
|
||||
local rate_mult = tonumber(KEYS[3])
|
||||
local rate_limit = tonumber(KEYS[5])
|
||||
dr = tonumber(redis.call('HGET', KEYS[1], 'dr')) / 10000
|
||||
|
||||
if rate_mult > 1.0 and dr < rate_limit then
|
||||
dr = dr * rate_mult
|
||||
if dr > 0.0001 then
|
||||
redis.call('HSET', KEYS[1], 'dr', tostring(math.floor(dr * 10000)))
|
||||
else
|
||||
redis.call('HSET', KEYS[1], 'dr', '1')
|
||||
end
|
||||
elseif rate_mult < 1.0 and dr > (1.0 / rate_limit) then
|
||||
dr = dr * rate_mult
|
||||
if dr > 0.0001 then
|
||||
redis.call('HSET', KEYS[1], 'dr', tostring(math.floor(dr * 10000)))
|
||||
else
|
||||
redis.call('HSET', KEYS[1], 'dr', '1')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if tonumber(KEYS[6]) > 1 then
|
||||
local rate_mult = tonumber(KEYS[4])
|
||||
local rate_limit = tonumber(KEYS[6])
|
||||
db = tonumber(redis.call('HGET', KEYS[1], 'db')) / 10000
|
||||
|
||||
if rate_mult > 1.0 and db < rate_limit then
|
||||
db = db * rate_mult
|
||||
if db > 0.0001 then
|
||||
redis.call('HSET', KEYS[1], 'db', tostring(math.floor(db * 10000)))
|
||||
else
|
||||
redis.call('HSET', KEYS[1], 'db', '1')
|
||||
end
|
||||
elseif rate_mult < 1.0 and db > (1.0 / rate_limit) then
|
||||
db = db * rate_mult
|
||||
if db > 0.0001 then
|
||||
redis.call('HSET', KEYS[1], 'db', tostring(math.floor(db * 10000)))
|
||||
else
|
||||
redis.call('HSET', KEYS[1], 'db', '1')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local burst = tonumber(redis.call('HGET', KEYS[1], 'b'))
|
||||
if burst < 0 then burst = 0 end
|
||||
|
||||
redis.call('HINCRBYFLOAT', KEYS[1], 'b', 1)
|
||||
redis.call('HSET', KEYS[1], 'l', KEYS[2])
|
||||
redis.call('EXPIRE', KEYS[1], KEYS[7])
|
||||
|
||||
return {tostring(burst), tostring(dr), tostring(db)}
|
||||
]]
|
||||
local bucket_update_id
|
||||
|
||||
-- message_func(task, limit_type, prefix, bucket, limit_key)
|
||||
local message_func = function(_, limit_type, _, _, _)
|
||||
return string.format('Ratelimit "%s" exceeded', limit_type)
|
||||
end
|
||||
|
||||
|
||||
local function load_scripts(cfg, ev_base)
|
||||
bucket_check_id = lua_redis.add_redis_script(bucket_check_script, redis_params)
|
||||
bucket_update_id = lua_redis.add_redis_script(bucket_update_script, redis_params)
|
||||
end
|
||||
|
||||
local limit_parser
|
||||
local function parse_string_limit(lim, no_error)
|
||||
local function parse_time_suffix(s)
|
||||
if s == 's' then
|
||||
return 1
|
||||
elseif s == 'm' then
|
||||
return 60
|
||||
elseif s == 'h' then
|
||||
return 3600
|
||||
elseif s == 'd' then
|
||||
return 86400
|
||||
end
|
||||
end
|
||||
local function parse_num_suffix(s)
|
||||
if s == '' then
|
||||
return 1
|
||||
elseif s == 'k' then
|
||||
return 1000
|
||||
elseif s == 'm' then
|
||||
return 1000000
|
||||
elseif s == 'g' then
|
||||
return 1000000000
|
||||
end
|
||||
end
|
||||
local lpeg = require "lpeg"
|
||||
|
||||
if not limit_parser then
|
||||
local digit = lpeg.R("09")
|
||||
limit_parser = {}
|
||||
limit_parser.integer =
|
||||
(lpeg.S("+-") ^ -1) *
|
||||
(digit ^ 1)
|
||||
limit_parser.fractional =
|
||||
(lpeg.P(".") ) *
|
||||
(digit ^ 1)
|
||||
limit_parser.number =
|
||||
(limit_parser.integer *
|
||||
(limit_parser.fractional ^ -1)) +
|
||||
(lpeg.S("+-") * limit_parser.fractional)
|
||||
limit_parser.time = lpeg.Cf(lpeg.Cc(1) *
|
||||
(limit_parser.number / tonumber) *
|
||||
((lpeg.S("smhd") / parse_time_suffix) ^ -1),
|
||||
function (acc, val) return acc * val end)
|
||||
limit_parser.suffixed_number = lpeg.Cf(lpeg.Cc(1) *
|
||||
(limit_parser.number / tonumber) *
|
||||
((lpeg.S("kmg") / parse_num_suffix) ^ -1),
|
||||
function (acc, val) return acc * val end)
|
||||
limit_parser.limit = lpeg.Ct(limit_parser.suffixed_number *
|
||||
(lpeg.S(" ") ^ 0) * lpeg.S("/") * (lpeg.S(" ") ^ 0) *
|
||||
limit_parser.time)
|
||||
end
|
||||
local t = lpeg.match(limit_parser.limit, lim)
|
||||
|
||||
if t and t[1] and t[2] and t[2] ~= 0 then
|
||||
return t[2], t[1]
|
||||
end
|
||||
|
||||
if not no_error then
|
||||
rspamd_logger.errx(rspamd_config, 'bad limit: %s', lim)
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
local function str_to_rate(str)
|
||||
local divider,divisor = parse_string_limit(str, false)
|
||||
|
||||
if not divisor then
|
||||
rspamd_logger.errx(rspamd_config, 'bad rate string: %s', str)
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
return divisor / divider
|
||||
end
|
||||
|
||||
local bucket_schema = ts.shape{
|
||||
burst = ts.number + ts.string / lua_util.dehumanize_number,
|
||||
rate = ts.number + ts.string / str_to_rate
|
||||
}
|
||||
|
||||
local function parse_limit(name, data)
|
||||
if type(data) == 'table' then
|
||||
-- 2 cases here:
|
||||
-- * old limit in format [burst, rate]
|
||||
-- * vector of strings in Andrew's string format (removed from 1.8.2)
|
||||
-- * proper bucket table
|
||||
if #data == 2 and tonumber(data[1]) and tonumber(data[2]) then
|
||||
-- Old style ratelimit
|
||||
rspamd_logger.warnx(rspamd_config, 'old style ratelimit for %s', name)
|
||||
if tonumber(data[1]) > 0 and tonumber(data[2]) > 0 then
|
||||
return {
|
||||
burst = data[1],
|
||||
rate = data[2]
|
||||
}
|
||||
elseif data[1] ~= 0 then
|
||||
rspamd_logger.warnx(rspamd_config, 'invalid numbers for %s', name)
|
||||
else
|
||||
rspamd_logger.infox(rspamd_config, 'disable limit %s, burst is zero', name)
|
||||
end
|
||||
|
||||
return nil
|
||||
else
|
||||
local parsed_bucket,err = bucket_schema:transform(data)
|
||||
|
||||
if not parsed_bucket or err then
|
||||
rspamd_logger.errx(rspamd_config, 'cannot parse bucket for %s: %s; original value: %s',
|
||||
name, err, data)
|
||||
else
|
||||
return parsed_bucket
|
||||
end
|
||||
end
|
||||
elseif type(data) == 'string' then
|
||||
local rep_rate, burst = parse_string_limit(data)
|
||||
rspamd_logger.warnx(rspamd_config, 'old style rate bucket config detected for %s: %s',
|
||||
name, data)
|
||||
if rep_rate and burst then
|
||||
return {
|
||||
burst = burst,
|
||||
rate = burst / rep_rate -- reciprocal
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
--- Check whether this addr is bounce
|
||||
local function check_bounce(from)
|
||||
return fun.any(function(b) return b == from end, settings.bounce_senders)
|
||||
end
|
||||
|
||||
local keywords = {
|
||||
['ip'] = {
|
||||
['get_value'] = function(task)
|
||||
local ip = task:get_ip()
|
||||
if ip and ip:is_valid() then return tostring(ip) end
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
['rip'] = {
|
||||
['get_value'] = function(task)
|
||||
local ip = task:get_ip()
|
||||
if ip and ip:is_valid() and not ip:is_local() then return tostring(ip) end
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
['from'] = {
|
||||
['get_value'] = function(task)
|
||||
local from = task:get_from(0)
|
||||
if ((from or E)[1] or E).addr then
|
||||
return string.lower(from[1]['addr'])
|
||||
end
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
['bounce'] = {
|
||||
['get_value'] = function(task)
|
||||
local from = task:get_from(0)
|
||||
if not ((from or E)[1] or E).user then
|
||||
return '_'
|
||||
end
|
||||
if check_bounce(from[1]['user']) then return '_' else return nil end
|
||||
end,
|
||||
},
|
||||
['asn'] = {
|
||||
['get_value'] = function(task)
|
||||
local asn = task:get_mempool():get_variable('asn')
|
||||
if not asn then
|
||||
return nil
|
||||
else
|
||||
return asn
|
||||
end
|
||||
end,
|
||||
},
|
||||
['user'] = {
|
||||
['get_value'] = function(task)
|
||||
local auser = task:get_user()
|
||||
if not auser then
|
||||
return nil
|
||||
else
|
||||
return auser
|
||||
end
|
||||
end,
|
||||
},
|
||||
['to'] = {
|
||||
['get_value'] = function(task)
|
||||
return task:get_principal_recipient()
|
||||
end,
|
||||
},
|
||||
['digest'] = {
|
||||
['get_value'] = function(task)
|
||||
return task:get_digest()
|
||||
end,
|
||||
},
|
||||
['attachments'] = {
|
||||
['get_value'] = function(task)
|
||||
local parts = task:get_parts() or E
|
||||
local digests = {}
|
||||
|
||||
for _,p in ipairs(parts) do
|
||||
if p:get_filename() then
|
||||
table.insert(digests, p:get_digest())
|
||||
end
|
||||
end
|
||||
|
||||
if #digests > 0 then
|
||||
return table.concat(digests, '')
|
||||
end
|
||||
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
['files'] = {
|
||||
['get_value'] = function(task)
|
||||
local parts = task:get_parts() or E
|
||||
local files = {}
|
||||
|
||||
for _,p in ipairs(parts) do
|
||||
local fname = p:get_filename()
|
||||
if fname then
|
||||
table.insert(files, fname)
|
||||
end
|
||||
end
|
||||
|
||||
if #files > 0 then
|
||||
return table.concat(files, ':')
|
||||
end
|
||||
|
||||
return nil
|
||||
end,
|
||||
},
|
||||
}
|
||||
|
||||
local function gen_rate_key(task, rtype, bucket)
|
||||
local key_t = {tostring(lua_util.round(100000.0 / bucket.burst))}
|
||||
local key_keywords = lua_util.str_split(rtype, '_')
|
||||
local have_user = false
|
||||
|
||||
for _, v in ipairs(key_keywords) do
|
||||
local ret
|
||||
|
||||
if keywords[v] and type(keywords[v]['get_value']) == 'function' then
|
||||
ret = keywords[v]['get_value'](task)
|
||||
end
|
||||
if not ret then return nil end
|
||||
if v == 'user' then have_user = true end
|
||||
if type(ret) ~= 'string' then ret = tostring(ret) end
|
||||
table.insert(key_t, ret)
|
||||
end
|
||||
|
||||
if have_user and not task:get_user() then
|
||||
return nil
|
||||
end
|
||||
|
||||
return table.concat(key_t, ":")
|
||||
end
|
||||
|
||||
local function make_prefix(redis_key, name, bucket)
|
||||
local hash_len = 24
|
||||
if hash_len > #redis_key then hash_len = #redis_key end
|
||||
local hash = settings.prefix ..
|
||||
string.sub(rspamd_hash.create(redis_key):base32(), 1, hash_len)
|
||||
-- Fill defaults
|
||||
if not bucket.spam_factor_rate then
|
||||
bucket.spam_factor_rate = settings.spam_factor_rate
|
||||
end
|
||||
if not bucket.ham_factor_rate then
|
||||
bucket.ham_factor_rate = settings.ham_factor_rate
|
||||
end
|
||||
if not bucket.spam_factor_burst then
|
||||
bucket.spam_factor_burst = settings.spam_factor_burst
|
||||
end
|
||||
if not bucket.ham_factor_burst then
|
||||
bucket.ham_factor_burst = settings.ham_factor_burst
|
||||
end
|
||||
|
||||
return {
|
||||
bucket = bucket,
|
||||
name = name,
|
||||
hash = hash
|
||||
}
|
||||
end
|
||||
|
||||
local function limit_to_prefixes(task, k, v, prefixes)
|
||||
local n = 0
|
||||
for _,bucket in ipairs(v.buckets) do
|
||||
if v.selector then
|
||||
local selectors = lua_selectors.process_selectors(task, v.selector)
|
||||
if selectors then
|
||||
local combined = lua_selectors.combine_selectors(task, selectors, ':')
|
||||
if type(combined) == 'string' then
|
||||
prefixes[combined] = make_prefix(combined, k, bucket)
|
||||
n = n + 1
|
||||
else
|
||||
fun.each(function(p)
|
||||
prefixes[p] = make_prefix(p, k, bucket)
|
||||
n = n + 1
|
||||
end, combined)
|
||||
end
|
||||
end
|
||||
else
|
||||
local prefix = gen_rate_key(task, k, bucket)
|
||||
if prefix then
|
||||
if type(prefix) == 'string' then
|
||||
prefixes[prefix] = make_prefix(prefix, k, bucket)
|
||||
n = n + 1
|
||||
else
|
||||
fun.each(function(p)
|
||||
prefixes[p] = make_prefix(p, k, bucket)
|
||||
n = n + 1
|
||||
end, prefix)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return n
|
||||
end
|
||||
|
||||
local function ratelimit_cb(task)
|
||||
if not settings.allow_local and
|
||||
rspamd_lua_utils.is_rspamc_or_controller(task) then return end
|
||||
|
||||
-- Get initial task data
|
||||
local ip = task:get_from_ip()
|
||||
if ip and ip:is_valid() and settings.whitelisted_ip then
|
||||
if settings.whitelisted_ip:get_key(ip) then
|
||||
-- Do not check whitelisted ip
|
||||
rspamd_logger.infox(task, 'skip ratelimit for whitelisted IP')
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Parse all rcpts
|
||||
local rcpts = task:get_recipients()
|
||||
local rcpts_user = {}
|
||||
if rcpts then
|
||||
fun.each(function(r)
|
||||
fun.each(function(type) table.insert(rcpts_user, r[type]) end, {'user', 'addr'})
|
||||
end, rcpts)
|
||||
|
||||
if fun.any(function(r) return settings.whitelisted_rcpts:get_key(r) end, rcpts_user) then
|
||||
rspamd_logger.infox(task, 'skip ratelimit for whitelisted recipient')
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Get user (authuser)
|
||||
if settings.whitelisted_user then
|
||||
local auser = task:get_user()
|
||||
if settings.whitelisted_user:get_key(auser) then
|
||||
rspamd_logger.infox(task, 'skip ratelimit for whitelisted user')
|
||||
return
|
||||
end
|
||||
end
|
||||
-- Now create all ratelimit prefixes
|
||||
local prefixes = {}
|
||||
local nprefixes = 0
|
||||
|
||||
for k,v in pairs(settings.limits) do
|
||||
nprefixes = nprefixes + limit_to_prefixes(task, k, v, prefixes)
|
||||
end
|
||||
|
||||
for k, hdl in pairs(settings.custom_keywords or E) do
|
||||
local ret, redis_key, bd = pcall(hdl, task)
|
||||
|
||||
if ret then
|
||||
local bucket = parse_limit(k, bd)
|
||||
if bucket then
|
||||
prefixes[redis_key] = make_prefix(redis_key, k, bucket)
|
||||
end
|
||||
nprefixes = nprefixes + 1
|
||||
else
|
||||
rspamd_logger.errx(task, 'cannot call handler for %s: %s',
|
||||
k, redis_key)
|
||||
end
|
||||
end
|
||||
|
||||
local function gen_check_cb(prefix, bucket, lim_name, lim_key)
|
||||
return function(err, data)
|
||||
if err then
|
||||
rspamd_logger.errx('cannot check limit %s: %s %s', prefix, err, data)
|
||||
elseif type(data) == 'table' and data[1] then
|
||||
lua_util.debugm(N, task,
|
||||
"got reply for limit %s (%s / %s); %s burst, %s:%s dyn, %s leaked",
|
||||
prefix, bucket.burst, bucket.rate,
|
||||
data[2], data[3], data[4], data[5])
|
||||
|
||||
if data[1] == 1 then
|
||||
-- set symbol only and do NOT soft reject
|
||||
if settings.symbol then
|
||||
task:insert_result(settings.symbol, 0.0,
|
||||
string.format('%s(%s)', lim_name, lim_key))
|
||||
rspamd_logger.infox(task,
|
||||
'set_symbol_only: ratelimit "%s(%s)" exceeded, (%s / %s): %s (%s:%s dyn); redis key: %s',
|
||||
lim_name, prefix,
|
||||
bucket.burst, bucket.rate,
|
||||
data[2], data[3], data[4], lim_key)
|
||||
return
|
||||
-- set INFO symbol and soft reject
|
||||
elseif settings.info_symbol then
|
||||
task:insert_result(settings.info_symbol, 1.0,
|
||||
string.format('%s(%s)', lim_name, lim_key))
|
||||
end
|
||||
rspamd_logger.infox(task,
|
||||
'ratelimit "%s(%s)" exceeded, (%s / %s): %s (%s:%s dyn); redis key: %s',
|
||||
lim_name, prefix,
|
||||
bucket.burst, bucket.rate,
|
||||
data[2], data[3], data[4], lim_key)
|
||||
task:set_pre_result('soft reject',
|
||||
message_func(task, lim_name, prefix, bucket, lim_key), N)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Don't do anything if pre-result has been already set
|
||||
if task:has_pre_result() then return end
|
||||
|
||||
if nprefixes > 0 then
|
||||
-- Save prefixes to the cache to allow update
|
||||
task:cache_set('ratelimit_prefixes', prefixes)
|
||||
local now = rspamd_util.get_time()
|
||||
now = lua_util.round(now * 1000.0) -- Get milliseconds
|
||||
-- Now call check script for all defined prefixes
|
||||
|
||||
for pr,value in pairs(prefixes) do
|
||||
local bucket = value.bucket
|
||||
local rate = (bucket.rate) / 1000.0 -- Leak rate in messages/ms
|
||||
lua_util.debugm(N, task, "check limit %s:%s -> %s (%s/%s)",
|
||||
value.name, pr, value.hash, bucket.burst, bucket.rate)
|
||||
lua_redis.exec_redis_script(bucket_check_id,
|
||||
{key = value.hash, task = task, is_write = true},
|
||||
gen_check_cb(pr, bucket, value.name, value.hash),
|
||||
{value.hash, tostring(now), tostring(rate), tostring(bucket.burst),
|
||||
tostring(settings.expire)})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function ratelimit_update_cb(task)
|
||||
if task:has_flag('skip') then return end
|
||||
if not settings.allow_local and lua_util.is_rspamc_or_controller(task) then return end
|
||||
local prefixes = task:cache_get('ratelimit_prefixes')
|
||||
|
||||
if prefixes then
|
||||
if task:has_pre_result() then
|
||||
-- Already rate limited/greylisted, do nothing
|
||||
lua_util.debugm(N, task, 'pre-action has been set, do not update')
|
||||
return
|
||||
end
|
||||
|
||||
local verdict = lua_util.get_task_verdict(task)
|
||||
|
||||
-- Update each bucket
|
||||
for k, v in pairs(prefixes) do
|
||||
local bucket = v.bucket
|
||||
local function update_bucket_cb(err, data)
|
||||
if err then
|
||||
rspamd_logger.errx(task, 'cannot update rate bucket %s: %s',
|
||||
k, err)
|
||||
else
|
||||
lua_util.debugm(N, task,
|
||||
"updated limit %s:%s -> %s (%s/%s), burst: %s, dyn_rate: %s, dyn_burst: %s",
|
||||
v.name, k, v.hash,
|
||||
bucket.burst, bucket.rate,
|
||||
data[1], data[2], data[3])
|
||||
end
|
||||
end
|
||||
local now = rspamd_util.get_time()
|
||||
now = lua_util.round(now * 1000.0) -- Get milliseconds
|
||||
local mult_burst = 1.0
|
||||
local mult_rate = 1.0
|
||||
|
||||
if verdict == 'spam' or verdict == 'junk' then
|
||||
mult_burst = bucket.spam_factor_burst or 1.0
|
||||
mult_rate = bucket.spam_factor_rate or 1.0
|
||||
elseif verdict == 'ham' then
|
||||
mult_burst = bucket.ham_factor_burst or 1.0
|
||||
mult_rate = bucket.ham_factor_rate or 1.0
|
||||
end
|
||||
|
||||
lua_redis.exec_redis_script(bucket_update_id,
|
||||
{key = v.hash, task = task, is_write = true},
|
||||
update_bucket_cb,
|
||||
{v.hash, tostring(now), tostring(mult_rate), tostring(mult_burst),
|
||||
tostring(settings.max_rate_mult), tostring(settings.max_bucket_mult),
|
||||
tostring(settings.expire)})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local opts = rspamd_config:get_all_opt(N)
|
||||
if opts then
|
||||
|
||||
settings = lua_util.override_defaults(settings, opts)
|
||||
|
||||
if opts['limit'] then
|
||||
rspamd_logger.errx(rspamd_config, 'Legacy ratelimit config format no longer supported')
|
||||
end
|
||||
|
||||
if opts['rates'] and type(opts['rates']) == 'table' then
|
||||
-- new way of setting limits
|
||||
fun.each(function(t, lim)
|
||||
local buckets = {}
|
||||
|
||||
if type(lim) == 'table' and lim.bucket then
|
||||
|
||||
if lim.bucket[1] then
|
||||
for _,bucket in ipairs(lim.bucket) do
|
||||
local b = parse_limit(t, bucket)
|
||||
|
||||
if not b then
|
||||
rspamd_logger.errx(rspamd_config, 'bad ratelimit bucket for %s: "%s"',
|
||||
t, b)
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(buckets, b)
|
||||
end
|
||||
else
|
||||
local bucket = parse_limit(t, lim.bucket)
|
||||
|
||||
if not bucket then
|
||||
rspamd_logger.errx(rspamd_config, 'bad ratelimit bucket for %s: "%s"',
|
||||
t, lim.bucket)
|
||||
return
|
||||
end
|
||||
|
||||
buckets = {bucket}
|
||||
end
|
||||
|
||||
settings.limits[t] = {
|
||||
buckets = buckets
|
||||
}
|
||||
|
||||
if lim.selector then
|
||||
local selector = lua_selectors.parse_selector(rspamd_config, lim.selector)
|
||||
if not selector then
|
||||
rspamd_logger.errx(rspamd_config, 'bad ratelimit selector for %s: "%s"',
|
||||
t, lim.selector)
|
||||
settings.limits[t] = nil
|
||||
return
|
||||
end
|
||||
|
||||
settings.limits[t].selector = selector
|
||||
end
|
||||
else
|
||||
rspamd_logger.warnx(rspamd_config, 'old syntax for ratelimits: %s', lim)
|
||||
buckets = parse_limit(t, lim)
|
||||
if buckets then
|
||||
settings.limits[t] = {
|
||||
buckets = {buckets}
|
||||
}
|
||||
end
|
||||
end
|
||||
end, opts['rates'])
|
||||
end
|
||||
|
||||
-- Display what's enabled
|
||||
fun.each(function(s)
|
||||
rspamd_logger.infox(rspamd_config, 'enabled ratelimit: %s', s)
|
||||
end, fun.map(function(n,d)
|
||||
return string.format('%s [%s]', n,
|
||||
table.concat(fun.totable(fun.map(function(v)
|
||||
return string.format('%s msgs burst, %s msgs/sec rate',
|
||||
v.burst, v.rate)
|
||||
end, d.buckets)), '; ')
|
||||
)
|
||||
end, settings.limits))
|
||||
|
||||
-- Ret, ret, ret: stupid legacy stuff:
|
||||
-- If we have a string with commas then load it as as static map
|
||||
-- otherwise, apply normal logic of Rspamd maps
|
||||
|
||||
local wrcpts = opts['whitelisted_rcpts']
|
||||
if type(wrcpts) == 'string' then
|
||||
if string.find(wrcpts, ',') then
|
||||
settings.whitelisted_rcpts = lua_maps.rspamd_map_add_from_ucl(
|
||||
lua_util.rspamd_str_split(wrcpts, ','), 'set', 'Ratelimit whitelisted rcpts')
|
||||
else
|
||||
settings.whitelisted_rcpts = lua_maps.rspamd_map_add_from_ucl(wrcpts, 'set',
|
||||
'Ratelimit whitelisted rcpts')
|
||||
end
|
||||
elseif type(opts['whitelisted_rcpts']) == 'table' then
|
||||
settings.whitelisted_rcpts = lua_maps.rspamd_map_add_from_ucl(wrcpts, 'set',
|
||||
'Ratelimit whitelisted rcpts')
|
||||
else
|
||||
-- Stupid default...
|
||||
settings.whitelisted_rcpts = lua_maps.rspamd_map_add_from_ucl(
|
||||
settings.whitelisted_rcpts, 'set', 'Ratelimit whitelisted rcpts')
|
||||
end
|
||||
|
||||
if opts['whitelisted_ip'] then
|
||||
settings.whitelisted_ip = lua_maps.rspamd_map_add('ratelimit', 'whitelisted_ip', 'radix',
|
||||
'Ratelimit whitelist ip map')
|
||||
end
|
||||
|
||||
if opts['whitelisted_user'] then
|
||||
settings.whitelisted_user = lua_maps.rspamd_map_add('ratelimit', 'whitelisted_user', 'set',
|
||||
'Ratelimit whitelist user map')
|
||||
end
|
||||
|
||||
settings.custom_keywords = {}
|
||||
if opts['custom_keywords'] then
|
||||
local ret, res_or_err = pcall(loadfile(opts['custom_keywords']))
|
||||
|
||||
if ret then
|
||||
opts['custom_keywords'] = {}
|
||||
if type(res_or_err) == 'table' then
|
||||
for k,hdl in pairs(res_or_err) do
|
||||
settings['custom_keywords'][k] = hdl
|
||||
end
|
||||
elseif type(res_or_err) == 'function' then
|
||||
settings['custom_keywords']['custom'] = res_or_err
|
||||
end
|
||||
else
|
||||
rspamd_logger.errx(rspamd_config, 'cannot execute %s: %s',
|
||||
opts['custom_keywords'], res_or_err)
|
||||
settings['custom_keywords'] = {}
|
||||
end
|
||||
end
|
||||
|
||||
if opts['message_func'] then
|
||||
message_func = assert(load(opts['message_func']))()
|
||||
end
|
||||
|
||||
redis_params = lua_redis.parse_redis_server('ratelimit')
|
||||
|
||||
if not redis_params then
|
||||
rspamd_logger.infox(rspamd_config, 'no servers are specified, disabling module')
|
||||
lua_util.disable_module(N, "redis")
|
||||
else
|
||||
local s = {
|
||||
type = 'prefilter,nostat',
|
||||
name = 'RATELIMIT_CHECK',
|
||||
priority = 7,
|
||||
callback = ratelimit_cb,
|
||||
flags = 'empty',
|
||||
}
|
||||
|
||||
if settings.symbol then
|
||||
s.name = settings.symbol
|
||||
elseif settings.info_symbol then
|
||||
s.name = settings.info_symbol
|
||||
end
|
||||
|
||||
rspamd_config:register_symbol(s)
|
||||
rspamd_config:register_symbol {
|
||||
type = 'idempotent',
|
||||
name = 'RATELIMIT_UPDATE',
|
||||
callback = ratelimit_update_cb,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
rspamd_config:add_on_load(function(cfg, ev_base, worker)
|
||||
load_scripts(cfg, ev_base)
|
||||
end)
|
@@ -36,7 +36,7 @@ RUN mkdir /usr/share/doc/sogo \
|
||||
sogo \
|
||||
sogo-activesync \
|
||||
&& rm -rf /var/lib/apt/lists/* \
|
||||
&& echo '* * * * * sogo /usr/sbin/sogo-ealarms-notify 2>/dev/null' > /etc/cron.d/sogo \
|
||||
&& echo '* * * * * sogo /usr/sbin/sogo-ealarms-notify -p /etc/sogo/sieve.creds 2>/dev/null' > /etc/cron.d/sogo \
|
||||
&& echo '* * * * * sogo /usr/sbin/sogo-tool expire-sessions 60' >> /etc/cron.d/sogo \
|
||||
&& echo '0 0 * * * sogo /usr/sbin/sogo-tool update-autoreply -p /etc/sogo/sieve.creds' >> /etc/cron.d/sogo \
|
||||
&& touch /etc/default/locale
|
||||
@@ -44,9 +44,6 @@ RUN mkdir /usr/share/doc/sogo \
|
||||
COPY ./bootstrap-sogo.sh /bootstrap-sogo.sh
|
||||
COPY syslog-ng.conf /etc/syslog-ng/syslog-ng.conf
|
||||
COPY supervisord.conf /etc/supervisor/supervisord.conf
|
||||
COPY theme-blue.js /usr/lib/GNUstep/SOGo/WebServerResources/js/theme-blue.js
|
||||
COPY theme-blue.css /usr/lib/GNUstep/SOGo/WebServerResources/css/theme-default.css
|
||||
COPY sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
|
||||
COPY acl.diff /acl.diff
|
||||
COPY stop-supervisor.sh /usr/local/sbin/stop-supervisor.sh
|
||||
|
||||
|
@@ -167,43 +167,6 @@ echo ' </dict>
|
||||
chown sogo:sogo -R /var/lib/sogo/
|
||||
chmod 600 /var/lib/sogo/GNUstep/Defaults/sogod.plist
|
||||
|
||||
# Prevent theme switching
|
||||
sed -i \
|
||||
-e 's/eaf5e9/E3F2FD/g' \
|
||||
-e 's/cbe5c8/BBDEFB/g' \
|
||||
-e 's/aad6a5/90CAF9/g' \
|
||||
-e 's/88c781/64B5F6/g' \
|
||||
-e 's/66b86a/42A5F5/g' \
|
||||
-e 's/56b04c/2196F3/g' \
|
||||
-e 's/4da143/1E88E5/g' \
|
||||
-e 's/388e3c/1976D2/g' \
|
||||
-e 's/367d2e/1565C0/g' \
|
||||
-e 's/225e1b/0D47A1/g' \
|
||||
-e 's/fafafa/82B1FF/g' \
|
||||
-e 's/69f0ae/448AFF/g' \
|
||||
-e 's/00e676/2979ff/g' \
|
||||
-e 's/00c853/2962ff/g' \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/js/Common/Common.app.js \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/js/Common.js
|
||||
|
||||
sed -i \
|
||||
-e 's/default: "900"/default: "700"/g' \
|
||||
-e 's/default: "500"/default: "700"/g' \
|
||||
-e 's/"hue-1": "400"/"hue-1": "500"/g' \
|
||||
-e 's/"hue-1": "A100"/"hue-1": "500"/g' \
|
||||
-e 's/"hue-2": "800"/"hue-2": "700"/g' \
|
||||
-e 's/"hue-2": "300"/"hue-2": "700"/g' \
|
||||
-e 's/"hue-3": "A700"/"hue-3": "A200"/' \
|
||||
-e 's/default:"900"/default:"700"/g' \
|
||||
-e 's/default:"500"/default:"700"/g' \
|
||||
-e 's/"hue-1":"400"/"hue-1":"500"/g' \
|
||||
-e 's/"hue-1":"A100"/"hue-1":"500"/g' \
|
||||
-e 's/"hue-2":"800"/"hue-2":"700"/g' \
|
||||
-e 's/"hue-2":"300"/"hue-2":"700"/g' \
|
||||
-e 's/"hue-3":"A700"/"hue-3":"A200"/' \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/js/Common/Common.app.js \
|
||||
/usr/lib/GNUstep/SOGo/WebServerResources/js/Common.js
|
||||
|
||||
# Patch ACLs
|
||||
if [[ ${ACL_ANYONE} == 'allow' ]]; then
|
||||
#enable any or authenticated targets for ACL
|
||||
@@ -217,4 +180,7 @@ else
|
||||
fi
|
||||
fi
|
||||
|
||||
# Copy logo, if any
|
||||
[[ -f /etc/sogo/sogo-full.svg ]] && cp /etc/sogo/sogo-full.svg /usr/lib/GNUstep/SOGo/WebServerResources/img/sogo-full.svg
|
||||
|
||||
exec gosu sogo /usr/sbin/sogod
|
||||
|
@@ -1,160 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Generator: Adobe Illustrator 16.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
|
||||
<svg
|
||||
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||
xmlns:cc="http://creativecommons.org/ns#"
|
||||
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||
xmlns:svg="http://www.w3.org/2000/svg"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
version="1.1"
|
||||
id="Layer_1"
|
||||
x="0px"
|
||||
y="0px"
|
||||
width="640px"
|
||||
height="350px"
|
||||
viewBox="78.712 58.488 640 350"
|
||||
style="enable-background:new 78.712 58.488 640 350;"
|
||||
xml:space="preserve"
|
||||
inkscape:version="0.91 r13725"
|
||||
sodipodi:docname="sogo-full.svg"><metadata
|
||||
id="metadata9"><rdf:RDF><cc:Work
|
||||
rdf:about=""><dc:format>image/svg+xml</dc:format><dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" /></cc:Work></rdf:RDF></metadata><defs
|
||||
id="defs7" /><sodipodi:namedview
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#666666"
|
||||
borderopacity="1"
|
||||
objecttolerance="10"
|
||||
gridtolerance="10"
|
||||
guidetolerance="10"
|
||||
inkscape:pageopacity="0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:window-width="1721"
|
||||
inkscape:window-height="1177"
|
||||
id="namedview5"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.8396893"
|
||||
inkscape:cx="360.23913"
|
||||
inkscape:cy="334.02085"
|
||||
inkscape:window-x="-8"
|
||||
inkscape:window-y="-8"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="Layer_1" /><path
|
||||
style="fill:#1976d2;fill-opacity:0.71428573"
|
||||
d="M648.541,145.679c-9.947,0-17.009-7.278-17.009-17.048c0-9.777,7.062-17.057,17.009-17.057 c10.024,0,17.086,7.279,17.086,17.057C665.627,138.401,658.565,145.679,648.541,145.679z M648.511,94.893 c-19.693,0-33.679,14.4-33.679,33.738c0,19.33,13.985,33.729,33.679,33.729c19.822,0,33.808-14.4,33.808-33.729 C682.318,109.293,668.333,94.893,648.511,94.893z M648.482,179.843c-29.889,0-51.123-21.868-51.123-51.212 c0-29.353,21.234-51.209,51.123-51.209c30.082,0,51.307,21.856,51.307,51.209C699.789,157.975,678.564,179.843,648.482,179.843z M648.442,58.488c-40.929,0-69.995,29.946-69.995,70.143c0,40.189,29.066,70.125,69.995,70.125c41.194,0,70.27-29.937,70.27-70.125 C718.712,88.434,689.637,58.488,648.442,58.488z M158.166,183.902l-21.018-5.008c-19.131-4.396-28.849-9.413-28.849-23.21 c0-15.684,15.99-21.965,30.419-21.965c14.667,0,25.382,7.329,31.693,18.737c0.02,0.048,0.051,0.097,0.09,0.157 c0.127,0.247,0.276,0.484,0.403,0.731l0.03-0.02c1.985,3.002,5.323,5.008,8.919,5.008c6.122,0,10.558-4.425,10.558-10.547 c0-2.341-0.504-4.82-1.601-6.688c-10.764-18.302-28.513-26.192-48.838-26.192c-27.594,0-54.262,13.797-54.262,44.218 c0,27.921,27.605,36.079,37.64,38.578l20.069,4.71c15.368,3.763,27.912,8.791,27.912,23.517c0,16.938-17.561,23.943-34.499,23.943 c-17.245,0-30.015-9.37-38.814-22.37h-0.01c-1.956-3-4.988-4.328-8.702-4.328c-5.984,0-10.805,5.185-10.587,11.162 c0.098,2.438,0.909,4.637,2.153,6.405c13.787,20.633,33.728,28.41,55.96,28.41c28.543,0,57.085-13.143,57.085-45.132 C193.918,203.325,178.551,188.613,158.166,183.902z M298.479,250.312c-33.866,0-55.199-25.403-55.199-58.331 c0-32.939,21.333-58.343,55.199-58.343c34.192,0,55.516,25.403,55.516,58.343C353.996,224.91,332.672,250.312,298.479,250.312z M298.479,114.823c-45.471,0-77.777,32.93-77.777,77.158c0,44.217,32.306,77.146,77.777,77.146 c45.786,0,78.093-32.929,78.093-77.146C376.572,147.753,344.266,114.823,298.479,114.823z M518.715,234.312 c-0.771,0.74-1.549,1.472-2.399,2.175c-1.106,1.014-2.391,2.112-3.854,3.208c-8.829,6.391-19.979,10.094-33.017,10.094 c-33.876,0-55.198-25.402-55.198-58.332c0-32.939,21.322-58.342,55.198-58.342c34.183,0,55.506,25.403,55.506,58.342 C534.951,208.653,529.135,223.774,518.715,234.312z M468.097,317.938c2.528,0,5.146-0.168,7.863-0.504 c5.018-0.631,9.588-0.909,13.729-0.909c19.24,0.109,29.036,5.7,34.943,12.158c5.895,6.499,8.168,15.311,8.158,22.796 c0.01,3.586-0.555,6.795-1.177,8.721c-2.944,8.93-8.888,15.002-17.996,19.576c-9.035,4.484-21.095,6.777-33.707,6.757 c-4.514,0-9.105-0.288-13.639-0.831c-8.573-0.987-19.911-4.671-28.13-11.093c-4.138-3.199-6.458-6.991-8.858-11.485 c-2.379-4.514-2.783-9.748-2.783-16.442v-0.742c0-12.346,4.84-20.544,11.051-26.5c3.07-2.904,5.69-5.064,7.99-6.438 c0.366-0.218,0.438-0.416,0.755-0.593C452.39,316.014,459.684,317.968,468.097,317.938z M479.445,114.301 c-45.471,0-77.786,32.929-77.786,77.157c0,29.887,14.765,54.598,38.378,67.489c-0.314,0.314-0.621,0.641-0.916,0.966 c-6.104,6.687-9.226,15.25-9.236,23.913c-0.008,3.821,0.624,7.741,1.977,11.494c-3.062,1.956-6.717,4.634-10.46,8.147 c-9.026,8.408-18.734,22.541-19.021,42.097c-0.01,0.454-0.01,0.829-0.01,1.118c-0.01,10.071,2.379,19.157,6.459,26.774 c6.133,11.466,15.683,19.445,25.539,24.77c9.917,5.334,20.257,8.166,29.273,9.274c5.373,0.643,10.826,0.988,16.268,0.988 c15.151-0.02,30.261-2.578,43.409-9.019c13.085-6.34,24.333-17.253,29.192-32.562c1.443-4.553,2.212-9.719,2.231-15.428 c-0.02-11.595-3.349-25.759-13.767-37.452c-10.421-11.734-27.654-19.566-51.288-19.459c-5.138,0-10.606,0.356-16.426,1.078 c-1.877,0.227-3.596,0.334-5.166,0.334c-7.239-0.048-10.872-2.053-13.036-4.098c-2.133-2.084-3.2-4.839-3.229-8.058 c-0.01-3.28,1.284-6.727,3.467-9.078c2.231-2.332,5.008-3.91,9.846-3.97c0.436,0,0.9,0.01,1.374,0.05 c3.101,0.216,6.112,0.325,9.037,0.325c24.188,0.047,42.38-7.448,54.756-17.759c12.415-10.312,18.971-22.854,22.071-32.76l-0.04-0.01 c3.37-8.899,5.197-18.715,5.197-29.166C557.539,147.229,525.234,114.301,479.445,114.301z"
|
||||
id="path3" /><g
|
||||
id="g3"
|
||||
transform="matrix(0.69327133,0,0,0.69327133,-230.59227,-153.05511)"><g
|
||||
id="g5"><g
|
||||
id="g7"><path
|
||||
d="m 748.616,546.705 c -6.272,4.929 9.576,36.937 20.52,33.516 10.944,-3.42 -10.945,-41.04 -20.52,-33.516 z"
|
||||
id="path19"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#acef29" /></g></g><g
|
||||
id="g37"><g
|
||||
id="g39"><g
|
||||
id="g41" /></g><g
|
||||
id="g45"><g
|
||||
id="g47"><g
|
||||
id="g49" /></g></g></g><g
|
||||
id="g57"><g
|
||||
id="g59"><g
|
||||
id="g61" /></g><g
|
||||
id="g65"><g
|
||||
id="g67"><g
|
||||
id="g69" /></g></g></g><g
|
||||
id="g73"
|
||||
style="opacity:0.38999999"><g
|
||||
id="g75" /></g><g
|
||||
id="g81" /><g
|
||||
id="g85" /><g
|
||||
id="g99"><polyline
|
||||
points="690.928,453.92 678.401,490.532 689.894,539.76 710.135,559.116 "
|
||||
id="polyline101"
|
||||
style="fill:#3d5263" /><g
|
||||
id="g103"><g
|
||||
id="g105"><polyline
|
||||
points="665.082,423.023 677.433,450.19 693.064,457.2 715.139,427.604 "
|
||||
id="polyline107"
|
||||
style="fill:#fef3df" /><g
|
||||
id="g109"><path
|
||||
d="m 705.288,438.868 c 0,0 -17.599,-18.89 -38.165,-13.309 0,0 4.277,25.767 36.096,32.372 l 2.164,6.413 c 0,0 -49.958,-5.925 -46.456,-49.964 0,0 36.728,-16.138 55.372,9.881"
|
||||
id="path111"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b58765" /><polyline
|
||||
points="855.735,417.576 844.957,445.404 829.753,453.295 806.023,425.008 "
|
||||
id="polyline113"
|
||||
style="fill:#fef3df" /><path
|
||||
d="m 816.503,435.691 c 0,0 16.491,-19.864 37.34,-15.466 0,0 -2.797,25.969 -34.187,34.38 l -1.796,6.527 c 0,0 49.537,-8.768 43.528,-52.535 0,0 -37.588,-14.015 -54.719,13.026"
|
||||
id="path115"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b58765" /></g></g><path
|
||||
d="m 721.173,570.346 42.418,-1.212 15.59845,-160.5887 c -42.319,1.209 -92.18245,30.1357 -91.14645,66.4047 0.031,1.102 0.125,2.111 0.182,3.163 0.181,2.98 0.504,5.741 0.89,8.424 1.602,11.197 4.722,20.488 7.355,32.71 1.27,5.906 4.299,17.614 4.299,17.614 0.052,0.308 0.143,0.606 0.196,0.913 2.281,12.491 9.666,24.028 20.208,32.572 z"
|
||||
id="path117"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b58765"
|
||||
sodipodi:nodetypes="cccccccccc" /><path
|
||||
d="m 758.532,407.21 4.626,161.937 42.418,-1.212 c 10.038,-9.132 16.75,-21.072 18.317,-33.672 0.035,-0.31 0.107,-0.613 0.141,-0.923 0,0 2.354,-11.862 3.287,-17.831 1.932,-12.352 4.518,-21.807 5.478,-33.075 0.23,-2.702 0.393,-5.477 0.4,-8.463 0.002,-1.053 0.036,-2.066 0.005,-3.168 -1.037,-36.269 -32.35,-64.802 -74.672,-63.593 z"
|
||||
id="path119"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#b58765" /><g
|
||||
id="g121"><g
|
||||
id="g123"><path
|
||||
d="m 822.293,541.988 c 0.613,21.473 -25.496,39.648 -58.32,40.586 -32.83,0.938 -59.932,-15.717 -60.546,-37.19 -0.614,-21.477 25.494,-39.648 58.324,-40.586 32.825,-0.938 59.929,15.713 60.542,37.19 z"
|
||||
id="path125"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#fef3df" /></g></g><g
|
||||
id="g127"><g
|
||||
id="g129"><g
|
||||
id="g131"><path
|
||||
d="m 735.761,538.45 c 0.135,4.712 -3.578,8.644 -8.294,8.778 -4.708,0.134 -8.641,-3.579 -8.776,-8.291 -0.135,-4.718 3.58,-8.644 8.288,-8.779 4.717,-0.134 8.647,3.573 8.782,8.292 z"
|
||||
id="path133"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#5a3620" /></g></g><g
|
||||
id="g135"><g
|
||||
id="g137"><path
|
||||
d="m 806.891,536.418 c 0.135,4.712 -3.575,8.644 -8.291,8.778 -4.714,0.135 -8.646,-3.579 -8.781,-8.291 -0.135,-4.718 3.579,-8.644 8.293,-8.779 4.716,-0.134 8.644,3.573 8.779,8.292 z"
|
||||
id="path139"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#5a3620" /></g></g></g><g
|
||||
id="g141"><path
|
||||
d="m 831.225,475.208 c 0.201,-2.368 0.344,-4.799 0.352,-7.411 0,-0.924 0.031,-1.81 0.003,-2.778 -0.883,-30.924 -26.904,-55.418 -62.478,-55.716 l -5.561,0.163 -0.005,0 c -0.005,0.014 -19.18666,70.69971 61.71134,107.73071 0.624,-3.294 9.87079,-9.74237 10.28979,-12.41137 1.694,-10.822 -5.15313,-19.70834 -4.31213,-29.57734 z"
|
||||
id="path143"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#87654a"
|
||||
sodipodi:nodetypes="ccccccccc" /></g><g
|
||||
id="g145"><g
|
||||
id="g147"><g
|
||||
id="g149"><g
|
||||
id="g151"><path
|
||||
d="m 807.344,471.221 c 0.151,5.28 -4.011,9.684 -9.294,9.835 -5.279,0.151 -9.686,-4.008 -9.837,-9.288 -0.151,-5.285 4.011,-9.687 9.291,-9.838 5.282,-0.151 9.689,4.006 9.84,9.291 z"
|
||||
id="path153"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#5a3620" /></g></g></g><g
|
||||
id="g155"><g
|
||||
id="g157"><g
|
||||
id="g159"><path
|
||||
d="m 737.68,473.211 c 0.151,5.28 -4.01,9.684 -9.289,9.835 -5.284,0.151 -9.685,-4.008 -9.835,-9.288 -0.151,-5.285 4.005,-9.687 9.289,-9.837 5.279,-0.152 9.684,4.005 9.835,9.29 z"
|
||||
id="path161"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#5a3620" /></g></g></g><g
|
||||
id="g163"><g
|
||||
id="g165"><path
|
||||
d="m 735.112,470.41 c 0.055,1.939 -1.47,3.555 -3.41,3.61 -1.939,0.055 -3.558,-1.47 -3.613,-3.41 -0.055,-1.935 1.474,-3.552 3.413,-3.607 1.94,-0.055 3.555,1.472 3.61,3.407 z"
|
||||
id="path167"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" /></g></g><g
|
||||
id="g169"><g
|
||||
id="g171"><path
|
||||
d="m 804.125,468.439 c 0.055,1.939 -1.472,3.555 -3.409,3.61 -1.94,0.055 -3.556,-1.47 -3.611,-3.41 -0.055,-1.935 1.471,-3.552 3.411,-3.607 1.937,-0.056 3.554,1.471 3.609,3.407 z"
|
||||
id="path173"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#ffffff" /></g></g></g></g><path
|
||||
d="m 761.738,405.962 c -50.342,1.438 -89.989,43.417 -88.55,93.759 1.438,50.342 43.417,89.987 93.758,88.549 50.343,-1.438 89.988,-43.415 88.55,-93.757 -1.438,-50.343 -43.415,-89.989 -93.758,-88.551 z m -4.396,163.807 c -40.192,1.148 -73.725,-31.172 -74.896,-72.19 -1.172,-41.017 30.461,-75.2 70.653,-76.348 40.191,-1.148 73.723,31.172 74.895,72.19 1.171,41.018 -30.462,75.2 -70.652,76.348 z"
|
||||
id="path179"
|
||||
inkscape:connector-curvature="0"
|
||||
style="fill:#f1f2f2" /><g
|
||||
id="g181" /></g></g></svg>
|
Before Width: | Height: | Size: 12 KiB |
File diff suppressed because one or more lines are too long
@@ -1,103 +0,0 @@
|
||||
/* -*- Mode: javascript; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
|
||||
(function() {
|
||||
'use strict';
|
||||
|
||||
angular.module('SOGo.Common')
|
||||
.config(configure)
|
||||
|
||||
/**
|
||||
* @ngInject
|
||||
*/
|
||||
configure.$inject = ['$mdThemingProvider'];
|
||||
function configure($mdThemingProvider) {
|
||||
|
||||
// Overwrite values to prevent flipping colors on login screen
|
||||
$mdThemingProvider.definePalette('mailcow-blue', {
|
||||
'50': 'E3F2FD',
|
||||
'100': 'BBDEFB',
|
||||
'200': '90CAF9',
|
||||
'300': '64B5F6',
|
||||
'400': '42A5F5',
|
||||
'500': '2196F3',
|
||||
'600': '1E88E5',
|
||||
'700': '1976D2',
|
||||
'800': '1565C0',
|
||||
'900': '0D47A1',
|
||||
'1000': '0D47A1',
|
||||
'A100': '82B1FF',
|
||||
'A200': '448AFF',
|
||||
'A400': '2979ff',
|
||||
'A700': '2962ff',
|
||||
'contrastDefaultColor': 'dark',
|
||||
'contrastLightColors': ['700', '800', '900'],
|
||||
'contrastDarkColors': undefined
|
||||
});
|
||||
|
||||
$mdThemingProvider.definePalette('sogo-green', {
|
||||
'50': 'E3F2FD',
|
||||
'100': 'BBDEFB',
|
||||
'200': '90CAF9',
|
||||
'300': '64B5F6',
|
||||
'400': '42A5F5',
|
||||
'500': '2196F3',
|
||||
'600': '1E88E5',
|
||||
'700': '1976D2',
|
||||
'800': '1565C0',
|
||||
'900': '0D47A1',
|
||||
'1000': '0D47A1',
|
||||
'A100': '82B1FF',
|
||||
'A200': '448AFF',
|
||||
'A400': '2979ff',
|
||||
'A700': '2962ff',
|
||||
'contrastDefaultColor': 'dark',
|
||||
'contrastLightColors': ['700', '800', '900'],
|
||||
'contrastDarkColors': undefined
|
||||
});
|
||||
|
||||
$mdThemingProvider.definePalette('default', {
|
||||
'50': 'E3F2FD',
|
||||
'100': 'BBDEFB',
|
||||
'200': '90CAF9',
|
||||
'300': '64B5F6',
|
||||
'400': '42A5F5',
|
||||
'500': '2196F3',
|
||||
'600': '1E88E5',
|
||||
'700': '1976D2',
|
||||
'800': '1565C0',
|
||||
'900': '0D47A1',
|
||||
'1000': '0D47A1',
|
||||
'A100': '82B1FF',
|
||||
'A200': '448AFF',
|
||||
'A400': '2979ff',
|
||||
'A700': '2962ff',
|
||||
'contrastDefaultColor': 'dark',
|
||||
'contrastLightColors': ['700', '800', '900'],
|
||||
'contrastDarkColors': undefined
|
||||
});
|
||||
|
||||
$mdThemingProvider.theme('default')
|
||||
.primaryPalette('mailcow-blue', {
|
||||
'default': '700', // top toolbar
|
||||
'hue-1': '500',
|
||||
'hue-2': '700', // sidebar toolbar
|
||||
'hue-3': 'A200'
|
||||
})
|
||||
.accentPalette('mailcow-blue', {
|
||||
'default': '800', // fab buttons
|
||||
'hue-1': '50', // center list toolbar
|
||||
'hue-2': '500',
|
||||
'hue-3': 'A700'
|
||||
})
|
||||
.backgroundPalette('grey', {
|
||||
'default': '50', // center list background
|
||||
'hue-1': '100',
|
||||
'hue-2': '200',
|
||||
'hue-3': '300'
|
||||
});
|
||||
|
||||
$mdThemingProvider.setDefaultTheme('default');
|
||||
$mdThemingProvider.generateThemesOnDemand(false);
|
||||
$mdThemingProvider.alwaysWatchTheme(true);
|
||||
}
|
||||
})();
|
@@ -322,6 +322,65 @@ phpfpm_checks() {
|
||||
return 1
|
||||
}
|
||||
|
||||
ratelimit_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
THRESHOLD=1
|
||||
RL_LOG_STATUS=$(redis-cli -h redis LRANGE RL_LOG 0 0 | jq .qid)
|
||||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
err_c_cur=${err_count}
|
||||
RL_LOG_STATUS_PREV=${RL_LOG_STATUS}
|
||||
RL_LOG_STATUS=$(redis-cli -h redis LRANGE RL_LOG 0 0 | jq .qid)
|
||||
if [[ ${RL_LOG_STATUS_PREV} != ${RL_LOG_STATUS} ]]; then
|
||||
err_count=$(( ${err_count} + 1 ))
|
||||
fi
|
||||
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
|
||||
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
|
||||
progress "Ratelimit" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
|
||||
if [[ $? == 10 ]]; then
|
||||
diff_c=0
|
||||
sleep 1
|
||||
else
|
||||
diff_c=0
|
||||
sleep $(( ( RANDOM % 30 ) + 10 ))
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
ipv6nat_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
THRESHOLD=1
|
||||
# Reduce error count by 2 after restarting an unhealthy container
|
||||
trap "[ ${err_count} -gt 1 ] && err_count=$(( ${err_count} - 2 ))" USR1
|
||||
while [ ${err_count} -lt ${THRESHOLD} ]; do
|
||||
err_c_cur=${err_count}
|
||||
IPV6NAT_CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\")) | .id")
|
||||
if [[ ! -z ${IPV6NAT_CONTAINER_ID} ]]; then
|
||||
LATEST_STARTED="$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\") | not)" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
|
||||
LATEST_IPV6NAT="$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], StartedAt: .State.StartedAt}" | jq -rc "select( .name | tostring | contains(\"ipv6nat\"))" | jq -rc .StartedAt | xargs -n1 date +%s -d | sort | tail -n1)"
|
||||
DIFFERENCE_START_TIME=$(expr ${LATEST_IPV6NAT} - ${LATEST_STARTED} 2>/dev/null)
|
||||
if [[ "${DIFFERENCE_START_TIME}" -lt 30 ]]; then
|
||||
err_count=$(( ${err_count} + 1 ))
|
||||
fi
|
||||
fi
|
||||
[ ${err_c_cur} -eq ${err_count} ] && [ ! $((${err_count} - 1)) -lt 0 ] && err_count=$((${err_count} - 1)) diff_c=1
|
||||
[ ${err_c_cur} -ne ${err_count} ] && diff_c=$(( ${err_c_cur} - ${err_count} ))
|
||||
progress "IPv6 NAT" ${THRESHOLD} $(( ${THRESHOLD} - ${err_count} )) ${diff_c}
|
||||
if [[ $? == 10 ]]; then
|
||||
diff_c=0
|
||||
sleep 1
|
||||
else
|
||||
diff_c=0
|
||||
sleep 3600
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
|
||||
rspamd_checks() {
|
||||
err_count=0
|
||||
diff_c=0
|
||||
@@ -448,6 +507,26 @@ done
|
||||
) &
|
||||
BACKGROUND_TASKS+=($!)
|
||||
|
||||
(
|
||||
while true; do
|
||||
if ! ratelimit_checks; then
|
||||
log_msg "Ratelimit hit error limit"
|
||||
echo ratelimit > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
) &
|
||||
BACKGROUND_TASKS+=($!)
|
||||
|
||||
(
|
||||
while true; do
|
||||
if ! ipv6nat_checks; then
|
||||
log_msg "IPv6 NAT warning: ipv6nat container was not started at least 30s after siblings (not an error)"
|
||||
echo ipv6nat > /tmp/com_pipe
|
||||
fi
|
||||
done
|
||||
) &
|
||||
BACKGROUND_TASKS+=($!)
|
||||
|
||||
# Monitor watchdog agents, stop script when agents fails and wait for respawn by Docker (restart:always:n)
|
||||
(
|
||||
while true; do
|
||||
@@ -482,7 +561,10 @@ while true; do
|
||||
CONTAINER_ID=
|
||||
HAS_INITDB=
|
||||
read com_pipe_answer </tmp/com_pipe
|
||||
if [[ ${com_pipe_answer} =~ .+-mailcow ]]; then
|
||||
if [[ ${com_pipe_answer} == "ratelimit" ]]; then
|
||||
log_msg "At least one ratelimit was applied"
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}" "No further information available."
|
||||
elif [[ ${com_pipe_answer} =~ .+-mailcow ]] || [[ ${com_pipe_answer} == "ipv6nat" ]]; then
|
||||
kill -STOP ${BACKGROUND_TASKS[*]}
|
||||
sleep 3
|
||||
CONTAINER_ID=$(curl --silent --insecure https://dockerapi/containers/json | jq -r ".[] | {name: .Config.Labels[\"com.docker.compose.service\"], id: .Id}" | jq -rc "select( .name | tostring | contains(\"${com_pipe_answer}\")) | .id")
|
||||
@@ -499,9 +581,11 @@ while true; do
|
||||
else
|
||||
log_msg "Sending restart command to ${CONTAINER_ID}..."
|
||||
curl --silent --insecure -XPOST https://dockerapi/containers/${CONTAINER_ID}/restart
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}"
|
||||
if [[ ${com_pipe_answer} != "ipv6nat" ]]; then
|
||||
[[ ! -z ${WATCHDOG_NOTIFY_EMAIL} ]] && mail_error "${com_pipe_answer}"
|
||||
fi
|
||||
log_msg "Wait for restarted container to settle and continue watching..."
|
||||
sleep 30
|
||||
sleep 35
|
||||
fi
|
||||
fi
|
||||
kill -CONT ${BACKGROUND_TASKS[*]}
|
||||
|
Reference in New Issue
Block a user