-
-
Notifications
You must be signed in to change notification settings - Fork 14.7k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
1,157 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,280 @@ | ||
{ | ||
config, | ||
lib, | ||
pkgs, | ||
... | ||
}: | ||
let | ||
cfg = config.services.gancio; | ||
settingsFormat = pkgs.formats.json { }; | ||
inherit (lib) | ||
mkEnableOption | ||
mkPackageOption | ||
mkOption | ||
types | ||
literalExpression | ||
mkIf | ||
optional | ||
mapAttrsToList | ||
concatStringsSep | ||
concatMapStringsSep | ||
getExe | ||
mkMerge | ||
mkDefault | ||
; | ||
in | ||
{ | ||
options.services.gancio = { | ||
enable = mkEnableOption "Gancio, a shared agenda for local communities"; | ||
|
||
package = mkPackageOption pkgs "gancio" { }; | ||
|
||
plugins = mkOption { | ||
type = with types; listOf package; | ||
default = [ ]; | ||
example = literalExpression "[ pkgs.gancioPlugins.telegram-bridge ]"; | ||
description = '' | ||
Paths of gancio plugins to activate (linked under $WorkingDirectory/plugins/). | ||
''; | ||
}; | ||
|
||
user = mkOption { | ||
type = types.str; | ||
description = "The user (and PostgreSQL database name) used to run the gancio server"; | ||
default = "gancio"; | ||
}; | ||
|
||
settings = mkOption rec { | ||
type = types.submodule { | ||
freeformType = settingsFormat.type; | ||
options = { | ||
hostname = mkOption { | ||
type = types.str; | ||
description = "The domain name under which the server is reachable."; | ||
}; | ||
baseurl = mkOption { | ||
type = types.str; | ||
default = ""; | ||
example = "/gancio"; | ||
description = "The URL path under which the server is reachable."; | ||
}; | ||
server = { | ||
host = mkOption { | ||
type = types.str; | ||
default = "localhost"; | ||
example = "::"; | ||
description = '' | ||
The address (IPv4, IPv6 or DNS) for the gancio server to listen on. | ||
''; | ||
}; | ||
port = mkOption { | ||
type = types.port; | ||
default = 13120; | ||
description = '' | ||
Port number of the gancio server to listen on. | ||
''; | ||
}; | ||
}; | ||
db = { | ||
dialect = mkOption { | ||
type = types.enum [ | ||
"sqlite" | ||
"postgres" | ||
]; | ||
default = "sqlite"; | ||
description = '' | ||
The database dialect to use | ||
''; | ||
}; | ||
storage = mkOption { | ||
description = '' | ||
Location for the SQLite database. | ||
''; | ||
readOnly = true; | ||
type = types.nullOr types.str; | ||
default = if cfg.settings.db.dialect == "sqlite" then "/var/lib/gancio/db.sqlite" else null; | ||
defaultText = '' | ||
if cfg.settings.db.dialect == "sqlite" then "/var/lib/gancio/db.sqlite" else null | ||
''; | ||
}; | ||
host = mkOption { | ||
description = '' | ||
Connection string for the PostgreSQL database | ||
''; | ||
readOnly = true; | ||
type = types.nullOr types.str; | ||
default = if cfg.settings.db.dialect == "postgres" then "/run/postgresql" else null; | ||
defaultText = '' | ||
if cfg.settings.db.dialect == "postgres" then "/run/postgresql" else null | ||
''; | ||
}; | ||
database = mkOption { | ||
description = '' | ||
Name of the PostgreSQL database | ||
''; | ||
readOnly = true; | ||
type = types.nullOr types.str; | ||
default = if cfg.settings.db.dialect == "postgres" then cfg.user else null; | ||
defaultText = '' | ||
if cfg.settings.db.dialect == "postgres" then cfg.user else null | ||
''; | ||
}; | ||
}; | ||
log_level = mkOption { | ||
description = "Gancio log level."; | ||
type = types.enum [ | ||
"debug" | ||
"info" | ||
"warning" | ||
"error" | ||
]; | ||
default = "info"; | ||
}; | ||
# FIXME upstream proper journald logging | ||
log_path = mkOption { | ||
description = "Directory Gancio logs into"; | ||
readOnly = true; | ||
type = types.str; | ||
default = "/var/log/gancio"; | ||
}; | ||
}; | ||
}; | ||
description = '' | ||
Configuration for Gancio, see <https://gancio.org/install/config> for supported values. | ||
''; | ||
}; | ||
|
||
userLocale = mkOption { | ||
type = with types; attrsOf (attrsOf (attrsOf str)); | ||
default = { }; | ||
example = { | ||
en.register.description = "My new registration page description"; | ||
}; | ||
description = '' | ||
Override default locales within gancio. | ||
See [https://framagit.org/les/gancio/tree/master/locales](default languages and locales). | ||
''; | ||
}; | ||
|
||
nginx = mkOption { | ||
type = types.submodule (import ../web-servers/nginx/vhost-options.nix { inherit config lib; }); | ||
default = { }; | ||
example = { | ||
enableACME = true; | ||
forceSSL = true; | ||
}; | ||
description = "Extra configuration for the nginx virtual host of gancio."; | ||
}; | ||
}; | ||
|
||
config = mkIf cfg.enable { | ||
environment.systemPackages = [ cfg.package ]; | ||
|
||
users.users.gancio = lib.mkIf (cfg.user == "gancio") { | ||
isSystemUser = true; | ||
group = cfg.user; | ||
home = "/var/lib/gancio"; | ||
}; | ||
users.groups.gancio = lib.mkIf (cfg.user == "gancio") { }; | ||
|
||
systemd.tmpfiles.settings."10-gancio" = | ||
let | ||
rules = { | ||
mode = "0755"; | ||
user = cfg.user; | ||
group = config.users.users.${cfg.user}.group; | ||
}; | ||
in | ||
{ | ||
"/var/lib/gancio/user_locale".d = rules; | ||
"/var/lib/gancio/plugins".d = rules; | ||
}; | ||
|
||
systemd.services.gancio = | ||
let | ||
configFile = settingsFormat.generate "gancio-config.json" cfg.settings; | ||
in | ||
{ | ||
description = "Gancio server"; | ||
documentation = [ "https://gancio.org/" ]; | ||
|
||
wantedBy = [ "multi-user.target" ]; | ||
after = [ | ||
"network.target" | ||
] ++ optional (cfg.settings.db.dialect == "postgres") "postgresql.service"; | ||
|
||
environment = { | ||
NODE_ENV = "production"; | ||
}; | ||
|
||
preStart = '' | ||
# We need this so the gancio executable run by the user finds the right settings. | ||
ln -sf ${configFile} config.json | ||
rm -f user_locale/* | ||
${concatStringsSep "\n" ( | ||
mapAttrsToList ( | ||
l: c: "ln -sf ${settingsFormat.generate "gancio-${l}-locale.json" c} user_locale/${l}.json" | ||
) cfg.userLocale | ||
)} | ||
rm -f plugins/* | ||
${concatMapStringsSep "\n" (p: "ln -sf ${p} plugins/") cfg.plugins} | ||
''; | ||
|
||
serviceConfig = { | ||
ExecStart = "${getExe cfg.package} start ${configFile}"; | ||
StateDirectory = "gancio"; | ||
WorkingDirectory = "/var/lib/gancio"; | ||
LogsDirectory = "gancio"; | ||
User = cfg.user; | ||
# hardening | ||
RestrictRealtime = true; | ||
RestrictNamespaces = true; | ||
LockPersonality = true; | ||
ProtectKernelModules = true; | ||
ProtectKernelTunables = true; | ||
ProtectKernelLogs = true; | ||
ProtectControlGroups = true; | ||
ProtectClock = true; | ||
RestrictSUIDSGID = true; | ||
SystemCallArchitectures = "native"; | ||
CapabilityBoundingSet = ""; | ||
ProtectProc = "invisible"; | ||
}; | ||
}; | ||
|
||
services.postgresql = mkIf (cfg.settings.db.dialect == "postgres") { | ||
enable = true; | ||
ensureDatabases = [ cfg.user ]; | ||
ensureUsers = [ | ||
{ | ||
name = cfg.user; | ||
ensureDBOwnership = true; | ||
} | ||
]; | ||
}; | ||
|
||
services.nginx = { | ||
enable = true; | ||
virtualHosts."${cfg.settings.hostname}" = mkMerge [ | ||
cfg.nginx | ||
{ | ||
enableACME = mkDefault true; | ||
forceSSL = mkDefault true; | ||
locations = { | ||
"/" = { | ||
index = "index.html"; | ||
tryFiles = "$uri $uri @proxy"; | ||
}; | ||
"@proxy" = { | ||
proxyWebsockets = true; | ||
proxyPass = "http://${cfg.settings.server.host}:${toString cfg.settings.server.port}"; | ||
recommendedProxySettings = true; | ||
}; | ||
}; | ||
} | ||
]; | ||
}; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
import ./make-test-python.nix ( | ||
{ pkgs, ... }: | ||
let | ||
extraHosts = '' | ||
192.168.13.12 agenda.example.com | ||
''; | ||
in | ||
{ | ||
name = "gancio"; | ||
meta.maintainers = with pkgs.lib.maintainers; [ jbgi ]; | ||
|
||
nodes = { | ||
server = | ||
{ pkgs, ... }: | ||
{ | ||
networking = { | ||
interfaces.eth1 = { | ||
ipv4.addresses = [ | ||
{ | ||
address = "192.168.13.12"; | ||
prefixLength = 24; | ||
} | ||
]; | ||
}; | ||
inherit extraHosts; | ||
firewall.allowedTCPPorts = [ 80 ]; | ||
}; | ||
environment.systemPackages = [ pkgs.gancio ]; | ||
services.gancio = { | ||
enable = true; | ||
settings = { | ||
hostname = "agenda.example.com"; | ||
db.dialect = "postgres"; | ||
}; | ||
plugins = [ pkgs.gancioPlugins.telegram-bridge ]; | ||
userLocale = { | ||
en = { | ||
register = { | ||
description = "My new registration page description"; | ||
}; | ||
}; | ||
}; | ||
nginx = { | ||
enableACME = false; | ||
forceSSL = false; | ||
}; | ||
}; | ||
}; | ||
|
||
client = | ||
{ pkgs, ... }: | ||
{ | ||
environment.systemPackages = [ pkgs.jq ]; | ||
networking = { | ||
interfaces.eth1 = { | ||
ipv4.addresses = [ | ||
{ | ||
address = "192.168.13.1"; | ||
prefixLength = 24; | ||
} | ||
]; | ||
}; | ||
inherit extraHosts; | ||
}; | ||
}; | ||
}; | ||
|
||
testScript = '' | ||
start_all() | ||
server.wait_for_unit("postgresql") | ||
server.wait_for_unit("gancio") | ||
server.wait_for_unit("nginx") | ||
server.wait_for_open_port(13120) | ||
server.wait_for_open_port(80) | ||
# Check can create user via cli | ||
server.succeed("cd /var/lib/gancio && sudo -u gancio gancio users create admin dummy admin") | ||
# Check event list is returned | ||
client.wait_until_succeeds("curl --verbose --fail-with-body http://agenda.example.com/api/events", timeout=30) | ||
server.shutdown() | ||
client.shutdown() | ||
''; | ||
} | ||
) |
Oops, something went wrong.