mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-10 01:53:09 +09:00
nixos/movim: add H2O support + testing with ejabberd
This commit is contained in:
parent
585b1bbffa
commit
8a8b892cc1
3 changed files with 418 additions and 25 deletions
|
@ -175,6 +175,37 @@ let
|
||||||
"mysql" = "mysql.service";
|
"mysql" = "mysql.service";
|
||||||
}
|
}
|
||||||
.${cfg.database.type};
|
.${cfg.database.type};
|
||||||
|
|
||||||
|
# exclusivity asserted in `assertions`
|
||||||
|
webServerService =
|
||||||
|
if cfg.h2o != null then
|
||||||
|
"h2o.service"
|
||||||
|
else if cfg.nginx != null then
|
||||||
|
"nginx.service"
|
||||||
|
else
|
||||||
|
null;
|
||||||
|
|
||||||
|
socketOwner =
|
||||||
|
if cfg.h2o != null then
|
||||||
|
config.services.h2o.user
|
||||||
|
else if cfg.nginx != null then
|
||||||
|
config.services.nginx.user
|
||||||
|
else
|
||||||
|
cfg.user;
|
||||||
|
|
||||||
|
# Movim needs a lot of unsafe values to function at this time. Perhaps if
|
||||||
|
# this is ever addressed in the future, the PHP application will send up the
|
||||||
|
# proper directive. For now this fairly conservative CSP will restrict a lot
|
||||||
|
# of potentially bad stuff as well as take in inventory of the features used.
|
||||||
|
#
|
||||||
|
# See: https://github.com/movim/movim/issues/314
|
||||||
|
movimCSP = lib.concatStringsSep "; " [
|
||||||
|
"default-src 'self'"
|
||||||
|
"img-src 'self' aesgcm: data: https:"
|
||||||
|
"media-src 'self' aesgcm: https:"
|
||||||
|
"script-src 'self' 'unsafe-eval' 'unsafe-inline'"
|
||||||
|
"style-src 'self' 'unsafe-inline'"
|
||||||
|
];
|
||||||
in
|
in
|
||||||
{
|
{
|
||||||
options.services = {
|
options.services = {
|
||||||
|
@ -475,6 +506,31 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
h2o = mkOption {
|
||||||
|
type = types.nullOr (
|
||||||
|
types.submodule (import ../web-servers/h2o/vhost-options.nix { inherit config lib; })
|
||||||
|
);
|
||||||
|
default = null;
|
||||||
|
example =
|
||||||
|
lib.literalExpression # nix
|
||||||
|
''
|
||||||
|
{
|
||||||
|
serverAliases = [
|
||||||
|
"pics.''${config.movim.domain}"
|
||||||
|
];
|
||||||
|
acme.enable = true;
|
||||||
|
tls.policy = "force";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
description = ''
|
||||||
|
With this option, you can customize an H2O virtual host which already
|
||||||
|
has sensible defaults for Movim. Set to `{ }` if you do not need any
|
||||||
|
customization to the virtual host. If enabled, then by default, the
|
||||||
|
{option}`serverName` is `''${domain}`, If this is set to `null` (the
|
||||||
|
default), no H2O `hosts` will be configured.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
nginx = mkOption {
|
nginx = mkOption {
|
||||||
type = types.nullOr (
|
type = types.nullOr (
|
||||||
types.submodule (import ../web-servers/nginx/vhost-options.nix { inherit config lib; })
|
types.submodule (import ../web-servers/nginx/vhost-options.nix { inherit config lib; })
|
||||||
|
@ -515,6 +571,25 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkIf cfg.enable {
|
config = mkIf cfg.enable {
|
||||||
|
assertions = [
|
||||||
|
(
|
||||||
|
let
|
||||||
|
webServers = [
|
||||||
|
"h2o"
|
||||||
|
"nginx"
|
||||||
|
];
|
||||||
|
checkConfigs = lib.concatMapStringsSep ", " (ws: "services.movim.${ws}") webServers;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
assertion = builtins.length (lib.lists.filter (ws: cfg.${ws} != null) webServers) <= 1;
|
||||||
|
message = ''
|
||||||
|
At most 1 web server virtual host configuration should be enabled
|
||||||
|
for Movim at a time. Check ${checkConfigs}.
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
)
|
||||||
|
];
|
||||||
|
|
||||||
environment.systemPackages = [ package ];
|
environment.systemPackages = [ package ];
|
||||||
|
|
||||||
users = {
|
users = {
|
||||||
|
@ -525,6 +600,9 @@ in
|
||||||
group = cfg.group;
|
group = cfg.group;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
// lib.optionalAttrs (cfg.h2o != null) {
|
||||||
|
"${config.services.h2o.user}".extraGroups = [ cfg.group ];
|
||||||
|
}
|
||||||
// lib.optionalAttrs (cfg.nginx != null) {
|
// lib.optionalAttrs (cfg.nginx != null) {
|
||||||
"${config.services.nginx.user}".extraGroups = [ cfg.group ];
|
"${config.services.nginx.user}".extraGroups = [ cfg.group ];
|
||||||
};
|
};
|
||||||
|
@ -571,6 +649,51 @@ in
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
h2o = mkIf (cfg.h2o != null) {
|
||||||
|
enable = true;
|
||||||
|
hosts."${cfg.domain}" = mkMerge [
|
||||||
|
{
|
||||||
|
settings = {
|
||||||
|
paths = {
|
||||||
|
"/ws/" = {
|
||||||
|
"proxy.preserve-host" = "ON";
|
||||||
|
"proxy.tunnel" = "ON";
|
||||||
|
"proxy.reverse.url" = "http://${cfg.settings.DAEMON_INTERFACE}:${builtins.toString cfg.port}/";
|
||||||
|
};
|
||||||
|
"/" =
|
||||||
|
{
|
||||||
|
"file.dir" = "${package}/share/php/movim/public";
|
||||||
|
"file.index" = [
|
||||||
|
"index.php"
|
||||||
|
"index.html"
|
||||||
|
];
|
||||||
|
redirect = {
|
||||||
|
url = "/index.php/";
|
||||||
|
internal = "YES";
|
||||||
|
status = 307;
|
||||||
|
};
|
||||||
|
"header.set" = [
|
||||||
|
"Content-Security-Policy: ${movimCSP}"
|
||||||
|
];
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (with cfg.precompressStaticFiles; brotli.enable || gzip.enable) {
|
||||||
|
"file.send-compressed" = "ON";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
"file.custom-handler" = {
|
||||||
|
extension = [ ".php" ];
|
||||||
|
"fastcgi.document_root" = package;
|
||||||
|
"fastcgi.connect" = {
|
||||||
|
port = fpm.socket;
|
||||||
|
type = "unix";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
cfg.h2o
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
nginx = mkIf (cfg.nginx != null) (
|
nginx = mkIf (cfg.nginx != null) (
|
||||||
{
|
{
|
||||||
enable = true;
|
enable = true;
|
||||||
|
@ -624,8 +747,7 @@ in
|
||||||
tryFiles = "$uri $uri/ /index.php$is_args$args";
|
tryFiles = "$uri $uri/ /index.php$is_args$args";
|
||||||
extraConfig = # nginx
|
extraConfig = # nginx
|
||||||
''
|
''
|
||||||
# https://github.com/movim/movim/issues/314
|
add_header Content-Security-Policy "${movimCSP}";
|
||||||
add_header Content-Security-Policy "default-src 'self'; img-src 'self' aesgcm: https:; media-src 'self' aesgcm: https:; script-src 'self' 'unsafe-eval' 'unsafe-inline'; style-src 'self' 'unsafe-inline';";
|
|
||||||
set $no_cache 1;
|
set $no_cache 1;
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
@ -699,11 +821,7 @@ in
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
|
|
||||||
phpfpm.pools.${pool} =
|
phpfpm.pools.${pool} = {
|
||||||
let
|
|
||||||
socketOwner = if (cfg.nginx != null) then config.services.nginx.user else cfg.user;
|
|
||||||
in
|
|
||||||
{
|
|
||||||
phpPackage = package.php;
|
phpPackage = package.php;
|
||||||
user = cfg.user;
|
user = cfg.user;
|
||||||
group = cfg.group;
|
group = cfg.group;
|
||||||
|
@ -781,9 +899,9 @@ in
|
||||||
};
|
};
|
||||||
|
|
||||||
services.${phpExecutionUnit} = {
|
services.${phpExecutionUnit} = {
|
||||||
wantedBy = lib.optional (cfg.nginx != null) "nginx.service";
|
wantedBy = lib.optional (webServerService != null) webServerService;
|
||||||
requiredBy = [ "movim.service" ];
|
requiredBy = [ "movim.service" ];
|
||||||
before = [ "movim.service" ] ++ lib.optional (cfg.nginx != null) "nginx.service";
|
before = [ "movim.service" ] ++ lib.optional (webServerService != null) webServerService;
|
||||||
wants = [ "network.target" ];
|
wants = [ "network.target" ];
|
||||||
requires = [ "movim-data-setup.service" ] ++ lib.optional cfg.database.createLocally dbService;
|
requires = [ "movim-data-setup.service" ] ++ lib.optional cfg.database.createLocally dbService;
|
||||||
after = [ "movim-data-setup.service" ] ++ lib.optional cfg.database.createLocally dbService;
|
after = [ "movim-data-setup.service" ] ++ lib.optional cfg.database.createLocally dbService;
|
||||||
|
@ -802,14 +920,14 @@ in
|
||||||
"${phpExecutionUnit}.service"
|
"${phpExecutionUnit}.service"
|
||||||
]
|
]
|
||||||
++ lib.optional cfg.database.createLocally dbService
|
++ lib.optional cfg.database.createLocally dbService
|
||||||
++ lib.optional (cfg.nginx != null) "nginx.service";
|
++ lib.optional (webServerService != null) webServerService;
|
||||||
after =
|
after =
|
||||||
[
|
[
|
||||||
"movim-data-setup.service"
|
"movim-data-setup.service"
|
||||||
"${phpExecutionUnit}.service"
|
"${phpExecutionUnit}.service"
|
||||||
]
|
]
|
||||||
++ lib.optional cfg.database.createLocally dbService
|
++ lib.optional cfg.database.createLocally dbService
|
||||||
++ lib.optional (cfg.nginx != null) "nginx.service";
|
++ lib.optional (webServerService != null) webServerService;
|
||||||
environment = {
|
environment = {
|
||||||
PUBLIC_URL = "//${cfg.domain}";
|
PUBLIC_URL = "//${cfg.domain}";
|
||||||
WS_PORT = builtins.toString cfg.port;
|
WS_PORT = builtins.toString cfg.port;
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
{ recurseIntoAttrs, runTest }:
|
{ recurseIntoAttrs, runTest }:
|
||||||
|
|
||||||
recurseIntoAttrs {
|
recurseIntoAttrs {
|
||||||
|
ejabberd-h2o = runTest ./ejabberd-h2o.nix;
|
||||||
prosody-nginx = runTest ./prosody-nginx.nix;
|
prosody-nginx = runTest ./prosody-nginx.nix;
|
||||||
}
|
}
|
||||||
|
|
274
nixos/tests/web-apps/movim/ejabberd-h2o.nix
Normal file
274
nixos/tests/web-apps/movim/ejabberd-h2o.nix
Normal file
|
@ -0,0 +1,274 @@
|
||||||
|
{ hostPkgs, lib, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
movim = {
|
||||||
|
domain = "movim.local";
|
||||||
|
port = 8080;
|
||||||
|
info = "No ToS in tests";
|
||||||
|
description = "NixOS testing server";
|
||||||
|
};
|
||||||
|
ejabberd = {
|
||||||
|
domain = "ejabberd.local";
|
||||||
|
ports = {
|
||||||
|
c2s = 5222;
|
||||||
|
s2s = 5269;
|
||||||
|
http = 5280;
|
||||||
|
};
|
||||||
|
spoolDir = "/var/lib/ejabberd";
|
||||||
|
admin = rec {
|
||||||
|
JID = "${username}@${ejabberd.domain}";
|
||||||
|
username = "romeo";
|
||||||
|
password = "juliet";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# START OF EJABBERD CONFIG ##################################################
|
||||||
|
#
|
||||||
|
# Ejabberd has sparse defaults as it is a generic XMPP server. As such this
|
||||||
|
# config might be longer than expected for a test.
|
||||||
|
#
|
||||||
|
# Movim suggests: https://github.com/movim/movim/wiki/Configure ejabberd
|
||||||
|
#
|
||||||
|
# In the future this may be the default setup
|
||||||
|
# See: https://github.com/NixOS/nixpkgs/pull/312316
|
||||||
|
ejabberd_config_file =
|
||||||
|
let
|
||||||
|
settingsFormat = hostPkgs.formats.yaml { };
|
||||||
|
in
|
||||||
|
settingsFormat.generate "ejabberd.yml" {
|
||||||
|
loglevel = "info";
|
||||||
|
hide_sensitive_log_data = false;
|
||||||
|
hosts = [ ejabberd.domain ];
|
||||||
|
default_db = "mnesia";
|
||||||
|
acme.auto = false;
|
||||||
|
s2s_access = "s2s";
|
||||||
|
s2s_use_starttls = false;
|
||||||
|
new_sql_schema = true;
|
||||||
|
acl = {
|
||||||
|
admin = [
|
||||||
|
{ user = ejabberd.admin.JID; }
|
||||||
|
];
|
||||||
|
local.user_regexp = "";
|
||||||
|
loopback.ip = [
|
||||||
|
"127.0.0.1/8"
|
||||||
|
"::1/128"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
access_rules = {
|
||||||
|
c2s = {
|
||||||
|
deny = "blocked";
|
||||||
|
allow = "all";
|
||||||
|
};
|
||||||
|
s2s = {
|
||||||
|
allow = "all";
|
||||||
|
};
|
||||||
|
local.allow = "local";
|
||||||
|
announce.allow = "admin";
|
||||||
|
configure.allow = "admin";
|
||||||
|
pubsub_createnode.allow = "local";
|
||||||
|
trusted_network.allow = "loopback";
|
||||||
|
};
|
||||||
|
api_permissions = {
|
||||||
|
"console commands" = {
|
||||||
|
from = [ "ejabberd_ctl" ];
|
||||||
|
who = "all";
|
||||||
|
what = "*";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
shaper = {
|
||||||
|
normal = {
|
||||||
|
rate = 3000;
|
||||||
|
burst_size = 20000;
|
||||||
|
};
|
||||||
|
fast = 100000;
|
||||||
|
};
|
||||||
|
modules = {
|
||||||
|
mod_caps = { };
|
||||||
|
mod_disco = { };
|
||||||
|
mod_mam = { };
|
||||||
|
mod_http_upload = {
|
||||||
|
docroot = "${ejabberd.spoolDir}/uploads";
|
||||||
|
dir_mode = "0755";
|
||||||
|
file_mode = "0644";
|
||||||
|
get_url = "http://@HOST@/upload";
|
||||||
|
put_url = "http://@HOST@/upload";
|
||||||
|
max_size = 65536;
|
||||||
|
custom_headers = {
|
||||||
|
Access-Control-Allow-Origin = "http://@HOST@,http://${movim.domain}";
|
||||||
|
Access-Control-Allow-Methods = "GET,HEAD,PUT,OPTIONS";
|
||||||
|
Access-Control-Allow-Headers = "Content-Type";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
# This PubSub block is required for Movim to work.
|
||||||
|
#
|
||||||
|
# See: https://github.com/movim/movim/wiki/Configure ejabberd#pubsub
|
||||||
|
mod_pubsub = {
|
||||||
|
hosts = [ "pubsub.@HOST@" ];
|
||||||
|
access_createnode = "pubsub_createnode";
|
||||||
|
ignore_pep_from_offline = false;
|
||||||
|
last_item_cache = false;
|
||||||
|
max_items_node = 2048;
|
||||||
|
default_node_config = {
|
||||||
|
max_items = 2048;
|
||||||
|
};
|
||||||
|
plugins = [
|
||||||
|
"flat"
|
||||||
|
"pep"
|
||||||
|
];
|
||||||
|
force_node_config = {
|
||||||
|
"storage:bookmarks".access_model = "whitelist";
|
||||||
|
"eu.siacs.conversations.axolotl.*".access_model = "open";
|
||||||
|
"urn:xmpp:bookmarks:0" = {
|
||||||
|
access_model = "whitelist";
|
||||||
|
send_last_published_item = "never";
|
||||||
|
max_items = "infinity";
|
||||||
|
persist_items = true;
|
||||||
|
};
|
||||||
|
"urn:xmpp:bookmarks:1" = {
|
||||||
|
access_model = "whitelist";
|
||||||
|
send_last_published_item = "never";
|
||||||
|
max_items = "infinity";
|
||||||
|
persist_items = true;
|
||||||
|
};
|
||||||
|
"urn:xmpp:pubsub:movim-public-subscription" = {
|
||||||
|
access_model = "whitelist";
|
||||||
|
max_items = "infinity";
|
||||||
|
persist_items = true;
|
||||||
|
};
|
||||||
|
"urn:xmpp:microblog:0" = {
|
||||||
|
notify_retract = true;
|
||||||
|
max_items = "infinity";
|
||||||
|
persist_items = true;
|
||||||
|
};
|
||||||
|
"urn:xmpp:microblog:0:comments*" = {
|
||||||
|
access_model = "open";
|
||||||
|
notify_retract = true;
|
||||||
|
max_items = "infinity";
|
||||||
|
persist_items = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
mod_stream_mgmt = { };
|
||||||
|
};
|
||||||
|
listen = [
|
||||||
|
{
|
||||||
|
module = "ejabberd_c2s";
|
||||||
|
port = ejabberd.ports.c2s;
|
||||||
|
max_stanza_size = 262144;
|
||||||
|
access = "c2s";
|
||||||
|
starttls_required = false;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
module = "ejabberd_s2s_in";
|
||||||
|
port = ejabberd.ports.s2s;
|
||||||
|
max_stanza_size = 524288;
|
||||||
|
shaper = "fast";
|
||||||
|
}
|
||||||
|
{
|
||||||
|
module = "ejabberd_http";
|
||||||
|
port = ejabberd.ports.http;
|
||||||
|
request_handlers = {
|
||||||
|
"/upload" = "mod_http_upload";
|
||||||
|
};
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
# END OF EJABBERD CONFIG ##################################################
|
||||||
|
in
|
||||||
|
{
|
||||||
|
name = "movim-ejabberd-h2o";
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
maintainers = with lib.maintainers; [ toastal ];
|
||||||
|
};
|
||||||
|
|
||||||
|
nodes = {
|
||||||
|
server =
|
||||||
|
{ pkgs, ... }:
|
||||||
|
{
|
||||||
|
environment.systemPackages = [
|
||||||
|
# For testing
|
||||||
|
pkgs.websocat
|
||||||
|
];
|
||||||
|
|
||||||
|
services.movim = {
|
||||||
|
inherit (movim) domain port;
|
||||||
|
enable = true;
|
||||||
|
verbose = true;
|
||||||
|
podConfig = {
|
||||||
|
inherit (movim) description info;
|
||||||
|
xmppdomain = ejabberd.domain;
|
||||||
|
};
|
||||||
|
database = {
|
||||||
|
type = "postgresql";
|
||||||
|
createLocally = true;
|
||||||
|
};
|
||||||
|
h2o = { };
|
||||||
|
};
|
||||||
|
|
||||||
|
services.ejabberd = {
|
||||||
|
inherit (ejabberd) spoolDir;
|
||||||
|
enable = true;
|
||||||
|
configFile = ejabberd_config_file;
|
||||||
|
imagemagick = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
services.h2o.settings = {
|
||||||
|
compress = "ON";
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.ejabberd = {
|
||||||
|
serviceConfig = {
|
||||||
|
# Certain misconfigurations can cause RAM usage to swell before
|
||||||
|
# crashing; fail sooner with more-than-liberal memory limits
|
||||||
|
StartupMemoryMax = "1G";
|
||||||
|
MemoryMax = "512M";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking = {
|
||||||
|
firewall.allowedTCPPorts = with ejabberd.ports; [
|
||||||
|
c2s
|
||||||
|
s2s
|
||||||
|
];
|
||||||
|
extraHosts = ''
|
||||||
|
127.0.0.1 ${movim.domain}
|
||||||
|
127.0.0.1 ${ejabberd.domain}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = # python
|
||||||
|
''
|
||||||
|
ejabberdctl = "su ejabberd -s $(which ejabberdctl) "
|
||||||
|
|
||||||
|
server.wait_for_unit("phpfpm-movim.service")
|
||||||
|
server.wait_for_unit("h2o.service")
|
||||||
|
server.wait_for_open_port(${builtins.toString movim.port})
|
||||||
|
server.wait_for_open_port(80)
|
||||||
|
|
||||||
|
server.wait_for_unit("ejabberd.service")
|
||||||
|
ejabberd_status = server.succeed(ejabberdctl + "status")
|
||||||
|
assert "status: started" in ejabberd_status
|
||||||
|
server.succeed(ejabberdctl + "register ${ejabberd.admin.username} ${ejabberd.domain} ${ejabberd.admin.password}")
|
||||||
|
|
||||||
|
server.wait_for_unit("movim.service")
|
||||||
|
|
||||||
|
# Test unauthenticated
|
||||||
|
server.fail("curl -L --fail-with-body --max-redirs 0 http://${movim.domain}/chat")
|
||||||
|
|
||||||
|
# Test basic Websocket
|
||||||
|
server.succeed("echo | websocat --origin 'http://${movim.domain}' 'ws://${movim.domain}/ws/?path=login&offset=0'")
|
||||||
|
|
||||||
|
# Test login + create cookiejar
|
||||||
|
login_html = server.succeed("curl --fail-with-body -c /tmp/cookies http://${movim.domain}/login")
|
||||||
|
assert "${movim.description}" in login_html
|
||||||
|
assert "${movim.info}" in login_html
|
||||||
|
|
||||||
|
# Test authentication POST
|
||||||
|
server.succeed("curl --fail-with-body -b /tmp/cookies -X POST --data-urlencode 'username=${ejabberd.admin.JID}' --data-urlencode 'password=${ejabberd.admin.password}' http://${movim.domain}/login")
|
||||||
|
|
||||||
|
server.succeed("curl -L --fail-with-body --max-redirs 1 -b /tmp/cookies http://${movim.domain}/chat")
|
||||||
|
'';
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue