at 23.11-pre 12 kB view raw
1{ lib, config, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.onlyoffice; 7in 8{ 9 options.services.onlyoffice = { 10 enable = mkEnableOption (lib.mdDoc "OnlyOffice DocumentServer"); 11 12 enableExampleServer = mkEnableOption (lib.mdDoc "OnlyOffice example server"); 13 14 hostname = mkOption { 15 type = types.str; 16 default = "localhost"; 17 description = lib.mdDoc "FQDN for the onlyoffice instance."; 18 }; 19 20 jwtSecretFile = mkOption { 21 type = types.nullOr types.str; 22 default = null; 23 description = lib.mdDoc '' 24 Path to a file that contains the secret to sign web requests using JSON Web Tokens. 25 If left at the default value null signing is disabled. 26 ''; 27 }; 28 29 package = mkOption { 30 type = types.package; 31 default = pkgs.onlyoffice-documentserver; 32 defaultText = lib.literalExpression "pkgs.onlyoffice-documentserver"; 33 description = lib.mdDoc "Which package to use for the OnlyOffice instance."; 34 }; 35 36 port = mkOption { 37 type = types.port; 38 default = 8000; 39 description = lib.mdDoc "Port the OnlyOffice DocumentServer should listens on."; 40 }; 41 42 examplePort = mkOption { 43 type = types.port; 44 default = null; 45 description = lib.mdDoc "Port the OnlyOffice Example server should listens on."; 46 }; 47 48 postgresHost = mkOption { 49 type = types.str; 50 default = "/run/postgresql"; 51 description = lib.mdDoc "The Postgresql hostname or socket path OnlyOffice should connect to."; 52 }; 53 54 postgresName = mkOption { 55 type = types.str; 56 default = "onlyoffice"; 57 description = lib.mdDoc "The name of database OnlyOffice should user."; 58 }; 59 60 postgresPasswordFile = mkOption { 61 type = types.nullOr types.str; 62 default = null; 63 description = lib.mdDoc '' 64 Path to a file that contains the password OnlyOffice should use to connect to Postgresql. 65 Unused when using socket authentication. 66 ''; 67 }; 68 69 postgresUser = mkOption { 70 type = types.str; 71 default = "onlyoffice"; 72 description = lib.mdDoc '' 73 The username OnlyOffice should use to connect to Postgresql. 74 Unused when using socket authentication. 75 ''; 76 }; 77 78 rabbitmqUrl = mkOption { 79 type = types.str; 80 default = "amqp://guest:guest@localhost:5672"; 81 description = lib.mdDoc "The Rabbitmq in amqp URI style OnlyOffice should connect to."; 82 }; 83 }; 84 85 config = lib.mkIf cfg.enable { 86 services = { 87 nginx = { 88 enable = mkDefault true; 89 # misses text/csv, font/ttf, application/x-font-ttf, application/rtf, application/wasm 90 recommendedGzipSettings = mkDefault true; 91 recommendedProxySettings = mkDefault true; 92 93 upstreams = { 94 # /etc/nginx/includes/http-common.conf 95 onlyoffice-docservice = { 96 servers = { "localhost:${toString cfg.port}" = { }; }; 97 }; 98 onlyoffice-example = lib.mkIf cfg.enableExampleServer { 99 servers = { "localhost:${toString cfg.examplePort}" = { }; }; 100 }; 101 }; 102 103 virtualHosts.${cfg.hostname} = { 104 locations = { 105 # /etc/nginx/includes/ds-docservice.conf 106 "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps\/apps\/api\/documents\/api\.js)$".extraConfig = '' 107 expires -1; 108 alias ${cfg.package}/var/www/onlyoffice/documentserver/$2; 109 ''; 110 "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps)(\/.*\.json)$".extraConfig = '' 111 expires 365d; 112 error_log /dev/null crit; 113 alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3; 114 ''; 115 "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(sdkjs-plugins)(\/.*\.json)$".extraConfig = '' 116 expires 365d; 117 error_log /dev/null crit; 118 alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3; 119 ''; 120 "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(web-apps|sdkjs|sdkjs-plugins|fonts)(\/.*)$".extraConfig = '' 121 expires 365d; 122 alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3; 123 ''; 124 "~* ^(\/cache\/files.*)(\/.*)".extraConfig = '' 125 alias /var/lib/onlyoffice/documentserver/App_Data$1; 126 add_header Content-Disposition "attachment; filename*=UTF-8''$arg_filename"; 127 128 set $secret_string verysecretstring; 129 secure_link $arg_md5,$arg_expires; 130 secure_link_md5 "$secure_link_expires$uri$secret_string"; 131 132 if ($secure_link = "") { 133 return 403; 134 } 135 136 if ($secure_link = "0") { 137 return 410; 138 } 139 ''; 140 "~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(internal)(\/.*)$".extraConfig = '' 141 allow 127.0.0.1; 142 deny all; 143 proxy_pass http://onlyoffice-docservice/$2$3; 144 ''; 145 "~* ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(info)(\/.*)$".extraConfig = '' 146 allow 127.0.0.1; 147 deny all; 148 proxy_pass http://onlyoffice-docservice/$2$3; 149 ''; 150 "/".extraConfig = '' 151 proxy_pass http://onlyoffice-docservice; 152 ''; 153 "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?(\/doc\/.*)".extraConfig = '' 154 proxy_pass http://onlyoffice-docservice$2; 155 proxy_http_version 1.1; 156 ''; 157 "/${cfg.package.version}/".extraConfig = '' 158 proxy_pass http://onlyoffice-docservice/; 159 ''; 160 "~ ^(\/[\d]+\.[\d]+\.[\d]+[\.|-][\d]+)?\/(dictionaries)(\/.*)$".extraConfig = '' 161 expires 365d; 162 alias ${cfg.package}/var/www/onlyoffice/documentserver/$2$3; 163 ''; 164 # /etc/nginx/includes/ds-example.conf 165 "~ ^(\/welcome\/.*)$".extraConfig = '' 166 expires 365d; 167 alias ${cfg.package}/var/www/onlyoffice/documentserver-example$1; 168 index docker.html; 169 ''; 170 "/example/".extraConfig = lib.mkIf cfg.enableExampleServer '' 171 proxy_pass http://onlyoffice-example/; 172 proxy_set_header X-Forwarded-Path /example; 173 ''; 174 }; 175 extraConfig = '' 176 rewrite ^/$ /welcome/ redirect; 177 rewrite ^\/OfficeWeb(\/apps\/.*)$ /${cfg.package.version}/web-apps$1 redirect; 178 rewrite ^(\/web-apps\/apps\/(?!api\/).*)$ /${cfg.package.version}$1 redirect; 179 180 # based on https://github.com/ONLYOFFICE/document-server-package/blob/master/common/documentserver/nginx/includes/http-common.conf.m4#L29-L34 181 # without variable indirection and correct variable names 182 proxy_set_header Host $host; 183 proxy_set_header X-Forwarded-Host $host; 184 proxy_set_header X-Forwarded-Proto $scheme; 185 # required for CSP to take effect 186 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 187 # required for websocket 188 proxy_set_header Upgrade $http_upgrade; 189 proxy_set_header Connection $connection_upgrade; 190 ''; 191 }; 192 }; 193 194 rabbitmq.enable = lib.mkDefault true; 195 196 postgresql = { 197 enable = lib.mkDefault true; 198 ensureDatabases = [ "onlyoffice" ]; 199 ensureUsers = [{ 200 name = "onlyoffice"; 201 ensurePermissions = { "DATABASE \"onlyoffice\"" = "ALL PRIVILEGES"; }; 202 }]; 203 }; 204 }; 205 206 systemd.services = { 207 onlyoffice-converter = { 208 description = "onlyoffice converter"; 209 after = [ "network.target" "onlyoffice-docservice.service" "postgresql.service" ]; 210 requires = [ "network.target" "onlyoffice-docservice.service" "postgresql.service" ]; 211 wantedBy = [ "multi-user.target" ]; 212 serviceConfig = { 213 ExecStart = "${cfg.package.fhs}/bin/onlyoffice-wrapper FileConverter/converter /run/onlyoffice/config"; 214 Group = "onlyoffice"; 215 Restart = "always"; 216 RuntimeDirectory = "onlyoffice"; 217 StateDirectory = "onlyoffice"; 218 Type = "simple"; 219 User = "onlyoffice"; 220 }; 221 }; 222 223 onlyoffice-docservice = 224 let 225 onlyoffice-prestart = pkgs.writeShellScript "onlyoffice-prestart" '' 226 PATH=$PATH:${lib.makeBinPath (with pkgs; [ jq moreutils config.services.postgresql.package ])} 227 umask 077 228 mkdir -p /run/onlyoffice/config/ /var/lib/onlyoffice/documentserver/sdkjs/{slide/themes,common}/ /var/lib/onlyoffice/documentserver/{fonts,server/FileConverter/bin}/ 229 cp -r ${cfg.package}/etc/onlyoffice/documentserver/* /run/onlyoffice/config/ 230 chmod u+w /run/onlyoffice/config/default.json 231 232 # Allow members of the onlyoffice group to serve files under /var/lib/onlyoffice/documentserver/App_Data 233 chmod g+x /var/lib/onlyoffice/documentserver 234 235 cp /run/onlyoffice/config/default.json{,.orig} 236 237 # for a mapping of environment variables from the docker container to json options see 238 # https://github.com/ONLYOFFICE/Docker-DocumentServer/blob/master/run-document-server.sh 239 jq ' 240 .services.CoAuthoring.server.port = ${toString cfg.port} | 241 .services.CoAuthoring.sql.dbHost = "${cfg.postgresHost}" | 242 .services.CoAuthoring.sql.dbName = "${cfg.postgresName}" | 243 ${lib.optionalString (cfg.postgresPasswordFile != null) '' 244 .services.CoAuthoring.sql.dbPass = "'"$(cat ${cfg.postgresPasswordFile})"'" | 245 ''} 246 .services.CoAuthoring.sql.dbUser = "${cfg.postgresUser}" | 247 ${lib.optionalString (cfg.jwtSecretFile != null) '' 248 .services.CoAuthoring.token.enable.browser = true | 249 .services.CoAuthoring.token.enable.request.inbox = true | 250 .services.CoAuthoring.token.enable.request.outbox = true | 251 .services.CoAuthoring.secret.inbox.string = "'"$(cat ${cfg.jwtSecretFile})"'" | 252 .services.CoAuthoring.secret.outbox.string = "'"$(cat ${cfg.jwtSecretFile})"'" | 253 .services.CoAuthoring.secret.session.string = "'"$(cat ${cfg.jwtSecretFile})"'" | 254 ''} 255 .rabbitmq.url = "${cfg.rabbitmqUrl}" 256 ' /run/onlyoffice/config/default.json | sponge /run/onlyoffice/config/default.json 257 258 if psql -d onlyoffice -c "SELECT 'task_result'::regclass;" >/dev/null; then 259 psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/removetbl.sql 260 psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/createdb.sql 261 else 262 psql -f ${cfg.package}/var/www/onlyoffice/documentserver/server/schema/postgresql/createdb.sql 263 fi 264 ''; 265 in 266 { 267 description = "onlyoffice documentserver"; 268 after = [ "network.target" "postgresql.service" ]; 269 requires = [ "postgresql.service" ]; 270 wantedBy = [ "multi-user.target" ]; 271 serviceConfig = { 272 ExecStart = "${cfg.package.fhs}/bin/onlyoffice-wrapper DocService/docservice /run/onlyoffice/config"; 273 ExecStartPre = [ onlyoffice-prestart ]; 274 Group = "onlyoffice"; 275 Restart = "always"; 276 RuntimeDirectory = "onlyoffice"; 277 StateDirectory = "onlyoffice"; 278 Type = "simple"; 279 User = "onlyoffice"; 280 }; 281 }; 282 }; 283 284 users.users = { 285 onlyoffice = { 286 description = "OnlyOffice Service"; 287 group = "onlyoffice"; 288 isSystemUser = true; 289 }; 290 291 nginx.extraGroups = [ "onlyoffice" ]; 292 }; 293 294 users.groups.onlyoffice = { }; 295 }; 296}