at 23.11-pre 35 kB view raw
1{ config, lib, pkgs, ... }: 2 3with lib; 4 5let 6 cfg = config.services.hedgedoc; 7 8 # 21.03 will not be an official release - it was instead 21.05. This 9 # versionAtLeast statement remains set to 21.03 for backwards compatibility. 10 # See https://github.com/NixOS/nixpkgs/pull/108899 and 11 # https://github.com/NixOS/rfcs/blob/master/rfcs/0080-nixos-release-schedule.md. 12 name = if versionAtLeast config.system.stateVersion "21.03" 13 then "hedgedoc" 14 else "codimd"; 15 16 settingsFormat = pkgs.formats.json {}; 17 18 prettyJSON = conf: 19 pkgs.runCommandLocal "hedgedoc-config.json" { 20 nativeBuildInputs = [ pkgs.jq ]; 21 } '' 22 jq '{production:del(.[]|nulls)|del(.[][]?|nulls)}' \ 23 < ${settingsFormat.generate "hedgedoc-ugly.json" cfg.settings} \ 24 > $out 25 ''; 26in 27{ 28 imports = [ 29 (mkRenamedOptionModule [ "services" "codimd" ] [ "services" "hedgedoc" ]) 30 (mkRenamedOptionModule 31 [ "services" "hedgedoc" "configuration" ] [ "services" "hedgedoc" "settings" ]) 32 ]; 33 34 options.services.hedgedoc = { 35 enable = mkEnableOption (lib.mdDoc "the HedgeDoc Markdown Editor"); 36 37 groups = mkOption { 38 type = types.listOf types.str; 39 default = []; 40 description = lib.mdDoc '' 41 Groups to which the service user should be added. 42 ''; 43 }; 44 45 workDir = mkOption { 46 type = types.path; 47 default = "/var/lib/${name}"; 48 description = lib.mdDoc '' 49 Working directory for the HedgeDoc service. 50 ''; 51 }; 52 53 settings = let options = { 54 debug = mkEnableOption (lib.mdDoc "debug mode"); 55 domain = mkOption { 56 type = types.nullOr types.str; 57 default = null; 58 example = "hedgedoc.org"; 59 description = lib.mdDoc '' 60 Domain name for the HedgeDoc instance. 61 ''; 62 }; 63 urlPath = mkOption { 64 type = types.nullOr types.str; 65 default = null; 66 example = "/url/path/to/hedgedoc"; 67 description = lib.mdDoc '' 68 Path under which HedgeDoc is accessible. 69 ''; 70 }; 71 host = mkOption { 72 type = types.str; 73 default = "localhost"; 74 description = lib.mdDoc '' 75 Address to listen on. 76 ''; 77 }; 78 port = mkOption { 79 type = types.port; 80 default = 3000; 81 example = 80; 82 description = lib.mdDoc '' 83 Port to listen on. 84 ''; 85 }; 86 path = mkOption { 87 type = types.nullOr types.str; 88 default = null; 89 example = "/run/hedgedoc.sock"; 90 description = lib.mdDoc '' 91 Specify where a UNIX domain socket should be placed. 92 ''; 93 }; 94 allowOrigin = mkOption { 95 type = types.listOf types.str; 96 default = []; 97 example = [ "localhost" "hedgedoc.org" ]; 98 description = lib.mdDoc '' 99 List of domains to whitelist. 100 ''; 101 }; 102 useSSL = mkOption { 103 type = types.bool; 104 default = false; 105 description = lib.mdDoc '' 106 Enable to use SSL server. This will also enable 107 {option}`protocolUseSSL`. 108 ''; 109 }; 110 hsts = { 111 enable = mkOption { 112 type = types.bool; 113 default = true; 114 description = lib.mdDoc '' 115 Whether to enable HSTS if HTTPS is also enabled. 116 ''; 117 }; 118 maxAgeSeconds = mkOption { 119 type = types.int; 120 default = 31536000; 121 description = lib.mdDoc '' 122 Max duration for clients to keep the HSTS status. 123 ''; 124 }; 125 includeSubdomains = mkOption { 126 type = types.bool; 127 default = true; 128 description = lib.mdDoc '' 129 Whether to include subdomains in HSTS. 130 ''; 131 }; 132 preload = mkOption { 133 type = types.bool; 134 default = true; 135 description = lib.mdDoc '' 136 Whether to allow preloading of the site's HSTS status. 137 ''; 138 }; 139 }; 140 csp = mkOption { 141 type = types.nullOr types.attrs; 142 default = null; 143 example = literalExpression '' 144 { 145 enable = true; 146 directives = { 147 scriptSrc = "trustworthy.scripts.example.com"; 148 }; 149 upgradeInsecureRequest = "auto"; 150 addDefaults = true; 151 } 152 ''; 153 description = lib.mdDoc '' 154 Specify the Content Security Policy which is passed to Helmet. 155 For configuration details see <https://helmetjs.github.io/docs/csp/>. 156 ''; 157 }; 158 protocolUseSSL = mkOption { 159 type = types.bool; 160 default = false; 161 description = lib.mdDoc '' 162 Enable to use TLS for resource paths. 163 This only applies when {option}`domain` is set. 164 ''; 165 }; 166 urlAddPort = mkOption { 167 type = types.bool; 168 default = false; 169 description = lib.mdDoc '' 170 Enable to add the port to callback URLs. 171 This only applies when {option}`domain` is set 172 and only for ports other than 80 and 443. 173 ''; 174 }; 175 useCDN = mkOption { 176 type = types.bool; 177 default = false; 178 description = lib.mdDoc '' 179 Whether to use CDN resources or not. 180 ''; 181 }; 182 allowAnonymous = mkOption { 183 type = types.bool; 184 default = true; 185 description = lib.mdDoc '' 186 Whether to allow anonymous usage. 187 ''; 188 }; 189 allowAnonymousEdits = mkOption { 190 type = types.bool; 191 default = false; 192 description = lib.mdDoc '' 193 Whether to allow guests to edit existing notes with the `freely` permission, 194 when {option}`allowAnonymous` is enabled. 195 ''; 196 }; 197 allowFreeURL = mkOption { 198 type = types.bool; 199 default = false; 200 description = lib.mdDoc '' 201 Whether to allow note creation by accessing a nonexistent note URL. 202 ''; 203 }; 204 requireFreeURLAuthentication = mkOption { 205 type = types.bool; 206 default = false; 207 description = lib.mdDoc '' 208 Whether to require authentication for FreeURL mode style note creation. 209 ''; 210 }; 211 defaultPermission = mkOption { 212 type = types.enum [ "freely" "editable" "limited" "locked" "private" ]; 213 default = "editable"; 214 description = lib.mdDoc '' 215 Default permissions for notes. 216 This only applies for signed-in users. 217 ''; 218 }; 219 dbURL = mkOption { 220 type = types.nullOr types.str; 221 default = null; 222 example = '' 223 postgres://user:pass@host:5432/dbname 224 ''; 225 description = lib.mdDoc '' 226 Specify which database to use. 227 HedgeDoc supports mysql, postgres, sqlite and mssql. 228 See [ 229 https://sequelize.readthedocs.io/en/v3/](https://sequelize.readthedocs.io/en/v3/) for more information. 230 Note: This option overrides {option}`db`. 231 ''; 232 }; 233 db = mkOption { 234 type = types.attrs; 235 default = {}; 236 example = literalExpression '' 237 { 238 dialect = "sqlite"; 239 storage = "/var/lib/${name}/db.${name}.sqlite"; 240 } 241 ''; 242 description = lib.mdDoc '' 243 Specify the configuration for sequelize. 244 HedgeDoc supports mysql, postgres, sqlite and mssql. 245 See [ 246 https://sequelize.readthedocs.io/en/v3/](https://sequelize.readthedocs.io/en/v3/) for more information. 247 Note: This option overrides {option}`db`. 248 ''; 249 }; 250 sslKeyPath= mkOption { 251 type = types.nullOr types.str; 252 default = null; 253 example = "/var/lib/hedgedoc/hedgedoc.key"; 254 description = lib.mdDoc '' 255 Path to the SSL key. Needed when {option}`useSSL` is enabled. 256 ''; 257 }; 258 sslCertPath = mkOption { 259 type = types.nullOr types.str; 260 default = null; 261 example = "/var/lib/hedgedoc/hedgedoc.crt"; 262 description = lib.mdDoc '' 263 Path to the SSL cert. Needed when {option}`useSSL` is enabled. 264 ''; 265 }; 266 sslCAPath = mkOption { 267 type = types.listOf types.str; 268 default = []; 269 example = [ "/var/lib/hedgedoc/ca.crt" ]; 270 description = lib.mdDoc '' 271 SSL ca chain. Needed when {option}`useSSL` is enabled. 272 ''; 273 }; 274 dhParamPath = mkOption { 275 type = types.nullOr types.str; 276 default = null; 277 example = "/var/lib/hedgedoc/dhparam.pem"; 278 description = lib.mdDoc '' 279 Path to the SSL dh params. Needed when {option}`useSSL` is enabled. 280 ''; 281 }; 282 tmpPath = mkOption { 283 type = types.str; 284 default = "/tmp"; 285 description = lib.mdDoc '' 286 Path to the temp directory HedgeDoc should use. 287 Note that {option}`serviceConfig.PrivateTmp` is enabled for 288 the HedgeDoc systemd service by default. 289 (Non-canonical paths are relative to HedgeDoc's base directory) 290 ''; 291 }; 292 defaultNotePath = mkOption { 293 type = types.nullOr types.str; 294 default = "${cfg.package}/public/default.md"; 295 defaultText = literalExpression "\"\${cfg.package}/public/default.md\""; 296 description = lib.mdDoc '' 297 Path to the default Note file. 298 (Non-canonical paths are relative to HedgeDoc's base directory) 299 ''; 300 }; 301 docsPath = mkOption { 302 type = types.nullOr types.str; 303 default = "${cfg.package}/public/docs"; 304 defaultText = literalExpression "\"\${cfg.package}/public/docs\""; 305 description = lib.mdDoc '' 306 Path to the docs directory. 307 (Non-canonical paths are relative to HedgeDoc's base directory) 308 ''; 309 }; 310 indexPath = mkOption { 311 type = types.nullOr types.str; 312 default = "${cfg.package}/public/views/index.ejs"; 313 defaultText = literalExpression "\"\${cfg.package}/public/views/index.ejs\""; 314 description = lib.mdDoc '' 315 Path to the index template file. 316 (Non-canonical paths are relative to HedgeDoc's base directory) 317 ''; 318 }; 319 hackmdPath = mkOption { 320 type = types.nullOr types.str; 321 default = "${cfg.package}/public/views/hackmd.ejs"; 322 defaultText = literalExpression "\"\${cfg.package}/public/views/hackmd.ejs\""; 323 description = lib.mdDoc '' 324 Path to the hackmd template file. 325 (Non-canonical paths are relative to HedgeDoc's base directory) 326 ''; 327 }; 328 errorPath = mkOption { 329 type = types.nullOr types.str; 330 default = "${cfg.package}/public/views/error.ejs"; 331 defaultText = literalExpression "\"\${cfg.package}/public/views/error.ejs\""; 332 description = lib.mdDoc '' 333 Path to the error template file. 334 (Non-canonical paths are relative to HedgeDoc's base directory) 335 ''; 336 }; 337 prettyPath = mkOption { 338 type = types.nullOr types.str; 339 default = "${cfg.package}/public/views/pretty.ejs"; 340 defaultText = literalExpression "\"\${cfg.package}/public/views/pretty.ejs\""; 341 description = lib.mdDoc '' 342 Path to the pretty template file. 343 (Non-canonical paths are relative to HedgeDoc's base directory) 344 ''; 345 }; 346 slidePath = mkOption { 347 type = types.nullOr types.str; 348 default = "${cfg.package}/public/views/slide.hbs"; 349 defaultText = literalExpression "\"\${cfg.package}/public/views/slide.hbs\""; 350 description = lib.mdDoc '' 351 Path to the slide template file. 352 (Non-canonical paths are relative to HedgeDoc's base directory) 353 ''; 354 }; 355 uploadsPath = mkOption { 356 type = types.str; 357 default = "${cfg.workDir}/uploads"; 358 defaultText = literalExpression "\"\${cfg.workDir}/uploads\""; 359 description = lib.mdDoc '' 360 Path under which uploaded files are saved. 361 ''; 362 }; 363 sessionName = mkOption { 364 type = types.str; 365 default = "connect.sid"; 366 description = lib.mdDoc '' 367 Specify the name of the session cookie. 368 ''; 369 }; 370 sessionSecret = mkOption { 371 type = types.nullOr types.str; 372 default = null; 373 description = lib.mdDoc '' 374 Specify the secret used to sign the session cookie. 375 If unset, one will be generated on startup. 376 ''; 377 }; 378 sessionLife = mkOption { 379 type = types.int; 380 default = 1209600000; 381 description = lib.mdDoc '' 382 Session life time in milliseconds. 383 ''; 384 }; 385 heartbeatInterval = mkOption { 386 type = types.int; 387 default = 5000; 388 description = lib.mdDoc '' 389 Specify the socket.io heartbeat interval. 390 ''; 391 }; 392 heartbeatTimeout = mkOption { 393 type = types.int; 394 default = 10000; 395 description = lib.mdDoc '' 396 Specify the socket.io heartbeat timeout. 397 ''; 398 }; 399 documentMaxLength = mkOption { 400 type = types.int; 401 default = 100000; 402 description = lib.mdDoc '' 403 Specify the maximum document length. 404 ''; 405 }; 406 email = mkOption { 407 type = types.bool; 408 default = true; 409 description = lib.mdDoc '' 410 Whether to enable email sign-in. 411 ''; 412 }; 413 allowEmailRegister = mkOption { 414 type = types.bool; 415 default = true; 416 description = lib.mdDoc '' 417 Whether to enable email registration. 418 ''; 419 }; 420 allowGravatar = mkOption { 421 type = types.bool; 422 default = true; 423 description = lib.mdDoc '' 424 Whether to use gravatar as profile picture source. 425 ''; 426 }; 427 imageUploadType = mkOption { 428 type = types.enum [ "imgur" "s3" "minio" "filesystem" ]; 429 default = "filesystem"; 430 description = lib.mdDoc '' 431 Specify where to upload images. 432 ''; 433 }; 434 minio = mkOption { 435 type = types.nullOr (types.submodule { 436 options = { 437 accessKey = mkOption { 438 type = types.str; 439 description = lib.mdDoc '' 440 Minio access key. 441 ''; 442 }; 443 secretKey = mkOption { 444 type = types.str; 445 description = lib.mdDoc '' 446 Minio secret key. 447 ''; 448 }; 449 endPoint = mkOption { 450 type = types.str; 451 description = lib.mdDoc '' 452 Minio endpoint. 453 ''; 454 }; 455 port = mkOption { 456 type = types.port; 457 default = 9000; 458 description = lib.mdDoc '' 459 Minio listen port. 460 ''; 461 }; 462 secure = mkOption { 463 type = types.bool; 464 default = true; 465 description = lib.mdDoc '' 466 Whether to use HTTPS for Minio. 467 ''; 468 }; 469 }; 470 }); 471 default = null; 472 description = lib.mdDoc "Configure the minio third-party integration."; 473 }; 474 s3 = mkOption { 475 type = types.nullOr (types.submodule { 476 options = { 477 accessKeyId = mkOption { 478 type = types.str; 479 description = lib.mdDoc '' 480 AWS access key id. 481 ''; 482 }; 483 secretAccessKey = mkOption { 484 type = types.str; 485 description = lib.mdDoc '' 486 AWS access key. 487 ''; 488 }; 489 region = mkOption { 490 type = types.str; 491 description = lib.mdDoc '' 492 AWS S3 region. 493 ''; 494 }; 495 }; 496 }); 497 default = null; 498 description = lib.mdDoc "Configure the s3 third-party integration."; 499 }; 500 s3bucket = mkOption { 501 type = types.nullOr types.str; 502 default = null; 503 description = lib.mdDoc '' 504 Specify the bucket name for upload types `s3` and `minio`. 505 ''; 506 }; 507 allowPDFExport = mkOption { 508 type = types.bool; 509 default = true; 510 description = lib.mdDoc '' 511 Whether to enable PDF exports. 512 ''; 513 }; 514 imgur.clientId = mkOption { 515 type = types.nullOr types.str; 516 default = null; 517 description = lib.mdDoc '' 518 Imgur API client ID. 519 ''; 520 }; 521 azure = mkOption { 522 type = types.nullOr (types.submodule { 523 options = { 524 connectionString = mkOption { 525 type = types.str; 526 description = lib.mdDoc '' 527 Azure Blob Storage connection string. 528 ''; 529 }; 530 container = mkOption { 531 type = types.str; 532 description = lib.mdDoc '' 533 Azure Blob Storage container name. 534 It will be created if non-existent. 535 ''; 536 }; 537 }; 538 }); 539 default = null; 540 description = lib.mdDoc "Configure the azure third-party integration."; 541 }; 542 oauth2 = mkOption { 543 type = types.nullOr (types.submodule { 544 options = { 545 authorizationURL = mkOption { 546 type = types.str; 547 description = lib.mdDoc '' 548 Specify the OAuth authorization URL. 549 ''; 550 }; 551 tokenURL = mkOption { 552 type = types.str; 553 description = lib.mdDoc '' 554 Specify the OAuth token URL. 555 ''; 556 }; 557 baseURL = mkOption { 558 type = with types; nullOr str; 559 default = null; 560 description = lib.mdDoc '' 561 Specify the OAuth base URL. 562 ''; 563 }; 564 userProfileURL = mkOption { 565 type = with types; nullOr str; 566 default = null; 567 description = lib.mdDoc '' 568 Specify the OAuth userprofile URL. 569 ''; 570 }; 571 userProfileUsernameAttr = mkOption { 572 type = with types; nullOr str; 573 default = null; 574 description = lib.mdDoc '' 575 Specify the name of the attribute for the username from the claim. 576 ''; 577 }; 578 userProfileDisplayNameAttr = mkOption { 579 type = with types; nullOr str; 580 default = null; 581 description = lib.mdDoc '' 582 Specify the name of the attribute for the display name from the claim. 583 ''; 584 }; 585 userProfileEmailAttr = mkOption { 586 type = with types; nullOr str; 587 default = null; 588 description = lib.mdDoc '' 589 Specify the name of the attribute for the email from the claim. 590 ''; 591 }; 592 scope = mkOption { 593 type = with types; nullOr str; 594 default = null; 595 description = lib.mdDoc '' 596 Specify the OAuth scope. 597 ''; 598 }; 599 providerName = mkOption { 600 type = with types; nullOr str; 601 default = null; 602 description = lib.mdDoc '' 603 Specify the name to be displayed for this strategy. 604 ''; 605 }; 606 rolesClaim = mkOption { 607 type = with types; nullOr str; 608 default = null; 609 description = lib.mdDoc '' 610 Specify the role claim name. 611 ''; 612 }; 613 accessRole = mkOption { 614 type = with types; nullOr str; 615 default = null; 616 description = lib.mdDoc '' 617 Specify role which should be included in the ID token roles claim to grant access 618 ''; 619 }; 620 clientID = mkOption { 621 type = types.str; 622 description = lib.mdDoc '' 623 Specify the OAuth client ID. 624 ''; 625 }; 626 clientSecret = mkOption { 627 type = types.str; 628 description = lib.mdDoc '' 629 Specify the OAuth client secret. 630 ''; 631 }; 632 }; 633 }); 634 default = null; 635 description = lib.mdDoc "Configure the OAuth integration."; 636 }; 637 facebook = mkOption { 638 type = types.nullOr (types.submodule { 639 options = { 640 clientID = mkOption { 641 type = types.str; 642 description = lib.mdDoc '' 643 Facebook API client ID. 644 ''; 645 }; 646 clientSecret = mkOption { 647 type = types.str; 648 description = lib.mdDoc '' 649 Facebook API client secret. 650 ''; 651 }; 652 }; 653 }); 654 default = null; 655 description = lib.mdDoc "Configure the facebook third-party integration"; 656 }; 657 twitter = mkOption { 658 type = types.nullOr (types.submodule { 659 options = { 660 consumerKey = mkOption { 661 type = types.str; 662 description = lib.mdDoc '' 663 Twitter API consumer key. 664 ''; 665 }; 666 consumerSecret = mkOption { 667 type = types.str; 668 description = lib.mdDoc '' 669 Twitter API consumer secret. 670 ''; 671 }; 672 }; 673 }); 674 default = null; 675 description = lib.mdDoc "Configure the Twitter third-party integration."; 676 }; 677 github = mkOption { 678 type = types.nullOr (types.submodule { 679 options = { 680 clientID = mkOption { 681 type = types.str; 682 description = lib.mdDoc '' 683 GitHub API client ID. 684 ''; 685 }; 686 clientSecret = mkOption { 687 type = types.str; 688 description = lib.mdDoc '' 689 Github API client secret. 690 ''; 691 }; 692 }; 693 }); 694 default = null; 695 description = lib.mdDoc "Configure the GitHub third-party integration."; 696 }; 697 gitlab = mkOption { 698 type = types.nullOr (types.submodule { 699 options = { 700 baseURL = mkOption { 701 type = types.str; 702 default = ""; 703 description = lib.mdDoc '' 704 GitLab API authentication endpoint. 705 Only needed for other endpoints than gitlab.com. 706 ''; 707 }; 708 clientID = mkOption { 709 type = types.str; 710 description = lib.mdDoc '' 711 GitLab API client ID. 712 ''; 713 }; 714 clientSecret = mkOption { 715 type = types.str; 716 description = lib.mdDoc '' 717 GitLab API client secret. 718 ''; 719 }; 720 scope = mkOption { 721 type = types.enum [ "api" "read_user" ]; 722 default = "api"; 723 description = lib.mdDoc '' 724 GitLab API requested scope. 725 GitLab snippet import/export requires api scope. 726 ''; 727 }; 728 }; 729 }); 730 default = null; 731 description = lib.mdDoc "Configure the GitLab third-party integration."; 732 }; 733 mattermost = mkOption { 734 type = types.nullOr (types.submodule { 735 options = { 736 baseURL = mkOption { 737 type = types.str; 738 description = lib.mdDoc '' 739 Mattermost authentication endpoint. 740 ''; 741 }; 742 clientID = mkOption { 743 type = types.str; 744 description = lib.mdDoc '' 745 Mattermost API client ID. 746 ''; 747 }; 748 clientSecret = mkOption { 749 type = types.str; 750 description = lib.mdDoc '' 751 Mattermost API client secret. 752 ''; 753 }; 754 }; 755 }); 756 default = null; 757 description = lib.mdDoc "Configure the Mattermost third-party integration."; 758 }; 759 dropbox = mkOption { 760 type = types.nullOr (types.submodule { 761 options = { 762 clientID = mkOption { 763 type = types.str; 764 description = lib.mdDoc '' 765 Dropbox API client ID. 766 ''; 767 }; 768 clientSecret = mkOption { 769 type = types.str; 770 description = lib.mdDoc '' 771 Dropbox API client secret. 772 ''; 773 }; 774 appKey = mkOption { 775 type = types.str; 776 description = lib.mdDoc '' 777 Dropbox app key. 778 ''; 779 }; 780 }; 781 }); 782 default = null; 783 description = lib.mdDoc "Configure the Dropbox third-party integration."; 784 }; 785 google = mkOption { 786 type = types.nullOr (types.submodule { 787 options = { 788 clientID = mkOption { 789 type = types.str; 790 description = lib.mdDoc '' 791 Google API client ID. 792 ''; 793 }; 794 clientSecret = mkOption { 795 type = types.str; 796 description = lib.mdDoc '' 797 Google API client secret. 798 ''; 799 }; 800 }; 801 }); 802 default = null; 803 description = lib.mdDoc "Configure the Google third-party integration."; 804 }; 805 ldap = mkOption { 806 type = types.nullOr (types.submodule { 807 options = { 808 providerName = mkOption { 809 type = types.str; 810 default = ""; 811 description = lib.mdDoc '' 812 Optional name to be displayed at login form, indicating the LDAP provider. 813 ''; 814 }; 815 url = mkOption { 816 type = types.str; 817 example = "ldap://localhost"; 818 description = lib.mdDoc '' 819 URL of LDAP server. 820 ''; 821 }; 822 bindDn = mkOption { 823 type = types.str; 824 description = lib.mdDoc '' 825 Bind DN for LDAP access. 826 ''; 827 }; 828 bindCredentials = mkOption { 829 type = types.str; 830 description = lib.mdDoc '' 831 Bind credentials for LDAP access. 832 ''; 833 }; 834 searchBase = mkOption { 835 type = types.str; 836 example = "o=users,dc=example,dc=com"; 837 description = lib.mdDoc '' 838 LDAP directory to begin search from. 839 ''; 840 }; 841 searchFilter = mkOption { 842 type = types.str; 843 example = "(uid={{username}})"; 844 description = lib.mdDoc '' 845 LDAP filter to search with. 846 ''; 847 }; 848 searchAttributes = mkOption { 849 type = types.nullOr (types.listOf types.str); 850 default = null; 851 example = [ "displayName" "mail" ]; 852 description = lib.mdDoc '' 853 LDAP attributes to search with. 854 ''; 855 }; 856 userNameField = mkOption { 857 type = types.str; 858 default = ""; 859 description = lib.mdDoc '' 860 LDAP field which is used as the username on HedgeDoc. 861 By default {option}`useridField` is used. 862 ''; 863 }; 864 useridField = mkOption { 865 type = types.str; 866 example = "uid"; 867 description = lib.mdDoc '' 868 LDAP field which is a unique identifier for users on HedgeDoc. 869 ''; 870 }; 871 tlsca = mkOption { 872 type = types.str; 873 default = "/etc/ssl/certs/ca-certificates.crt"; 874 example = "server-cert.pem,root.pem"; 875 description = lib.mdDoc '' 876 Root CA for LDAP TLS in PEM format. 877 ''; 878 }; 879 }; 880 }); 881 default = null; 882 description = lib.mdDoc "Configure the LDAP integration."; 883 }; 884 saml = mkOption { 885 type = types.nullOr (types.submodule { 886 options = { 887 idpSsoUrl = mkOption { 888 type = types.str; 889 example = "https://idp.example.com/sso"; 890 description = lib.mdDoc '' 891 IdP authentication endpoint. 892 ''; 893 }; 894 idpCert = mkOption { 895 type = types.path; 896 example = "/path/to/cert.pem"; 897 description = lib.mdDoc '' 898 Path to IdP certificate file in PEM format. 899 ''; 900 }; 901 issuer = mkOption { 902 type = types.str; 903 default = ""; 904 description = lib.mdDoc '' 905 Optional identity of the service provider. 906 This defaults to the server URL. 907 ''; 908 }; 909 identifierFormat = mkOption { 910 type = types.str; 911 default = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; 912 description = lib.mdDoc '' 913 Optional name identifier format. 914 ''; 915 }; 916 groupAttribute = mkOption { 917 type = types.str; 918 default = ""; 919 example = "memberOf"; 920 description = lib.mdDoc '' 921 Optional attribute name for group list. 922 ''; 923 }; 924 externalGroups = mkOption { 925 type = types.listOf types.str; 926 default = []; 927 example = [ "Temporary-staff" "External-users" ]; 928 description = lib.mdDoc '' 929 Excluded group names. 930 ''; 931 }; 932 requiredGroups = mkOption { 933 type = types.listOf types.str; 934 default = []; 935 example = [ "Hedgedoc-Users" ]; 936 description = lib.mdDoc '' 937 Required group names. 938 ''; 939 }; 940 providerName = mkOption { 941 type = types.str; 942 default = ""; 943 example = "My institution"; 944 description = lib.mdDoc '' 945 Optional name to be displayed at login form indicating the SAML provider. 946 ''; 947 }; 948 attribute = { 949 id = mkOption { 950 type = types.str; 951 default = ""; 952 description = lib.mdDoc '' 953 Attribute map for `id`. 954 Defaults to `NameID` of SAML response. 955 ''; 956 }; 957 username = mkOption { 958 type = types.str; 959 default = ""; 960 description = lib.mdDoc '' 961 Attribute map for `username`. 962 Defaults to `NameID` of SAML response. 963 ''; 964 }; 965 email = mkOption { 966 type = types.str; 967 default = ""; 968 description = lib.mdDoc '' 969 Attribute map for `email`. 970 Defaults to `NameID` of SAML response if 971 {option}`identifierFormat` has 972 the default value. 973 ''; 974 }; 975 }; 976 }; 977 }); 978 default = null; 979 description = lib.mdDoc "Configure the SAML integration."; 980 }; 981 }; in lib.mkOption { 982 type = lib.types.submodule { 983 freeformType = settingsFormat.type; 984 inherit options; 985 }; 986 description = lib.mdDoc '' 987 HedgeDoc configuration, see 988 <https://docs.hedgedoc.org/configuration/> 989 for documentation. 990 ''; 991 }; 992 993 environmentFile = mkOption { 994 type = with types; nullOr path; 995 default = null; 996 example = "/var/lib/hedgedoc/hedgedoc.env"; 997 description = lib.mdDoc '' 998 Environment file as defined in {manpage}`systemd.exec(5)`. 999 1000 Secrets may be passed to the service without adding them to the world-readable 1001 Nix store, by specifying placeholder variables as the option value in Nix and 1002 setting these variables accordingly in the environment file. 1003 1004 ``` 1005 # snippet of HedgeDoc-related config 1006 services.hedgedoc.settings.dbURL = "postgres://hedgedoc:\''${DB_PASSWORD}@db-host:5432/hedgedocdb"; 1007 services.hedgedoc.settings.minio.secretKey = "$MINIO_SECRET_KEY"; 1008 ``` 1009 1010 ``` 1011 # content of the environment file 1012 DB_PASSWORD=verysecretdbpassword 1013 MINIO_SECRET_KEY=verysecretminiokey 1014 ``` 1015 1016 Note that this file needs to be available on the host on which 1017 `HedgeDoc` is running. 1018 ''; 1019 }; 1020 1021 package = mkOption { 1022 type = types.package; 1023 default = pkgs.hedgedoc; 1024 defaultText = literalExpression "pkgs.hedgedoc"; 1025 description = lib.mdDoc '' 1026 Package that provides HedgeDoc. 1027 ''; 1028 }; 1029 1030 }; 1031 1032 config = mkIf cfg.enable { 1033 assertions = [ 1034 { assertion = cfg.settings.db == {} -> ( 1035 cfg.settings.dbURL != "" && cfg.settings.dbURL != null 1036 ); 1037 message = "Database configuration for HedgeDoc missing."; } 1038 ]; 1039 users.groups.${name} = {}; 1040 users.users.${name} = { 1041 description = "HedgeDoc service user"; 1042 group = name; 1043 extraGroups = cfg.groups; 1044 home = cfg.workDir; 1045 createHome = true; 1046 isSystemUser = true; 1047 }; 1048 1049 systemd.services.hedgedoc = { 1050 description = "HedgeDoc Service"; 1051 wantedBy = [ "multi-user.target" ]; 1052 after = [ "networking.target" ]; 1053 preStart = '' 1054 ${pkgs.envsubst}/bin/envsubst \ 1055 -o ${cfg.workDir}/config.json \ 1056 -i ${prettyJSON cfg.settings} 1057 mkdir -p ${cfg.settings.uploadsPath} 1058 ''; 1059 serviceConfig = { 1060 WorkingDirectory = cfg.workDir; 1061 StateDirectory = [ cfg.workDir cfg.settings.uploadsPath ]; 1062 ExecStart = "${cfg.package}/bin/hedgedoc"; 1063 EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; 1064 Environment = [ 1065 "CMD_CONFIG_FILE=${cfg.workDir}/config.json" 1066 "NODE_ENV=production" 1067 ]; 1068 Restart = "always"; 1069 User = name; 1070 PrivateTmp = true; 1071 }; 1072 }; 1073 }; 1074}