337 lines
10 KiB
Nix
337 lines
10 KiB
Nix
{ clanLib, ... }:
|
|
{
|
|
_class = "clan.service";
|
|
manifest.name = "phonebox";
|
|
manifest.description = "";
|
|
manifest.categories = [ "System" ];
|
|
|
|
roles.default = {
|
|
interface =
|
|
{ lib, ... }:
|
|
{
|
|
options.ata-ethernet-iface = lib.mkOption {
|
|
type = lib.types.str;
|
|
description = "An Ethernet interface that connect to ATA box.";
|
|
};
|
|
};
|
|
perInstance =
|
|
{
|
|
roles,
|
|
settings,
|
|
...
|
|
}:
|
|
{
|
|
nixosModule =
|
|
{
|
|
lib,
|
|
config,
|
|
...
|
|
}:
|
|
let
|
|
user = "asterisk";
|
|
rtpPortFrom = 10000;
|
|
rtpPortTo = 20000;
|
|
ata-interface = settings.ata-ethernet-iface;
|
|
|
|
genServerSIPEndpoint =
|
|
{ hostname, address }:
|
|
''
|
|
[${hostname}](internal_endpoint)
|
|
aors=${hostname}
|
|
|
|
[${hostname}](ip_auth)
|
|
endpoint=${hostname}
|
|
match=[${address}]
|
|
|
|
[${hostname}](dynamiic_aor)
|
|
contact=sip:[${address}]
|
|
'';
|
|
|
|
genLocalSIPEndpoint =
|
|
{ localNumber }:
|
|
''
|
|
[${localNumber}](internal_endpoint)
|
|
aors=${localNumber}
|
|
auth=${localNumber}
|
|
|
|
[${localNumber}](userpass_auth)
|
|
username=${localNumber}
|
|
password=${localNumber}
|
|
|
|
[${localNumber}](dynamiic_aor)
|
|
max_contacts=1
|
|
'';
|
|
|
|
genLocalExtenConf =
|
|
{ localNumber }:
|
|
''
|
|
exten => ${localNumber},1,Dial(PJSIP/${localNumber},20)
|
|
'';
|
|
genExtentConf =
|
|
{ prefixNumber, hostname }:
|
|
''
|
|
exten => _${prefixNumber}XXX,1,Dial(PJSIP/''${EXTEN:1}@${hostname},30)
|
|
'';
|
|
|
|
getYggdrasilIP =
|
|
machineName:
|
|
if config.clan.core.vars.generators.yggdrasil.files.address ? value then
|
|
clanLib.getPublicValue {
|
|
flake = config.clan.core.settings.directory;
|
|
machine = machineName;
|
|
generator = "yggdrasil";
|
|
file = "address";
|
|
default = null;
|
|
}
|
|
else
|
|
throw "clanService/yggdrasil is required";
|
|
in
|
|
{
|
|
clan.core.vars.generators.phonebox = {
|
|
files = {
|
|
server-prefix-number.secret = false;
|
|
ata-local-number.secret = false;
|
|
ata-password = {
|
|
owner = user;
|
|
group = user;
|
|
secret = true;
|
|
};
|
|
};
|
|
|
|
prompts = {
|
|
server-prefix-number = {
|
|
persist = true;
|
|
type = "line";
|
|
description = "Server prefix number: the first number of [X000]";
|
|
};
|
|
ata-local-number = {
|
|
persist = true;
|
|
type = "line";
|
|
description = "Local suffix number: 3 last number of [0XXX]";
|
|
};
|
|
ata-password = {
|
|
persist = true;
|
|
type = "hidden";
|
|
description = "Password for SIP registration.";
|
|
};
|
|
};
|
|
|
|
script = ''
|
|
cat $prompts/server-prefix-number > $out/server-prefix-number
|
|
cat $prompts/ata-local-number > $out/ata-local-number
|
|
cat $prompts/ata-password > $out/ata-password
|
|
'';
|
|
};
|
|
|
|
systemd.services.asterisk.serviceConfig = {
|
|
LoadCredential = [
|
|
"sip_password_filepath:${config.clan.core.vars.generators.phonebox.files.ata-password.path}"
|
|
];
|
|
|
|
Environment = [
|
|
"SIP_PASSWORD=%d/sip_password_filepath"
|
|
];
|
|
};
|
|
|
|
networking.interfaces = {
|
|
${ata-interface} = {
|
|
useDHCP = false;
|
|
ipv4.addresses = [
|
|
{
|
|
address = "192.168.254.1";
|
|
prefixLength = 24;
|
|
}
|
|
];
|
|
};
|
|
};
|
|
|
|
services.dnsmasq = {
|
|
enable = true;
|
|
|
|
settings = {
|
|
bind-dynamic = true;
|
|
listen-address = "192.168.254.1";
|
|
# enable-ra = true;
|
|
domain-needed = true;
|
|
domain = "localhost";
|
|
dhcp-range = [
|
|
"192.168.254.100,192.168.254.100,255.255.255.0,24h"
|
|
];
|
|
dhcp-option = [
|
|
"3,192.168.254.1"
|
|
];
|
|
interface = [ ata-interface ];
|
|
};
|
|
};
|
|
|
|
services.nginx = {
|
|
enable = true;
|
|
virtualHosts = {
|
|
"_" = {
|
|
locations."/" = {
|
|
proxyPass = "http://192.168.254.100";
|
|
extraConfig = ''
|
|
client_max_body_size 100M;
|
|
'';
|
|
};
|
|
};
|
|
};
|
|
};
|
|
|
|
networking.firewall.allowedUDPPortRanges = [
|
|
{
|
|
from = rtpPortFrom;
|
|
to = rtpPortTo;
|
|
}
|
|
];
|
|
|
|
networking.firewall.allowedUDPPorts = [
|
|
53
|
|
67
|
|
5060
|
|
];
|
|
networking.firewall.allowedTCPPorts = [
|
|
53
|
|
];
|
|
networking.firewall.interfaces =
|
|
let
|
|
matchAll = if !config.networking.nftables.enable then "zt+" else "zt*";
|
|
in
|
|
{
|
|
"${matchAll}".allowedTCPPorts = [ 80 ];
|
|
};
|
|
|
|
services.asterisk = {
|
|
enable = lib.mkDefault true;
|
|
confFiles =
|
|
let
|
|
machines = lib.attrNames roles.default.machines;
|
|
nodes = builtins.foldl' (
|
|
nodes: name:
|
|
nodes
|
|
++ [
|
|
{
|
|
hostname = name;
|
|
address = getYggdrasilIP name;
|
|
prefixNumber = clanLib.getPublicValue {
|
|
flake = config.clan.core.settings.directory;
|
|
machine = name;
|
|
generator = "phonebox";
|
|
file = "server-prefix-number";
|
|
default = null;
|
|
};
|
|
|
|
localNumber = clanLib.getPublicValue {
|
|
flake = config.clan.core.settings.directory;
|
|
machine = name;
|
|
generator = "phonebox";
|
|
file = "ata-local-number";
|
|
default = null;
|
|
};
|
|
}
|
|
]
|
|
) [ ] machines;
|
|
in
|
|
{
|
|
"logger.conf" = ''
|
|
[general]
|
|
dateformat = %F %T.%3q ; ISO 8601 date format with milliseconds
|
|
use_callids = yes
|
|
appendhostname = no
|
|
queue_log = yes
|
|
queue_log_to_file = no
|
|
queue_log_name = queue_log
|
|
queue_log_realtime_use_gmt = no
|
|
rotatestrategy = rotate
|
|
exec_after_rotate=gzip -9 $\{filename\}.2
|
|
[logfiles]
|
|
console => notice,warning,error
|
|
security => security
|
|
messages => notice,warning,error
|
|
full => notice,warning,error,verbose,dtmf,fax
|
|
syslog.local0 => notice,warning,error
|
|
'';
|
|
|
|
# Dial plan config
|
|
"extensions.conf" =
|
|
let
|
|
serverConf = builtins.foldl' (
|
|
config: node:
|
|
config
|
|
+ (genExtentConf {
|
|
prefixNumber = node.prefixNumber;
|
|
hostname = node.hostname;
|
|
})
|
|
) "" nodes;
|
|
in
|
|
''
|
|
[from-internal]
|
|
exten => 999,1,Answer()
|
|
same => n,Playback(hello-world)
|
|
same => n,Hangup()
|
|
|
|
''
|
|
+ (genLocalExtenConf {
|
|
localNumber = config.clan.core.vars.generators.phonebox.files.ata-local-number.value;
|
|
})
|
|
+ serverConf;
|
|
|
|
"rtp.conf" = ''
|
|
[general]
|
|
rtpstart=${builtins.toString rtpPortFrom}
|
|
rtpend=${builtins.toString rtpPortTo}
|
|
'';
|
|
|
|
"pjsip.conf" =
|
|
let
|
|
serverConf = builtins.foldl' (
|
|
conf: node:
|
|
conf
|
|
+ (genServerSIPEndpoint {
|
|
hostname = node.hostname;
|
|
address = node.address;
|
|
})
|
|
) "" nodes;
|
|
in
|
|
''
|
|
[transport-udp]
|
|
type=transport
|
|
protocol=udp
|
|
bind=0.0.0.0
|
|
[transport-udp6]
|
|
type=transport
|
|
protocol=udp
|
|
bind=::
|
|
|
|
[base_endpoint](!)
|
|
type=endpoint
|
|
disallow=all
|
|
allow=ulaw,alaw,g722,gsm
|
|
direct_media=no
|
|
|
|
[internal_endpoint](!,base_endpoint)
|
|
context=from-internal
|
|
|
|
[userpass_auth](!)
|
|
type=auth
|
|
auth_type=userpass
|
|
|
|
[ip_auth](!)
|
|
type=identify
|
|
endpoint=external
|
|
|
|
[dynamiic_aor](!)
|
|
type=aor
|
|
|
|
''
|
|
+ (genLocalSIPEndpoint {
|
|
localNumber = config.clan.core.vars.generators.phonebox.files.ata-local-number.value;
|
|
})
|
|
+ serverConf;
|
|
};
|
|
};
|
|
};
|
|
};
|
|
};
|
|
}
|