mirror of
https://github.com/NixOS/nixpkgs.git
synced 2025-06-09 17:46:29 +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";
|
||||
}
|
||||
.${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
|
||||
{
|
||||
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 {
|
||||
type = types.nullOr (
|
||||
types.submodule (import ../web-servers/nginx/vhost-options.nix { inherit config lib; })
|
||||
|
@ -515,6 +571,25 @@ in
|
|||
};
|
||||
|
||||
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 ];
|
||||
|
||||
users = {
|
||||
|
@ -525,6 +600,9 @@ in
|
|||
group = cfg.group;
|
||||
};
|
||||
}
|
||||
// lib.optionalAttrs (cfg.h2o != null) {
|
||||
"${config.services.h2o.user}".extraGroups = [ cfg.group ];
|
||||
}
|
||||
// lib.optionalAttrs (cfg.nginx != null) {
|
||||
"${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) (
|
||||
{
|
||||
enable = true;
|
||||
|
@ -624,8 +747,7 @@ in
|
|||
tryFiles = "$uri $uri/ /index.php$is_args$args";
|
||||
extraConfig = # nginx
|
||||
''
|
||||
# https://github.com/movim/movim/issues/314
|
||||
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';";
|
||||
add_header Content-Security-Policy "${movimCSP}";
|
||||
set $no_cache 1;
|
||||
'';
|
||||
};
|
||||
|
@ -699,11 +821,7 @@ in
|
|||
'';
|
||||
};
|
||||
|
||||
phpfpm.pools.${pool} =
|
||||
let
|
||||
socketOwner = if (cfg.nginx != null) then config.services.nginx.user else cfg.user;
|
||||
in
|
||||
{
|
||||
phpfpm.pools.${pool} = {
|
||||
phpPackage = package.php;
|
||||
user = cfg.user;
|
||||
group = cfg.group;
|
||||
|
@ -781,9 +899,9 @@ in
|
|||
};
|
||||
|
||||
services.${phpExecutionUnit} = {
|
||||
wantedBy = lib.optional (cfg.nginx != null) "nginx.service";
|
||||
wantedBy = lib.optional (webServerService != null) webServerService;
|
||||
requiredBy = [ "movim.service" ];
|
||||
before = [ "movim.service" ] ++ lib.optional (cfg.nginx != null) "nginx.service";
|
||||
before = [ "movim.service" ] ++ lib.optional (webServerService != null) webServerService;
|
||||
wants = [ "network.target" ];
|
||||
requires = [ "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"
|
||||
]
|
||||
++ lib.optional cfg.database.createLocally dbService
|
||||
++ lib.optional (cfg.nginx != null) "nginx.service";
|
||||
++ lib.optional (webServerService != null) webServerService;
|
||||
after =
|
||||
[
|
||||
"movim-data-setup.service"
|
||||
"${phpExecutionUnit}.service"
|
||||
]
|
||||
++ lib.optional cfg.database.createLocally dbService
|
||||
++ lib.optional (cfg.nginx != null) "nginx.service";
|
||||
++ lib.optional (webServerService != null) webServerService;
|
||||
environment = {
|
||||
PUBLIC_URL = "//${cfg.domain}";
|
||||
WS_PORT = builtins.toString cfg.port;
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
{ recurseIntoAttrs, runTest }:
|
||||
|
||||
recurseIntoAttrs {
|
||||
ejabberd-h2o = runTest ./ejabberd-h2o.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