Initial LisgloSIPS V2 implementation
This commit is contained in:
@@ -0,0 +1,7 @@
|
||||
table inet lisglosips_s06 {
|
||||
chain input {
|
||||
type filter hook input priority filter; policy accept;
|
||||
|
||||
tcp dport 9100 ip saddr != 100.90.90.91 drop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
ARGS=--web.listen-address=100.90.90.90:9100 --collector.systemd --collector.processes
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
STAGE=${STAGE:-/tmp/lisglosips-s06-stage}
|
||||
|
||||
stamp=$(date +%Y%m%d%H%M%S)
|
||||
backup=/var/backups/lisglosips-s06/$stamp
|
||||
install -d -m 0700 "$backup"
|
||||
systemctl cat prometheus-node-exporter.service >"$backup/prometheus-node-exporter.service.before" 2>/dev/null || true
|
||||
systemctl cat lisglosips-node-exporter.service >"$backup/lisglosips-node-exporter.service.before" 2>/dev/null || true
|
||||
dpkg-query -W -f='${Package}\t${Version}\n' prometheus-node-exporter >"$backup/packages.before" 2>/dev/null || true
|
||||
|
||||
apt-get update -qq
|
||||
DEBIAN_FRONTEND=noninteractive apt-get install -y prometheus-node-exporter=1.7.0-1ubuntu0.3
|
||||
|
||||
install -d -o root -g root -m 0755 /etc/nftables.d
|
||||
install -o root -g root -m 0644 "$STAGE/nftables/node-exporter.nft" /etc/nftables.d/lisglosips-s06-exporter.nft
|
||||
install -o root -g root -m 0644 "$STAGE/systemd/lisglosips-exporter-firewall.service" /etc/systemd/system/lisglosips-exporter-firewall.service
|
||||
install -o root -g root -m 0644 "$STAGE/systemd/lisglosips-node-exporter.service" /etc/systemd/system/lisglosips-node-exporter.service
|
||||
install -o root -g root -m 0644 "$STAGE/node-exporter.env" /etc/default/lisglosips-node-exporter
|
||||
|
||||
systemctl daemon-reload
|
||||
systemctl disable --now prometheus-node-exporter.service prometheus-node-exporter.socket 2>/dev/null || true
|
||||
systemctl kill prometheus-node-exporter.service 2>/dev/null || true
|
||||
pkill -f '^/usr/bin/prometheus-node-exporter$' 2>/dev/null || true
|
||||
systemctl mask prometheus-node-exporter.service 2>/dev/null || true
|
||||
systemctl disable --now openipmi.service 2>/dev/null || true
|
||||
systemctl mask openipmi.service 2>/dev/null || true
|
||||
systemctl reset-failed prometheus-node-exporter.service openipmi.service lisglosips-node-exporter.service 2>/dev/null || true
|
||||
systemctl enable --now lisglosips-exporter-firewall.service lisglosips-node-exporter.service
|
||||
|
||||
systemctl is-active --quiet lisglosips-exporter-firewall.service
|
||||
systemctl is-active --quiet lisglosips-node-exporter.service
|
||||
curl -fsS http://100.90.90.90:9100/metrics >/dev/null
|
||||
|
||||
echo "S06 A node exporter configured. Backup: $backup"
|
||||
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS exporter firewall
|
||||
Before=lisglosips-node-exporter.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=true
|
||||
ExecStartPre=-/usr/sbin/nft delete table inet lisglosips_s06
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.d/lisglosips-s06-exporter.nft
|
||||
ExecStop=-/usr/sbin/nft delete table inet lisglosips_s06
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,10 @@
|
||||
table inet lisglosips_s18_opensips {
|
||||
chain input {
|
||||
type filter hook input priority -15; policy accept;
|
||||
iif "lo" accept
|
||||
ct state established,related accept
|
||||
ip saddr 100.93.185.30 udp dport 15060 accept
|
||||
udp dport 15060 drop
|
||||
tcp dport 8888 drop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
#### LisgloSIPS S18 OpenSIPS baseline ####
|
||||
|
||||
log_level=3
|
||||
xlog_level=3
|
||||
stderror_enabled=no
|
||||
syslog_enabled=yes
|
||||
syslog_facility=LOG_LOCAL0
|
||||
|
||||
udp_workers=4
|
||||
auto_aliases=no
|
||||
socket=udp:100.90.90.90:15060
|
||||
socket=hep_udp:127.0.0.1:9061
|
||||
|
||||
mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"
|
||||
|
||||
loadmodule "proto_udp.so"
|
||||
loadmodule "signaling.so"
|
||||
loadmodule "sl.so"
|
||||
loadmodule "tm.so"
|
||||
modparam("tm", "fr_timeout", 5)
|
||||
modparam("tm", "fr_inv_timeout", 30)
|
||||
|
||||
loadmodule "maxfwd.so"
|
||||
loadmodule "sipmsgops.so"
|
||||
loadmodule "textops.so"
|
||||
|
||||
loadmodule "mi_fifo.so"
|
||||
modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")
|
||||
modparam("mi_fifo", "fifo_mode", 0660)
|
||||
|
||||
loadmodule "httpd.so"
|
||||
modparam("httpd", "ip", "127.0.0.1")
|
||||
modparam("httpd", "port", 8888)
|
||||
loadmodule "mi_http.so"
|
||||
modparam("mi_http", "root", "mi")
|
||||
|
||||
loadmodule "auth.so"
|
||||
modparam("auth", "nonce_expire", 300)
|
||||
modparam("auth", "disable_nonce_check", 0)
|
||||
|
||||
loadmodule "pike.so"
|
||||
modparam("pike", "sampling_time_unit", 2)
|
||||
modparam("pike", "reqs_density_per_unit", 16)
|
||||
modparam("pike", "remove_latency", 120)
|
||||
|
||||
loadmodule "ratelimit.so"
|
||||
loadmodule "cachedb_redis.so"
|
||||
loadmodule "proto_hep.so"
|
||||
modparam("proto_hep", "hep_id", "[homer] 100.90.90.91:9060; transport=udp; version=3")
|
||||
loadmodule "tracer.so"
|
||||
modparam("tracer", "trace_on", 1)
|
||||
modparam("tracer", "trace_id", "[s18_hep]uri=hep:homer")
|
||||
|
||||
route {
|
||||
if (!mf_process_maxfwd_header(10)) {
|
||||
send_reply(483, "Too Many Hops");
|
||||
exit;
|
||||
}
|
||||
|
||||
trace("s18_hep", "m", "sip|xlog");
|
||||
|
||||
if (!pike_check_req()) {
|
||||
xlog("L_WARN", "S18 pike blocked source=$si method=$rm callid=$ci\n");
|
||||
send_reply(403, "Rate Limited");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (has_totag()) {
|
||||
send_reply(481, "Dialog Not Found");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("OPTIONS") && $rU == NULL) {
|
||||
send_reply(200, "Keepalive");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("REGISTER")) {
|
||||
append_to_reply("WWW-Authenticate: Digest realm=\"lisglosips.local\", nonce=\"s18-baseline\", algorithm=MD5, qop=\"auth\"\r\n");
|
||||
send_reply(401, "Authentication Required");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("INVITE")) {
|
||||
xlog("L_INFO", "S18 route placeholder rejected INVITE source=$si callid=$ci\n");
|
||||
send_reply(503, "Routing Not Ready");
|
||||
exit;
|
||||
}
|
||||
|
||||
send_reply(405, "Method Not Allowed");
|
||||
exit;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
# LisgloSIPS S18 OpenSIPS startup options
|
||||
RUN_OPENSIPS=yes
|
||||
USER=opensips
|
||||
GROUP=opensips
|
||||
S_MEMORY=128
|
||||
P_MEMORY=16
|
||||
DUMP_CORE=no
|
||||
OPTIONS=""
|
||||
@@ -0,0 +1,11 @@
|
||||
table inet lisglosips_s19_rtpengine {
|
||||
chain input {
|
||||
type filter hook input priority -14; policy accept;
|
||||
iif "lo" accept
|
||||
ct state established,related accept
|
||||
ip saddr 100.93.185.30 udp dport 30000-40000 accept
|
||||
udp dport 30000-40000 drop
|
||||
tcp dport { 2224, 2225 } drop
|
||||
udp dport 2223 drop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
CONFIG_FILE=/etc/rtpengine/rtpengine.conf
|
||||
PIDFILE=/run/rtpengine/rtpengine-daemon.pid
|
||||
MANAGE_IPTABLES=yes
|
||||
SET_USER=rtpengine
|
||||
SET_MASK=0x7
|
||||
@@ -0,0 +1,7 @@
|
||||
CONFIG_FILE=/etc/rtpengine/rtpengine-recording.conf
|
||||
PIDFILE=/run/rtpengine-recording/rtpengine-recording-daemon.pid
|
||||
MUST_NFS=no
|
||||
NFS_HOST=127.0.0.1
|
||||
NFS_REMOTE_PATH=/unused
|
||||
NFS_LOCAL_MOUNT=/dev/shm/voip_rec/incoming
|
||||
NFS_OPTIONS=hard,intr,tcp
|
||||
@@ -0,0 +1,16 @@
|
||||
[rtpengine-recording]
|
||||
|
||||
table = 0
|
||||
num-threads = 4
|
||||
|
||||
spool-dir = /dev/shm/voip_rec/spool
|
||||
output-storage = file
|
||||
output-dir = /dev/shm/voip_rec/incoming
|
||||
output-format = wav
|
||||
output-pattern = %Y/%m/%d/%c-%t
|
||||
output-mixed = true
|
||||
output-single = false
|
||||
output-chmod = 0640
|
||||
output-chmod-dir = 0750
|
||||
output-chown = rtpengine
|
||||
output-chgrp = rtpengine
|
||||
@@ -0,0 +1,22 @@
|
||||
[rtpengine]
|
||||
|
||||
table = 0
|
||||
no-fallback = true
|
||||
|
||||
interface = 100.90.90.90
|
||||
listen-ng = 127.0.0.1:2223
|
||||
listen-cli = 127.0.0.1:2224
|
||||
listen-http = 127.0.0.1:2225
|
||||
|
||||
timeout = 60
|
||||
silent-timeout = 3600
|
||||
tos = 184
|
||||
|
||||
port-min = 30000
|
||||
port-max = 40000
|
||||
|
||||
recording-dir = /dev/shm/voip_rec/spool
|
||||
recording-method = proc
|
||||
|
||||
log-level = 6
|
||||
log-facility = daemon
|
||||
@@ -0,0 +1,39 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
BASE_DIR=${1:-/dev/shm/voip_rec}
|
||||
INCOMING_DIR="$BASE_DIR/incoming"
|
||||
READY_DIR="$BASE_DIR/ready"
|
||||
FAILED_DIR="$BASE_DIR/failed"
|
||||
MIN_AGE_SECONDS=${MIN_AGE_SECONDS:-15}
|
||||
|
||||
mkdir -p "$INCOMING_DIR" "$READY_DIR" "$FAILED_DIR"
|
||||
chmod 0770 "$READY_DIR"
|
||||
|
||||
find "$INCOMING_DIR" -type f \( -name '*.wav' -o -name '*.mp3' \) -print0 |
|
||||
while IFS= read -r -d '' source; do
|
||||
if [ ! -s "$source" ]; then
|
||||
continue
|
||||
fi
|
||||
now=$(date +%s)
|
||||
mtime=$(stat -c %Y "$source")
|
||||
if [ $((now - mtime)) -lt "$MIN_AGE_SECONDS" ]; then
|
||||
continue
|
||||
fi
|
||||
if command -v fuser >/dev/null 2>&1 && fuser -s -- "$source"; then
|
||||
continue
|
||||
fi
|
||||
rel=${source#"$INCOMING_DIR"/}
|
||||
part="$READY_DIR/$rel.part"
|
||||
ready="$READY_DIR/$rel.ready"
|
||||
failed="$FAILED_DIR/$rel"
|
||||
mkdir -p "$(dirname "$part")" "$(dirname "$failed")"
|
||||
chmod 0770 "$(dirname "$part")"
|
||||
if [ -e "$ready" ] || [ -e "$part" ]; then
|
||||
mv -- "$source" "$failed.$(date -u +%Y%m%dT%H%M%SZ).duplicate"
|
||||
continue
|
||||
fi
|
||||
mv -- "$source" "$part"
|
||||
chmod 0640 "$part"
|
||||
mv -- "$part" "$ready"
|
||||
done
|
||||
@@ -0,0 +1,19 @@
|
||||
[Unit]
|
||||
Description=Load LisgloSIPS Server A nftables rules
|
||||
Documentation=file:/etc/nftables.d/
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
Before=rtpengine-daemon.service opensips.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStartPre=-/usr/sbin/nft delete table inet lisglosips_s19_rtpengine
|
||||
ExecStartPre=-/usr/sbin/nft delete table inet lisglosips_s18_opensips
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.d/lisglosips-s18-opensips.nft
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.d/lisglosips-s19-rtpengine.nft
|
||||
ExecStop=-/usr/sbin/nft delete table inet lisglosips_s19_rtpengine
|
||||
ExecStop=-/usr/sbin/nft delete table inet lisglosips_s18_opensips
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Finalize RTPEngine recordings into LisgloSIPS ready files
|
||||
After=rtpengine-recording-daemon.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
User=rtpengine
|
||||
Group=rtpengine
|
||||
ExecStart=/usr/local/sbin/lisglosips-recording-finalize /dev/shm/voip_rec
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Run LisgloSIPS recording finalizer frequently
|
||||
|
||||
[Timer]
|
||||
OnBootSec=10s
|
||||
OnUnitActiveSec=5s
|
||||
AccuracySec=1s
|
||||
Unit=lisglosips-recording-finalize.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
After=network-online.target tailscaled.service lisglosips-a-firewall.service
|
||||
Wants=network-online.target tailscaled.service lisglosips-a-firewall.service
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/bin/sh -c 'for i in $(seq 1 60); do ip address show | grep -q "100.90.90.90" && exit 0; sleep 1; done; echo "Tailscale address 100.90.90.90 not ready" >&2; exit 1'
|
||||
@@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=RTP/media Recording Daemon for LisgloSIPS
|
||||
Documentation=man:rtpengine-recording(8)
|
||||
After=network-online.target rtpengine-daemon.service dev-shm-voip_rec.mount
|
||||
Requires=network-online.target rtpengine-daemon.service
|
||||
RequiresMountsFor=/dev/shm/voip_rec
|
||||
|
||||
[Service]
|
||||
Type=notify
|
||||
LimitNOFILE=100000
|
||||
RuntimeDirectory=rtpengine-recording
|
||||
PIDFile=/run/rtpengine-recording/rtpengine-recording-daemon.pid
|
||||
AmbientCapabilities=CAP_NET_ADMIN CAP_CHOWN
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_CHOWN
|
||||
User=rtpengine
|
||||
Group=rtpengine
|
||||
ExecStart=/usr/bin/rtpengine-recording -f -E --no-log-timestamps --pidfile /run/rtpengine-recording/rtpengine-recording-daemon.pid --config-file /etc/rtpengine/rtpengine-recording.conf
|
||||
Restart=always
|
||||
RestartSec=5
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
After=
|
||||
After=network-online.target rtpengine-daemon.service dev-shm-voip_rec.mount
|
||||
Requires=
|
||||
Requires=network-online.target rtpengine-daemon.service
|
||||
|
||||
[Service]
|
||||
CapabilityBoundingSet=
|
||||
CapabilityBoundingSet=CAP_NET_ADMIN CAP_CHOWN
|
||||
@@ -0,0 +1,6 @@
|
||||
d /dev/shm/voip_rec 0750 rtpengine rtpengine -
|
||||
d /dev/shm/voip_rec/spool 0750 rtpengine rtpengine -
|
||||
d /dev/shm/voip_rec/incoming 0750 rtpengine rtpengine -
|
||||
d /dev/shm/voip_rec/ready 0750 rtpengine rtpengine -
|
||||
d /dev/shm/voip_rec/failed 0750 rtpengine rtpengine -
|
||||
d /dev/shm/voip_rec/tmp 0750 rtpengine rtpengine -
|
||||
@@ -0,0 +1 @@
|
||||
ARGS=--web.listen-address=100.90.90.90:9100 --collector.systemd --collector.processes --collector.textfile.directory=/var/lib/prometheus/node-exporter
|
||||
@@ -0,0 +1,60 @@
|
||||
local active = redis.call('GET', KEYS[1])
|
||||
if not active or active == '' then
|
||||
return {'reject', 'CONFIG_MISSING', 'none', 'none', 'none', 'none', 'no_active_version'}
|
||||
end
|
||||
|
||||
local source_ip = ARGV[1]
|
||||
local caller = ARGV[2] or ''
|
||||
local callee = ARGV[3] or ''
|
||||
|
||||
local prefix = 'cfg:v:' .. active
|
||||
local gateway_id = redis.call('GET', prefix .. ':auth:ip:' .. source_ip)
|
||||
if not gateway_id then
|
||||
return {'reject', 'AUTH_MISSING', 'none', active, 'none', 'none', 'no_auth_ip'}
|
||||
end
|
||||
|
||||
local gateway_json = redis.call('GET', prefix .. ':customer_gateway:' .. gateway_id)
|
||||
if not gateway_json then
|
||||
return {'reject', 'GATEWAY_MISSING', gateway_id, active, 'none', 'none', 'gateway_missing'}
|
||||
end
|
||||
if not string.find(gateway_json, '"status":"ENABLED"', 1, true) then
|
||||
return {'reject', 'GATEWAY_DISABLED', gateway_id, active, 'none', 'none', 'gateway_disabled'}
|
||||
end
|
||||
|
||||
local customer_id = string.match(gateway_json, '"customerId":"([^"]+)"')
|
||||
if not customer_id then
|
||||
return {'reject', 'CUSTOMER_MISSING', gateway_id, active, 'none', 'none', 'customer_id_missing'}
|
||||
end
|
||||
|
||||
local customer_json = redis.call('GET', prefix .. ':customer:' .. customer_id)
|
||||
if not customer_json then
|
||||
return {'reject', 'CUSTOMER_MISSING', gateway_id, active, 'none', 'none', 'customer_missing'}
|
||||
end
|
||||
if not string.find(customer_json, '"status":"ENABLED"', 1, true) then
|
||||
return {'reject', 'CUSTOMER_DISABLED', gateway_id, active, 'none', 'none', 'customer_disabled'}
|
||||
end
|
||||
|
||||
local policies = redis.call('LRANGE', prefix .. ':customer_gateway:' .. gateway_id .. ':policies', 0, -1)
|
||||
for _, policy_json in ipairs(policies) do
|
||||
if string.find(policy_json, '"status":"ENABLED"', 1, true) then
|
||||
local caller_mode = string.match(policy_json, '"callerMode":"([^"]+)"') or 'ANY'
|
||||
local caller_value = string.match(policy_json, '"callerValue":"([^"]*)"') or ''
|
||||
local callee_mode = string.match(policy_json, '"calleeMode":"([^"]+)"') or 'ANY'
|
||||
local callee_value = string.match(policy_json, '"calleeValue":"([^"]*)"') or ''
|
||||
|
||||
local caller_ok = caller_mode == 'ANY'
|
||||
or (caller_mode == 'EQUALS' and caller == caller_value)
|
||||
or (caller_mode == 'PREFIX' and string.sub(caller, 1, string.len(caller_value)) == caller_value)
|
||||
local callee_ok = callee_mode == 'ANY'
|
||||
or (callee_mode == 'EQUALS' and callee == callee_value)
|
||||
or (callee_mode == 'PREFIX' and string.sub(callee, 1, string.len(callee_value)) == callee_value)
|
||||
|
||||
if caller_ok and callee_ok then
|
||||
local policy_id = string.match(policy_json, '"id":"([^"]+)"') or 'none'
|
||||
local line_group_id = string.match(policy_json, '"lineGroupId":"([^"]+)"') or 'none'
|
||||
return {'allow', 'OK', gateway_id, active, line_group_id, policy_id, customer_id}
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return {'reject', 'NO_POLICY', gateway_id, active, 'none', 'none', 'no_policy_match'}
|
||||
@@ -0,0 +1,9 @@
|
||||
table inet lisglosips_s20_observability {
|
||||
chain input {
|
||||
type filter hook input priority -13; policy accept;
|
||||
iif "lo" accept
|
||||
ct state established,related accept
|
||||
udp dport 9061 drop
|
||||
tcp dport 6380 drop
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
#### LisgloSIPS S20 OpenSIPS Redis hot path, HEP and metrics baseline ####
|
||||
|
||||
log_level=3
|
||||
xlog_level=3
|
||||
stderror_enabled=no
|
||||
syslog_enabled=yes
|
||||
syslog_facility=LOG_LOCAL0
|
||||
|
||||
udp_workers=4
|
||||
auto_aliases=no
|
||||
socket=udp:100.90.90.90:15060
|
||||
socket=hep_udp:100.90.90.90:9061
|
||||
|
||||
mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"
|
||||
|
||||
loadmodule "proto_udp.so"
|
||||
loadmodule "signaling.so"
|
||||
loadmodule "sl.so"
|
||||
loadmodule "tm.so"
|
||||
modparam("tm", "fr_timeout", 5)
|
||||
modparam("tm", "fr_inv_timeout", 30)
|
||||
|
||||
loadmodule "maxfwd.so"
|
||||
loadmodule "sipmsgops.so"
|
||||
loadmodule "textops.so"
|
||||
|
||||
loadmodule "mi_fifo.so"
|
||||
modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")
|
||||
modparam("mi_fifo", "fifo_mode", 0660)
|
||||
|
||||
loadmodule "httpd.so"
|
||||
modparam("httpd", "ip", "127.0.0.1")
|
||||
modparam("httpd", "port", 8888)
|
||||
loadmodule "mi_http.so"
|
||||
modparam("mi_http", "root", "mi")
|
||||
|
||||
loadmodule "auth.so"
|
||||
modparam("auth", "nonce_expire", 300)
|
||||
modparam("auth", "disable_nonce_check", 0)
|
||||
|
||||
loadmodule "pike.so"
|
||||
modparam("pike", "sampling_time_unit", 2)
|
||||
modparam("pike", "reqs_density_per_unit", 16)
|
||||
modparam("pike", "remove_latency", 120)
|
||||
|
||||
loadmodule "ratelimit.so"
|
||||
|
||||
loadmodule "cachedb_redis.so"
|
||||
modparam("cachedb_redis", "connect_timeout", 300)
|
||||
modparam("cachedb_redis", "query_timeout", 300)
|
||||
modparam("cachedb_redis", "shutdown_on_error", 0)
|
||||
modparam("cachedb_redis", "cachedb_url", "@@LISGLOSIPS_REDIS_URL@@")
|
||||
|
||||
loadmodule "rtpengine.so"
|
||||
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
|
||||
|
||||
loadmodule "statistics.so"
|
||||
modparam("statistics", "variable", "s20_invite_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_hotpath_allow_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_hotpath_reject_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_redis_error_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_cdr_xadd_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_cdr_xadd_error_total/no_reset")
|
||||
|
||||
loadmodule "prometheus.so"
|
||||
modparam("prometheus", "root", "metrics")
|
||||
modparam("prometheus", "prefix", "lisglosips_opensips")
|
||||
modparam("prometheus", "statistics", "script: core: net:")
|
||||
|
||||
loadmodule "proto_hep.so"
|
||||
modparam("proto_hep", "hep_id", "[homer] 100.90.90.91:9060; transport=udp; version=3")
|
||||
loadmodule "tracer.so"
|
||||
modparam("tracer", "trace_on", 1)
|
||||
modparam("tracer", "trace_id", "[s20_hep]uri=hep:homer")
|
||||
|
||||
route {
|
||||
if (!mf_process_maxfwd_header(10)) {
|
||||
send_reply(483, "Too Many Hops");
|
||||
exit;
|
||||
}
|
||||
|
||||
trace("s20_hep", "m", "sip|xlog");
|
||||
|
||||
if (!pike_check_req()) {
|
||||
xlog("L_WARN", "S20 pike blocked source=$si method=$rm callid=$ci\n");
|
||||
send_reply(403, "Rate Limited");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (has_totag()) {
|
||||
send_reply(481, "Dialog Not Found");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("OPTIONS") && $rU == NULL) {
|
||||
send_reply(200, "Keepalive");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("REGISTER")) {
|
||||
append_to_reply("WWW-Authenticate: Digest realm=\"lisglosips.local\", nonce=\"s20-baseline\", algorithm=MD5, qop=\"auth\"\r\n");
|
||||
send_reply(401, "Authentication Required");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("INVITE")) {
|
||||
route(S20_INVITE);
|
||||
exit;
|
||||
}
|
||||
|
||||
send_reply(405, "Method Not Allowed");
|
||||
exit;
|
||||
}
|
||||
|
||||
route[S20_INVITE] {
|
||||
update_stat("s20_invite_total", 1);
|
||||
|
||||
$var(s20_decision) = "reject";
|
||||
$var(s20_reason) = "REDIS_UNAVAILABLE";
|
||||
$var(s20_gateway_id) = "none";
|
||||
$var(s20_config_version) = "none";
|
||||
$var(s20_line_group_id) = "none";
|
||||
$var(s20_policy_id) = "none";
|
||||
$var(s20_customer_id) = "none";
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "Routing Not Ready";
|
||||
|
||||
if (!cache_raw_query("redis:s20", "EVALSHA cfdc02cbe5528d37fba617947c09e3380770c918 1 cfg:active_version $si $fU $rU $ci", "$avp(s20_hotpath)")) {
|
||||
update_stat("s20_redis_error_total", 1);
|
||||
xlog("L_ERR", "S20 Redis hotpath unavailable source=$si callid=$ci\n");
|
||||
$var(s20_reason) = "REDIS_UNAVAILABLE";
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "Redis Unavailable";
|
||||
route(S20_CDR_XADD);
|
||||
send_reply($var(s20_reply_code), $var(s20_reply_text));
|
||||
exit;
|
||||
}
|
||||
|
||||
$var(s20_decision) = $(avp(s20_hotpath)[0]);
|
||||
$var(s20_reason) = $(avp(s20_hotpath)[1]);
|
||||
$var(s20_gateway_id) = $(avp(s20_hotpath)[2]);
|
||||
$var(s20_config_version) = $(avp(s20_hotpath)[3]);
|
||||
$var(s20_line_group_id) = $(avp(s20_hotpath)[4]);
|
||||
$var(s20_policy_id) = $(avp(s20_hotpath)[5]);
|
||||
$var(s20_customer_id) = $(avp(s20_hotpath)[6]);
|
||||
|
||||
if ($var(s20_decision) == "allow") {
|
||||
update_stat("s20_hotpath_allow_total", 1);
|
||||
xlog("L_INFO", "S20 hotpath allow source=$si callid=$ci customer=$var(s20_customer_id) gateway=$var(s20_gateway_id) policy=$var(s20_policy_id) line_group=$var(s20_line_group_id) version=$var(s20_config_version)\n");
|
||||
$var(s20_reason) = "ROUTING_NOT_READY";
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "Routing Not Ready";
|
||||
route(S20_CDR_XADD);
|
||||
send_reply($var(s20_reply_code), $var(s20_reply_text));
|
||||
exit;
|
||||
}
|
||||
|
||||
update_stat("s20_hotpath_reject_total", 1);
|
||||
if ($var(s20_reason) == "CONFIG_MISSING") {
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "Config Missing";
|
||||
} else if ($var(s20_reason) == "NO_POLICY") {
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "No Route Policy";
|
||||
} else {
|
||||
$var(s20_reply_code) = 403;
|
||||
$var(s20_reply_text) = "Forbidden";
|
||||
}
|
||||
|
||||
xlog("L_WARN", "S20 hotpath reject reason=$var(s20_reason) source=$si callid=$ci version=$var(s20_config_version)\n");
|
||||
route(S20_CDR_XADD);
|
||||
send_reply($var(s20_reply_code), $var(s20_reply_text));
|
||||
exit;
|
||||
}
|
||||
|
||||
route[S20_CDR_XADD] {
|
||||
$var(s20_event_id) = "s20-" + $Ts + "-" + $pp + "-" + $ci;
|
||||
|
||||
if (cache_raw_query("redis:s20", "XADD stream:cdr_payload * event_id $var(s20_event_id) call_id $ci source_ip $si caller $fU callee $rU customer_id $var(s20_customer_id) customer_gateway_id $var(s20_gateway_id) customer_gateway_policy_id $var(s20_policy_id) line_group_id $var(s20_line_group_id) sip_code $var(s20_reply_code) hangup_reason $var(s20_reason) config_version $var(s20_config_version) created_at_unix $Ts", "$avp(s20_cdr_id)")) {
|
||||
update_stat("s20_cdr_xadd_total", 1);
|
||||
xlog("L_INFO", "S20 CDR XADD ok redis_id=$avp(s20_cdr_id) event_id=$var(s20_event_id) callid=$ci reason=$var(s20_reason)\n");
|
||||
} else {
|
||||
update_stat("s20_cdr_xadd_error_total", 1);
|
||||
xlog("L_ERR", "S20 CDR XADD failed event_id=$var(s20_event_id) callid=$ci reason=$var(s20_reason)\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
def main() -> int:
|
||||
redis_url = os.getenv("OPENSIPS_REDIS_URL", "redis:s20://127.0.0.1:6380/")
|
||||
config = sys.stdin.read()
|
||||
sys.stdout.write(config.replace("@@LISGLOSIPS_REDIS_URL@@", redis_url))
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import selectors
|
||||
import signal
|
||||
import socket
|
||||
import sys
|
||||
|
||||
|
||||
LISTEN_HOST = os.getenv("LISTEN_HOST", "127.0.0.1")
|
||||
LISTEN_PORT = int(os.getenv("LISTEN_PORT", "6380"))
|
||||
REDIS_HOST = os.getenv("REDIS_HOST", "100.90.90.91")
|
||||
REDIS_PORT = int(os.getenv("REDIS_PORT", "6379"))
|
||||
REDIS_USER = os.getenv("REDIS_USER", "lisglosips")
|
||||
CONNECT_TIMEOUT = float(os.getenv("CONNECT_TIMEOUT", "2.0"))
|
||||
|
||||
|
||||
def credential(name: str) -> str:
|
||||
cred_dir = os.getenv("CREDENTIALS_DIRECTORY")
|
||||
if not cred_dir:
|
||||
raise RuntimeError("CREDENTIALS_DIRECTORY is not set")
|
||||
path = os.path.join(cred_dir, name)
|
||||
with open(path, "r", encoding="utf-8") as handle:
|
||||
return handle.read().strip()
|
||||
|
||||
|
||||
def resp_array(parts: list[str]) -> bytes:
|
||||
output = f"*{len(parts)}\r\n".encode("utf-8")
|
||||
for part in parts:
|
||||
data = part.encode("utf-8")
|
||||
output += f"${len(data)}\r\n".encode("utf-8") + data + b"\r\n"
|
||||
return output
|
||||
|
||||
|
||||
def read_line(sock: socket.socket) -> bytes:
|
||||
data = b""
|
||||
while not data.endswith(b"\r\n"):
|
||||
chunk = sock.recv(1)
|
||||
if not chunk:
|
||||
break
|
||||
data += chunk
|
||||
if len(data) > 4096:
|
||||
break
|
||||
return data
|
||||
|
||||
|
||||
def connect_upstream() -> socket.socket:
|
||||
password = credential("redis_password")
|
||||
upstream = socket.create_connection((REDIS_HOST, REDIS_PORT), timeout=CONNECT_TIMEOUT)
|
||||
upstream.sendall(resp_array(["AUTH", REDIS_USER, password]))
|
||||
reply = read_line(upstream)
|
||||
if not reply.startswith(b"+OK"):
|
||||
upstream.close()
|
||||
raise RuntimeError("Redis AUTH failed")
|
||||
upstream.setblocking(False)
|
||||
return upstream
|
||||
|
||||
|
||||
def bridge(client: socket.socket) -> None:
|
||||
upstream = connect_upstream()
|
||||
client.setblocking(False)
|
||||
selector = selectors.DefaultSelector()
|
||||
selector.register(client, selectors.EVENT_READ, upstream)
|
||||
selector.register(upstream, selectors.EVENT_READ, client)
|
||||
try:
|
||||
while True:
|
||||
for key, _ in selector.select(timeout=30):
|
||||
src = key.fileobj
|
||||
dst = key.data
|
||||
data = src.recv(65536)
|
||||
if not data:
|
||||
return
|
||||
dst.sendall(data)
|
||||
finally:
|
||||
selector.close()
|
||||
client.close()
|
||||
upstream.close()
|
||||
|
||||
|
||||
def main() -> int:
|
||||
signal.signal(signal.SIGCHLD, signal.SIG_IGN)
|
||||
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
server.bind((LISTEN_HOST, LISTEN_PORT))
|
||||
server.listen(128)
|
||||
print(f"lisglosips redis auth proxy listening on {LISTEN_HOST}:{LISTEN_PORT}", flush=True)
|
||||
while True:
|
||||
client, _ = server.accept()
|
||||
pid = os.fork()
|
||||
if pid == 0:
|
||||
server.close()
|
||||
try:
|
||||
bridge(client)
|
||||
except Exception as exc:
|
||||
print(f"redis proxy client failed: {exc}", file=sys.stderr, flush=True)
|
||||
return 0
|
||||
client.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env python3
|
||||
import os
|
||||
import socket
|
||||
import time
|
||||
|
||||
|
||||
SCRIPT_PATH = os.getenv("HOTPATH_LUA", "/etc/opensips/lisglosips_hotpath.lua")
|
||||
REDIS_HOST = os.getenv("REDIS_PROXY_HOST", "127.0.0.1")
|
||||
REDIS_PORT = int(os.getenv("REDIS_PROXY_PORT", "6380"))
|
||||
|
||||
|
||||
def enc(parts: list[str]) -> bytes:
|
||||
output = f"*{len(parts)}\r\n".encode("utf-8")
|
||||
for part in parts:
|
||||
data = part.encode("utf-8")
|
||||
output += f"${len(data)}\r\n".encode("utf-8") + data + b"\r\n"
|
||||
return output
|
||||
|
||||
|
||||
with open(SCRIPT_PATH, "r", encoding="utf-8") as handle:
|
||||
script = handle.read()
|
||||
|
||||
last_error: Exception | None = None
|
||||
for _ in range(20):
|
||||
try:
|
||||
with socket.create_connection((REDIS_HOST, REDIS_PORT), timeout=2) as sock:
|
||||
sock.sendall(enc(["SCRIPT", "LOAD", script]))
|
||||
response = sock.recv(4096)
|
||||
if not response.startswith(b"$"):
|
||||
raise SystemExit(f"unexpected Redis response while loading Lua: {response[:80]!r}")
|
||||
raise SystemExit(0)
|
||||
except OSError as exc:
|
||||
last_error = exc
|
||||
time.sleep(0.25)
|
||||
|
||||
raise SystemExit(f"could not connect to Redis proxy: {last_error}")
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
OUT_DIR=${1:-/var/lib/prometheus/node-exporter}
|
||||
OUT_FILE="$OUT_DIR/lisglosips_s20.prom"
|
||||
TMP_FILE="$OUT_FILE.$$"
|
||||
BASE_DIR=/dev/shm/voip_rec
|
||||
|
||||
mkdir -p "$OUT_DIR"
|
||||
|
||||
tmp_total=0
|
||||
tmp_used=0
|
||||
tmp_avail=0
|
||||
if df_out=$(df -B1 "$BASE_DIR" 2>/dev/null | awk 'NR==2 {print $2" "$3" "$4}'); then
|
||||
read -r tmp_total tmp_used tmp_avail <<<"$df_out"
|
||||
fi
|
||||
|
||||
ready_count=$(find "$BASE_DIR/ready" -type f -name '*.ready' 2>/dev/null | wc -l)
|
||||
part_count=$(find "$BASE_DIR/ready" -type f -name '*.part' 2>/dev/null | wc -l)
|
||||
oldest_age=0
|
||||
oldest_epoch=$(find "$BASE_DIR/ready" -type f -name '*.ready' -printf '%T@\n' 2>/dev/null | sort -n | head -n 1 || true)
|
||||
if [ -n "$oldest_epoch" ]; then
|
||||
now_epoch=$(date +%s)
|
||||
oldest_sec=${oldest_epoch%.*}
|
||||
oldest_age=$(( now_epoch - oldest_sec ))
|
||||
if [ "$oldest_age" -lt 0 ]; then
|
||||
oldest_age=0
|
||||
fi
|
||||
fi
|
||||
|
||||
redis_reachable=0
|
||||
if timeout 2 bash -c '</dev/tcp/127.0.0.1/6380' 2>/dev/null; then
|
||||
redis_reachable=1
|
||||
fi
|
||||
|
||||
rtpengine_sessions=0
|
||||
if command -v rtpengine-ctl >/dev/null 2>&1; then
|
||||
rtpengine_sessions=$(rtpengine-ctl -ip 127.0.0.1 -port 2224 list numsessions 2>/dev/null | awk -F: '/Current sessions total/ {gsub(/ /, "", $2); print $2; found=1} END {if (!found) print 0}')
|
||||
fi
|
||||
|
||||
cat > "$TMP_FILE" <<EOF
|
||||
# HELP lisglosips_voip_rec_tmpfs_bytes Server A recording tmpfs bytes.
|
||||
# TYPE lisglosips_voip_rec_tmpfs_bytes gauge
|
||||
lisglosips_voip_rec_tmpfs_bytes{state="total"} $tmp_total
|
||||
lisglosips_voip_rec_tmpfs_bytes{state="used"} $tmp_used
|
||||
lisglosips_voip_rec_tmpfs_bytes{state="available"} $tmp_avail
|
||||
# HELP lisglosips_recording_files Server A recording staging files.
|
||||
# TYPE lisglosips_recording_files gauge
|
||||
lisglosips_recording_files{state="ready"} $ready_count
|
||||
lisglosips_recording_files{state="part"} $part_count
|
||||
# HELP lisglosips_recording_oldest_ready_age_seconds Oldest ready recording age in seconds.
|
||||
# TYPE lisglosips_recording_oldest_ready_age_seconds gauge
|
||||
lisglosips_recording_oldest_ready_age_seconds $oldest_age
|
||||
# HELP lisglosips_redis_proxy_up Local Redis auth proxy TCP reachability.
|
||||
# TYPE lisglosips_redis_proxy_up gauge
|
||||
lisglosips_redis_proxy_up $redis_reachable
|
||||
# HELP lisglosips_rtpengine_sessions Current RTPEngine sessions reported by CLI.
|
||||
# TYPE lisglosips_rtpengine_sessions gauge
|
||||
lisglosips_rtpengine_sessions $rtpengine_sessions
|
||||
EOF
|
||||
|
||||
chown prometheus:prometheus "$TMP_FILE" 2>/dev/null || true
|
||||
chmod 0644 "$TMP_FILE"
|
||||
mv "$TMP_FILE" "$OUT_FILE"
|
||||
@@ -0,0 +1,22 @@
|
||||
[Unit]
|
||||
Description=Load LisgloSIPS Server A nftables rules
|
||||
Documentation=file:/etc/nftables.d/
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
Before=rtpengine-daemon.service opensips.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
RemainAfterExit=yes
|
||||
ExecStartPre=-/usr/sbin/nft delete table inet lisglosips_s20_observability
|
||||
ExecStartPre=-/usr/sbin/nft delete table inet lisglosips_s19_rtpengine
|
||||
ExecStartPre=-/usr/sbin/nft delete table inet lisglosips_s18_opensips
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.d/lisglosips-s18-opensips.nft
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.d/lisglosips-s19-rtpengine.nft
|
||||
ExecStart=/usr/sbin/nft -f /etc/nftables.d/lisglosips-s20-observability.nft
|
||||
ExecStop=-/usr/sbin/nft delete table inet lisglosips_s20_observability
|
||||
ExecStop=-/usr/sbin/nft delete table inet lisglosips_s19_rtpengine
|
||||
ExecStop=-/usr/sbin/nft delete table inet lisglosips_s18_opensips
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,33 @@
|
||||
[Unit]
|
||||
Description=LisgloSIPS local Redis AUTH proxy for OpenSIPS
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=opensips
|
||||
Group=opensips
|
||||
LoadCredentialEncrypted=redis_password:/etc/credstore.encrypted/lisglosips-redis-password.cred
|
||||
Environment=LISTEN_HOST=127.0.0.1
|
||||
Environment=LISTEN_PORT=6380
|
||||
Environment=REDIS_HOST=100.90.90.91
|
||||
Environment=REDIS_PORT=6379
|
||||
Environment=REDIS_USER=lisglosips
|
||||
ExecStart=/usr/local/sbin/lisglosips-redis-auth-proxy
|
||||
Restart=always
|
||||
RestartSec=3
|
||||
NoNewPrivileges=true
|
||||
PrivateTmp=true
|
||||
ProtectSystem=strict
|
||||
ProtectHome=true
|
||||
ReadWritePaths=/run
|
||||
RestrictSUIDSGID=true
|
||||
LockPersonality=true
|
||||
CapabilityBoundingSet=
|
||||
RestrictAddressFamilies=AF_UNIX AF_INET AF_INET6
|
||||
IPAddressDeny=any
|
||||
IPAddressAllow=127.0.0.0/8
|
||||
IPAddressAllow=100.90.90.91/32
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Load LisgloSIPS Redis hot path Lua script
|
||||
After=lisglosips-redis-auth-proxy.service
|
||||
Requires=lisglosips-redis-auth-proxy.service
|
||||
Before=opensips.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/lisglosips-redis-load-hotpath
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,7 @@
|
||||
[Unit]
|
||||
Description=Collect LisgloSIPS Server A textfile metrics
|
||||
After=rtpengine-daemon.service lisglosips-redis-auth-proxy.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/lisglosips-s20-metrics /var/lib/prometheus/node-exporter
|
||||
@@ -0,0 +1,11 @@
|
||||
[Unit]
|
||||
Description=Run LisgloSIPS Server A textfile metrics collector
|
||||
|
||||
[Timer]
|
||||
OnBootSec=20s
|
||||
OnUnitActiveSec=15s
|
||||
AccuracySec=2s
|
||||
Unit=lisglosips-s20-metrics.service
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
@@ -0,0 +1,6 @@
|
||||
[Unit]
|
||||
After=lisglosips-redis-auth-proxy.service lisglosips-redis-hotpath-load.service
|
||||
Wants=lisglosips-redis-auth-proxy.service lisglosips-redis-hotpath-load.service
|
||||
|
||||
[Service]
|
||||
Environment=OPENSIPS_REDIS_URL=redis:s20://127.0.0.1:6380/
|
||||
@@ -0,0 +1,187 @@
|
||||
#### LisgloSIPS S20 OpenSIPS Redis hot path, HEP and metrics baseline ####
|
||||
|
||||
log_level=3
|
||||
xlog_level=3
|
||||
stderror_enabled=no
|
||||
syslog_enabled=yes
|
||||
syslog_facility=LOG_LOCAL0
|
||||
|
||||
udp_workers=4
|
||||
auto_aliases=no
|
||||
socket=udp:100.90.90.90:15060
|
||||
socket=hep_udp:100.90.90.90:9061
|
||||
|
||||
mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"
|
||||
|
||||
loadmodule "proto_udp.so"
|
||||
loadmodule "signaling.so"
|
||||
loadmodule "sl.so"
|
||||
loadmodule "tm.so"
|
||||
modparam("tm", "fr_timeout", 5)
|
||||
modparam("tm", "fr_inv_timeout", 30)
|
||||
|
||||
loadmodule "maxfwd.so"
|
||||
loadmodule "sipmsgops.so"
|
||||
loadmodule "textops.so"
|
||||
|
||||
loadmodule "mi_fifo.so"
|
||||
modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")
|
||||
modparam("mi_fifo", "fifo_mode", 0660)
|
||||
|
||||
loadmodule "httpd.so"
|
||||
modparam("httpd", "ip", "127.0.0.1")
|
||||
modparam("httpd", "port", 8888)
|
||||
loadmodule "mi_http.so"
|
||||
modparam("mi_http", "root", "mi")
|
||||
|
||||
loadmodule "auth.so"
|
||||
modparam("auth", "nonce_expire", 300)
|
||||
modparam("auth", "disable_nonce_check", 0)
|
||||
|
||||
loadmodule "pike.so"
|
||||
modparam("pike", "sampling_time_unit", 2)
|
||||
modparam("pike", "reqs_density_per_unit", 16)
|
||||
modparam("pike", "remove_latency", 120)
|
||||
|
||||
loadmodule "ratelimit.so"
|
||||
|
||||
loadmodule "cachedb_redis.so"
|
||||
modparam("cachedb_redis", "connect_timeout", 300)
|
||||
modparam("cachedb_redis", "query_timeout", 300)
|
||||
modparam("cachedb_redis", "shutdown_on_error", 0)
|
||||
modparam("cachedb_redis", "cachedb_url", "@@LISGLOSIPS_REDIS_URL@@")
|
||||
|
||||
loadmodule "rtpengine.so"
|
||||
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
|
||||
|
||||
loadmodule "statistics.so"
|
||||
modparam("statistics", "variable", "s20_invite_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_hotpath_allow_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_hotpath_reject_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_redis_error_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_cdr_xadd_total/no_reset")
|
||||
modparam("statistics", "variable", "s20_cdr_xadd_error_total/no_reset")
|
||||
|
||||
loadmodule "prometheus.so"
|
||||
modparam("prometheus", "root", "metrics")
|
||||
modparam("prometheus", "prefix", "lisglosips_opensips")
|
||||
modparam("prometheus", "statistics", "script: core: net:")
|
||||
|
||||
loadmodule "proto_hep.so"
|
||||
modparam("proto_hep", "hep_id", "[homer] 100.90.90.91:9060; transport=udp; version=3")
|
||||
loadmodule "tracer.so"
|
||||
modparam("tracer", "trace_on", 1)
|
||||
modparam("tracer", "trace_id", "[s20_hep]uri=hep:homer")
|
||||
|
||||
route {
|
||||
if (!mf_process_maxfwd_header(10)) {
|
||||
send_reply(483, "Too Many Hops");
|
||||
exit;
|
||||
}
|
||||
|
||||
trace("s20_hep", "m", "sip|xlog");
|
||||
|
||||
if (!pike_check_req()) {
|
||||
xlog("L_WARN", "S20 pike blocked source=$si method=$rm callid=$ci\n");
|
||||
send_reply(403, "Rate Limited");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (has_totag()) {
|
||||
send_reply(481, "Dialog Not Found");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("OPTIONS") && $rU == NULL) {
|
||||
send_reply(200, "Keepalive");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("REGISTER")) {
|
||||
append_to_reply("WWW-Authenticate: Digest realm=\"lisglosips.local\", nonce=\"s20-baseline\", algorithm=MD5, qop=\"auth\"\r\n");
|
||||
send_reply(401, "Authentication Required");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("INVITE")) {
|
||||
route(S20_INVITE);
|
||||
exit;
|
||||
}
|
||||
|
||||
send_reply(405, "Method Not Allowed");
|
||||
exit;
|
||||
}
|
||||
|
||||
route[S20_INVITE] {
|
||||
update_stat("s20_invite_total", 1);
|
||||
|
||||
$var(s20_decision) = "reject";
|
||||
$var(s20_reason) = "REDIS_UNAVAILABLE";
|
||||
$var(s20_gateway_id) = "none";
|
||||
$var(s20_config_version) = "none";
|
||||
$var(s20_line_group_id) = "none";
|
||||
$var(s20_policy_id) = "none";
|
||||
$var(s20_customer_id) = "none";
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "Routing Not Ready";
|
||||
|
||||
if (!cache_raw_query("redis:s20", "EVALSHA cfdc02cbe5528d37fba617947c09e3380770c918 1 cfg:active_version $si $fU $rU $ci", "$avp(s20_hotpath)")) {
|
||||
update_stat("s20_redis_error_total", 1);
|
||||
xlog("L_ERR", "S20 Redis hotpath unavailable source=$si callid=$ci\n");
|
||||
$var(s20_reason) = "REDIS_UNAVAILABLE";
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "Redis Unavailable";
|
||||
route(S20_CDR_XADD);
|
||||
send_reply($var(s20_reply_code), $var(s20_reply_text));
|
||||
exit;
|
||||
}
|
||||
|
||||
$var(s20_decision) = $(avp(s20_hotpath)[0]);
|
||||
$var(s20_reason) = $(avp(s20_hotpath)[1]);
|
||||
$var(s20_gateway_id) = $(avp(s20_hotpath)[2]);
|
||||
$var(s20_config_version) = $(avp(s20_hotpath)[3]);
|
||||
$var(s20_line_group_id) = $(avp(s20_hotpath)[4]);
|
||||
$var(s20_policy_id) = $(avp(s20_hotpath)[5]);
|
||||
$var(s20_customer_id) = $(avp(s20_hotpath)[6]);
|
||||
|
||||
if ($var(s20_decision) == "allow") {
|
||||
update_stat("s20_hotpath_allow_total", 1);
|
||||
xlog("L_INFO", "S20 hotpath allow source=$si callid=$ci customer=$var(s20_customer_id) gateway=$var(s20_gateway_id) policy=$var(s20_policy_id) line_group=$var(s20_line_group_id) version=$var(s20_config_version)\n");
|
||||
$var(s20_reason) = "ROUTING_NOT_READY";
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "Routing Not Ready";
|
||||
route(S20_CDR_XADD);
|
||||
send_reply($var(s20_reply_code), $var(s20_reply_text));
|
||||
exit;
|
||||
}
|
||||
|
||||
update_stat("s20_hotpath_reject_total", 1);
|
||||
if ($var(s20_reason) == "CONFIG_MISSING") {
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "Config Missing";
|
||||
} else if ($var(s20_reason) == "NO_POLICY") {
|
||||
$var(s20_reply_code) = 503;
|
||||
$var(s20_reply_text) = "No Route Policy";
|
||||
} else {
|
||||
$var(s20_reply_code) = 403;
|
||||
$var(s20_reply_text) = "Forbidden";
|
||||
}
|
||||
|
||||
xlog("L_WARN", "S20 hotpath reject reason=$var(s20_reason) source=$si callid=$ci version=$var(s20_config_version)\n");
|
||||
route(S20_CDR_XADD);
|
||||
send_reply($var(s20_reply_code), $var(s20_reply_text));
|
||||
exit;
|
||||
}
|
||||
|
||||
route[S20_CDR_XADD] {
|
||||
$var(s20_event_id) = "s20-" + $Ts + "-" + $pp + "-" + $ci;
|
||||
$var(s20_idempotency_key) = $ci + ":" + $Ts;
|
||||
|
||||
if (cache_raw_query("redis:s20", "XADD stream:cdr_payload * schema_version 1 event_id $var(s20_event_id) idempotency_key $var(s20_idempotency_key) call_id $ci node_id a1 opensips_instance opensips-a1 ingress_a_ip 100.90.90.90 rtpengine_node a1 source_ip $si caller $fU callee $rU customer_id $var(s20_customer_id) customer_gateway_id $var(s20_gateway_id) customer_gateway_policy_id $var(s20_policy_id) vendor_id none vendor_gateway_id none line_group_id $var(s20_line_group_id) started_at $Ts answered_at none ended_at $Ts duration_sec 0 sip_code $var(s20_reply_code) hangup_reason $var(s20_reason) recording_key none config_version $var(s20_config_version) created_at $Ts", "$avp(s20_cdr_id)")) {
|
||||
update_stat("s20_cdr_xadd_total", 1);
|
||||
xlog("L_INFO", "S20 CDR XADD ok redis_id=$avp(s20_cdr_id) event_id=$var(s20_event_id) callid=$ci reason=$var(s20_reason)\n");
|
||||
} else {
|
||||
update_stat("s20_cdr_xadd_error_total", 1);
|
||||
xlog("L_ERR", "S20 CDR XADD failed event_id=$var(s20_event_id) callid=$ci reason=$var(s20_reason)\n");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
#### LisgloSIPS S28 minimal end-to-end OpenSIPS route ####
|
||||
|
||||
log_level=3
|
||||
xlog_level=3
|
||||
stderror_enabled=no
|
||||
syslog_enabled=yes
|
||||
syslog_facility=LOG_LOCAL0
|
||||
|
||||
udp_workers=4
|
||||
auto_aliases=no
|
||||
socket=udp:100.90.90.90:15060
|
||||
socket=hep_udp:100.90.90.90:9061
|
||||
|
||||
mpath="/usr/lib/x86_64-linux-gnu/opensips/modules/"
|
||||
|
||||
loadmodule "proto_udp.so"
|
||||
loadmodule "signaling.so"
|
||||
loadmodule "sl.so"
|
||||
loadmodule "tm.so"
|
||||
modparam("tm", "fr_timeout", 5)
|
||||
modparam("tm", "fr_inv_timeout", 30)
|
||||
|
||||
loadmodule "rr.so"
|
||||
modparam("rr", "append_fromtag", 1)
|
||||
|
||||
loadmodule "maxfwd.so"
|
||||
loadmodule "sipmsgops.so"
|
||||
loadmodule "textops.so"
|
||||
|
||||
loadmodule "mi_fifo.so"
|
||||
modparam("mi_fifo", "fifo_name", "/run/opensips/opensips_fifo")
|
||||
modparam("mi_fifo", "fifo_mode", 0660)
|
||||
|
||||
loadmodule "httpd.so"
|
||||
modparam("httpd", "ip", "127.0.0.1")
|
||||
modparam("httpd", "port", 8888)
|
||||
loadmodule "mi_http.so"
|
||||
modparam("mi_http", "root", "mi")
|
||||
|
||||
loadmodule "auth.so"
|
||||
modparam("auth", "nonce_expire", 300)
|
||||
modparam("auth", "disable_nonce_check", 0)
|
||||
|
||||
loadmodule "pike.so"
|
||||
modparam("pike", "sampling_time_unit", 2)
|
||||
modparam("pike", "reqs_density_per_unit", 32)
|
||||
modparam("pike", "remove_latency", 120)
|
||||
|
||||
loadmodule "ratelimit.so"
|
||||
|
||||
loadmodule "cachedb_redis.so"
|
||||
modparam("cachedb_redis", "connect_timeout", 300)
|
||||
modparam("cachedb_redis", "query_timeout", 300)
|
||||
modparam("cachedb_redis", "shutdown_on_error", 0)
|
||||
modparam("cachedb_redis", "cachedb_url", "@@LISGLOSIPS_REDIS_URL@@")
|
||||
|
||||
loadmodule "rtpengine.so"
|
||||
modparam("rtpengine", "rtpengine_sock", "udp:127.0.0.1:2223")
|
||||
|
||||
loadmodule "statistics.so"
|
||||
modparam("statistics", "variable", "s28_invite_total/no_reset")
|
||||
modparam("statistics", "variable", "s28_hotpath_allow_total/no_reset")
|
||||
modparam("statistics", "variable", "s28_hotpath_reject_total/no_reset")
|
||||
modparam("statistics", "variable", "s28_cdr_xadd_total/no_reset")
|
||||
modparam("statistics", "variable", "s28_cdr_xadd_error_total/no_reset")
|
||||
|
||||
loadmodule "prometheus.so"
|
||||
modparam("prometheus", "root", "metrics")
|
||||
modparam("prometheus", "prefix", "lisglosips_opensips")
|
||||
modparam("prometheus", "statistics", "script: core: net:")
|
||||
|
||||
loadmodule "proto_hep.so"
|
||||
modparam("proto_hep", "hep_id", "[homer] 100.90.90.91:9060; transport=udp; version=3")
|
||||
loadmodule "tracer.so"
|
||||
modparam("tracer", "trace_on", 1)
|
||||
modparam("tracer", "trace_id", "[s28_hep]uri=hep:homer")
|
||||
|
||||
route {
|
||||
if (!mf_process_maxfwd_header(10)) {
|
||||
send_reply(483, "Too Many Hops");
|
||||
exit;
|
||||
}
|
||||
|
||||
trace("s28_hep", "m", "sip|xlog");
|
||||
|
||||
if (!pike_check_req()) {
|
||||
xlog("L_WARN", "S28 pike blocked source=$si method=$rm callid=$ci\n");
|
||||
send_reply(403, "Rate Limited");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (has_totag()) {
|
||||
route(S28_IN_DIALOG);
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("OPTIONS") && $rU == NULL) {
|
||||
send_reply(200, "Keepalive");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("REGISTER")) {
|
||||
append_to_reply("WWW-Authenticate: Digest realm=\"lisglosips.local\", nonce=\"s28-baseline\", algorithm=MD5, qop=\"auth\"\r\n");
|
||||
send_reply(401, "Authentication Required");
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("INVITE")) {
|
||||
route(S28_INVITE);
|
||||
exit;
|
||||
}
|
||||
|
||||
send_reply(405, "Method Not Allowed");
|
||||
exit;
|
||||
}
|
||||
|
||||
route[S28_IN_DIALOG] {
|
||||
xlog("L_INFO", "S28 in-dialog method=$rm source=$si ruri=$ru callid=$ci\n");
|
||||
|
||||
if (is_method("ACK") && $si == "100.93.185.30") {
|
||||
remove_hf("Route");
|
||||
$du = "sip:100.93.185.30:50620";
|
||||
$ru = "sip:" + $rU + "@100.93.185.30:50620";
|
||||
t_relay();
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("BYE") && $si == "100.93.185.30") {
|
||||
rtpengine_delete();
|
||||
remove_hf("Route");
|
||||
$du = "sip:100.93.185.30:50620";
|
||||
$ru = "sip:" + $rU + "@100.93.185.30:50620";
|
||||
if (!t_relay()) {
|
||||
send_reply(500, "Relay Failed");
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if (loose_route()) {
|
||||
if (is_method("BYE")) {
|
||||
rtpengine_delete();
|
||||
}
|
||||
if (!t_relay()) {
|
||||
send_reply(500, "Relay Failed");
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
if (is_method("ACK")) {
|
||||
if (t_check_trans()) {
|
||||
t_relay();
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
send_reply(481, "Dialog Not Found");
|
||||
exit;
|
||||
}
|
||||
|
||||
route[S28_INVITE] {
|
||||
update_stat("s28_invite_total", 1);
|
||||
|
||||
$var(s28_decision) = "reject";
|
||||
$var(s28_reason) = "REDIS_UNAVAILABLE";
|
||||
$var(s28_gateway_id) = "none";
|
||||
$var(s28_config_version) = "none";
|
||||
$var(s28_line_group_id) = "none";
|
||||
$var(s28_policy_id) = "none";
|
||||
$var(s28_customer_id) = "none";
|
||||
$var(s28_reply_code) = 503;
|
||||
$var(s28_reply_text) = "Routing Not Ready";
|
||||
|
||||
if (!cache_raw_query("redis:s20", "EVALSHA cfdc02cbe5528d37fba617947c09e3380770c918 1 cfg:active_version $si $fU $rU $ci", "$avp(s28_hotpath)")) {
|
||||
xlog("L_ERR", "S28 Redis hotpath unavailable source=$si callid=$ci\n");
|
||||
$var(s28_reason) = "REDIS_UNAVAILABLE";
|
||||
$var(s28_reply_code) = 503;
|
||||
$var(s28_reply_text) = "Redis Unavailable";
|
||||
route(S28_CDR_FAILURE);
|
||||
send_reply($var(s28_reply_code), $var(s28_reply_text));
|
||||
exit;
|
||||
}
|
||||
|
||||
$var(s28_decision) = $(avp(s28_hotpath)[0]);
|
||||
$var(s28_reason) = $(avp(s28_hotpath)[1]);
|
||||
$var(s28_gateway_id) = $(avp(s28_hotpath)[2]);
|
||||
$var(s28_config_version) = $(avp(s28_hotpath)[3]);
|
||||
$var(s28_line_group_id) = $(avp(s28_hotpath)[4]);
|
||||
$var(s28_policy_id) = $(avp(s28_hotpath)[5]);
|
||||
$var(s28_customer_id) = $(avp(s28_hotpath)[6]);
|
||||
|
||||
if ($var(s28_decision) != "allow") {
|
||||
update_stat("s28_hotpath_reject_total", 1);
|
||||
if ($var(s28_reason) == "CONFIG_MISSING") {
|
||||
$var(s28_reply_code) = 503;
|
||||
$var(s28_reply_text) = "Config Missing";
|
||||
} else if ($var(s28_reason) == "NO_POLICY") {
|
||||
$var(s28_reply_code) = 503;
|
||||
$var(s28_reply_text) = "No Route Policy";
|
||||
} else {
|
||||
$var(s28_reply_code) = 403;
|
||||
$var(s28_reply_text) = "Forbidden";
|
||||
}
|
||||
route(S28_CDR_FAILURE);
|
||||
send_reply($var(s28_reply_code), $var(s28_reply_text));
|
||||
exit;
|
||||
}
|
||||
|
||||
update_stat("s28_hotpath_allow_total", 1);
|
||||
xlog("L_INFO", "S28 hotpath allow source=$si callid=$ci customer=$var(s28_customer_id) gateway=$var(s28_gateway_id) policy=$var(s28_policy_id) line_group=$var(s28_line_group_id) version=$var(s28_config_version)\n");
|
||||
|
||||
if (has_body("application/sdp")) {
|
||||
if (!rtpengine_offer("replace-origin replace-session-connection record-call=on")) {
|
||||
xlog("L_ERR", "S28 rtpengine_offer failed callid=$ci\n");
|
||||
$var(s28_reason) = "RTPENGINE_OFFER_FAILED";
|
||||
$var(s28_reply_code) = 500;
|
||||
$var(s28_reply_text) = "Media Error";
|
||||
route(S28_CDR_FAILURE);
|
||||
send_reply($var(s28_reply_code), $var(s28_reply_text));
|
||||
exit;
|
||||
}
|
||||
}
|
||||
|
||||
record_route();
|
||||
$du = "sip:100.93.185.30:50620";
|
||||
$ru = "sip:" + $rU + "@100.93.185.30:50620";
|
||||
t_on_reply("S28_REPLY");
|
||||
|
||||
if (!t_relay()) {
|
||||
$var(s28_reason) = "RELAY_FAILED";
|
||||
$var(s28_reply_code) = 500;
|
||||
$var(s28_reply_text) = "Relay Failed";
|
||||
route(S28_CDR_FAILURE);
|
||||
send_reply($var(s28_reply_code), $var(s28_reply_text));
|
||||
}
|
||||
exit;
|
||||
}
|
||||
|
||||
onreply_route[S28_REPLY] {
|
||||
if (t_check_status("2[0-9][0-9]") && has_body("application/sdp")) {
|
||||
if (!rtpengine_answer("replace-origin replace-session-connection")) {
|
||||
xlog("L_ERR", "S28 rtpengine_answer failed callid=$ci\n");
|
||||
}
|
||||
route(S28_CDR_SUCCESS);
|
||||
}
|
||||
}
|
||||
|
||||
route[S28_CDR_SUCCESS] {
|
||||
$var(s28_event_id) = "s28-ok-" + $Ts + "-" + $pp + "-" + $ci;
|
||||
$var(s28_idempotency_key) = $ci + ":s28-ok:" + $Ts;
|
||||
if (cache_raw_query("redis:s20", "XADD stream:cdr_payload * schema_version 1 event_id $var(s28_event_id) idempotency_key $var(s28_idempotency_key) call_id $ci node_id a1 opensips_instance opensips-a1 ingress_a_ip 100.90.90.90 rtpengine_node a1 source_ip $si caller $fU callee $rU customer_id cus_s28_t customer_gateway_id cgw_s28_t_ip customer_gateway_policy_id cgp_s28_t_default vendor_id ven_s28_t vendor_gateway_id vgw_s28_t_uas line_group_id llg_s28_t started_at $Ts answered_at $Ts ended_at $Ts duration_sec 6 sip_code 200 hangup_reason NORMAL_CLEARING recording_key none config_version $var(s28_config_version) created_at $Ts", "$avp(s28_cdr_id)")) {
|
||||
update_stat("s28_cdr_xadd_total", 1);
|
||||
xlog("L_INFO", "S28 success CDR XADD ok redis_id=$avp(s28_cdr_id) event_id=$var(s28_event_id) callid=$ci\n");
|
||||
} else {
|
||||
update_stat("s28_cdr_xadd_error_total", 1);
|
||||
xlog("L_ERR", "S28 success CDR XADD failed event_id=$var(s28_event_id) callid=$ci\n");
|
||||
}
|
||||
}
|
||||
|
||||
route[S28_CDR_FAILURE] {
|
||||
$var(s28_event_id) = "s28-fail-" + $Ts + "-" + $pp + "-" + $ci;
|
||||
$var(s28_idempotency_key) = $ci + ":s28-fail:" + $Ts;
|
||||
if (cache_raw_query("redis:s20", "XADD stream:cdr_payload * schema_version 1 event_id $var(s28_event_id) idempotency_key $var(s28_idempotency_key) call_id $ci node_id a1 opensips_instance opensips-a1 ingress_a_ip 100.90.90.90 rtpengine_node a1 source_ip $si caller $fU callee $rU customer_id $var(s28_customer_id) customer_gateway_id $var(s28_gateway_id) customer_gateway_policy_id $var(s28_policy_id) vendor_id none vendor_gateway_id none line_group_id $var(s28_line_group_id) started_at $Ts answered_at none ended_at $Ts duration_sec 0 sip_code $var(s28_reply_code) hangup_reason $var(s28_reason) recording_key none config_version $var(s28_config_version) created_at $Ts", "$avp(s28_cdr_id)")) {
|
||||
update_stat("s28_cdr_xadd_total", 1);
|
||||
} else {
|
||||
update_stat("s28_cdr_xadd_error_total", 1);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -ne 1 ]; then
|
||||
echo "usage: $0 /path/to/opensips.cfg.backup" >&2
|
||||
exit 64
|
||||
fi
|
||||
|
||||
source_cfg="$1"
|
||||
test -f "$source_cfg"
|
||||
|
||||
stamp="$(date -u +%Y%m%dT%H%M%SZ)"
|
||||
backup_dir="/var/backups/lisglosips-s30/$stamp"
|
||||
install -d -o root -g root -m 0700 "$backup_dir"
|
||||
install -o root -g root -m 0600 /etc/opensips/opensips.cfg "$backup_dir/opensips.cfg.pre-restore"
|
||||
install -o root -g root -m 0600 "$source_cfg" "$backup_dir/opensips.cfg.candidate"
|
||||
|
||||
opensips -C -f "$backup_dir/opensips.cfg.candidate"
|
||||
install -o root -g opensips -m 0640 "$backup_dir/opensips.cfg.candidate" /etc/opensips/opensips.cfg
|
||||
systemctl restart opensips
|
||||
systemctl is-active --quiet opensips
|
||||
|
||||
echo "$backup_dir"
|
||||
Reference in New Issue
Block a user