at 23.05-pre 34 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.int; 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 = "./public/default.md"; 295 description = lib.mdDoc '' 296 Path to the default Note file. 297 (Non-canonical paths are relative to HedgeDoc's base directory) 298 ''; 299 }; 300 docsPath = mkOption { 301 type = types.nullOr types.str; 302 default = "./public/docs"; 303 description = lib.mdDoc '' 304 Path to the docs directory. 305 (Non-canonical paths are relative to HedgeDoc's base directory) 306 ''; 307 }; 308 indexPath = mkOption { 309 type = types.nullOr types.str; 310 default = "./public/views/index.ejs"; 311 description = lib.mdDoc '' 312 Path to the index template file. 313 (Non-canonical paths are relative to HedgeDoc's base directory) 314 ''; 315 }; 316 hackmdPath = mkOption { 317 type = types.nullOr types.str; 318 default = "./public/views/hackmd.ejs"; 319 description = lib.mdDoc '' 320 Path to the hackmd template file. 321 (Non-canonical paths are relative to HedgeDoc's base directory) 322 ''; 323 }; 324 errorPath = mkOption { 325 type = types.nullOr types.str; 326 default = null; 327 defaultText = literalExpression "./public/views/error.ejs"; 328 description = lib.mdDoc '' 329 Path to the error template file. 330 (Non-canonical paths are relative to HedgeDoc's base directory) 331 ''; 332 }; 333 prettyPath = mkOption { 334 type = types.nullOr types.str; 335 default = null; 336 defaultText = literalExpression "./public/views/pretty.ejs"; 337 description = lib.mdDoc '' 338 Path to the pretty template file. 339 (Non-canonical paths are relative to HedgeDoc's base directory) 340 ''; 341 }; 342 slidePath = mkOption { 343 type = types.nullOr types.str; 344 default = null; 345 defaultText = literalExpression "./public/views/slide.hbs"; 346 description = lib.mdDoc '' 347 Path to the slide template file. 348 (Non-canonical paths are relative to HedgeDoc's base directory) 349 ''; 350 }; 351 uploadsPath = mkOption { 352 type = types.str; 353 default = "${cfg.workDir}/uploads"; 354 defaultText = literalExpression "/var/lib/${name}/uploads"; 355 description = lib.mdDoc '' 356 Path under which uploaded files are saved. 357 ''; 358 }; 359 sessionName = mkOption { 360 type = types.str; 361 default = "connect.sid"; 362 description = lib.mdDoc '' 363 Specify the name of the session cookie. 364 ''; 365 }; 366 sessionSecret = mkOption { 367 type = types.nullOr types.str; 368 default = null; 369 description = lib.mdDoc '' 370 Specify the secret used to sign the session cookie. 371 If unset, one will be generated on startup. 372 ''; 373 }; 374 sessionLife = mkOption { 375 type = types.int; 376 default = 1209600000; 377 description = lib.mdDoc '' 378 Session life time in milliseconds. 379 ''; 380 }; 381 heartbeatInterval = mkOption { 382 type = types.int; 383 default = 5000; 384 description = lib.mdDoc '' 385 Specify the socket.io heartbeat interval. 386 ''; 387 }; 388 heartbeatTimeout = mkOption { 389 type = types.int; 390 default = 10000; 391 description = lib.mdDoc '' 392 Specify the socket.io heartbeat timeout. 393 ''; 394 }; 395 documentMaxLength = mkOption { 396 type = types.int; 397 default = 100000; 398 description = lib.mdDoc '' 399 Specify the maximum document length. 400 ''; 401 }; 402 email = mkOption { 403 type = types.bool; 404 default = true; 405 description = lib.mdDoc '' 406 Whether to enable email sign-in. 407 ''; 408 }; 409 allowEmailRegister = mkOption { 410 type = types.bool; 411 default = true; 412 description = lib.mdDoc '' 413 Whether to enable email registration. 414 ''; 415 }; 416 allowGravatar = mkOption { 417 type = types.bool; 418 default = true; 419 description = lib.mdDoc '' 420 Whether to use gravatar as profile picture source. 421 ''; 422 }; 423 imageUploadType = mkOption { 424 type = types.enum [ "imgur" "s3" "minio" "filesystem" ]; 425 default = "filesystem"; 426 description = lib.mdDoc '' 427 Specify where to upload images. 428 ''; 429 }; 430 minio = mkOption { 431 type = types.nullOr (types.submodule { 432 options = { 433 accessKey = mkOption { 434 type = types.str; 435 description = lib.mdDoc '' 436 Minio access key. 437 ''; 438 }; 439 secretKey = mkOption { 440 type = types.str; 441 description = lib.mdDoc '' 442 Minio secret key. 443 ''; 444 }; 445 endPoint = mkOption { 446 type = types.str; 447 description = lib.mdDoc '' 448 Minio endpoint. 449 ''; 450 }; 451 port = mkOption { 452 type = types.int; 453 default = 9000; 454 description = lib.mdDoc '' 455 Minio listen port. 456 ''; 457 }; 458 secure = mkOption { 459 type = types.bool; 460 default = true; 461 description = lib.mdDoc '' 462 Whether to use HTTPS for Minio. 463 ''; 464 }; 465 }; 466 }); 467 default = null; 468 description = lib.mdDoc "Configure the minio third-party integration."; 469 }; 470 s3 = mkOption { 471 type = types.nullOr (types.submodule { 472 options = { 473 accessKeyId = mkOption { 474 type = types.str; 475 description = lib.mdDoc '' 476 AWS access key id. 477 ''; 478 }; 479 secretAccessKey = mkOption { 480 type = types.str; 481 description = lib.mdDoc '' 482 AWS access key. 483 ''; 484 }; 485 region = mkOption { 486 type = types.str; 487 description = lib.mdDoc '' 488 AWS S3 region. 489 ''; 490 }; 491 }; 492 }); 493 default = null; 494 description = lib.mdDoc "Configure the s3 third-party integration."; 495 }; 496 s3bucket = mkOption { 497 type = types.nullOr types.str; 498 default = null; 499 description = lib.mdDoc '' 500 Specify the bucket name for upload types `s3` and `minio`. 501 ''; 502 }; 503 allowPDFExport = mkOption { 504 type = types.bool; 505 default = true; 506 description = lib.mdDoc '' 507 Whether to enable PDF exports. 508 ''; 509 }; 510 imgur.clientId = mkOption { 511 type = types.nullOr types.str; 512 default = null; 513 description = lib.mdDoc '' 514 Imgur API client ID. 515 ''; 516 }; 517 azure = mkOption { 518 type = types.nullOr (types.submodule { 519 options = { 520 connectionString = mkOption { 521 type = types.str; 522 description = lib.mdDoc '' 523 Azure Blob Storage connection string. 524 ''; 525 }; 526 container = mkOption { 527 type = types.str; 528 description = lib.mdDoc '' 529 Azure Blob Storage container name. 530 It will be created if non-existent. 531 ''; 532 }; 533 }; 534 }); 535 default = null; 536 description = lib.mdDoc "Configure the azure third-party integration."; 537 }; 538 oauth2 = mkOption { 539 type = types.nullOr (types.submodule { 540 options = { 541 authorizationURL = mkOption { 542 type = types.str; 543 description = lib.mdDoc '' 544 Specify the OAuth authorization URL. 545 ''; 546 }; 547 tokenURL = mkOption { 548 type = types.str; 549 description = lib.mdDoc '' 550 Specify the OAuth token URL. 551 ''; 552 }; 553 baseURL = mkOption { 554 type = with types; nullOr str; 555 default = null; 556 description = lib.mdDoc '' 557 Specify the OAuth base URL. 558 ''; 559 }; 560 userProfileURL = mkOption { 561 type = with types; nullOr str; 562 default = null; 563 description = lib.mdDoc '' 564 Specify the OAuth userprofile URL. 565 ''; 566 }; 567 userProfileUsernameAttr = mkOption { 568 type = with types; nullOr str; 569 default = null; 570 description = lib.mdDoc '' 571 Specify the name of the attribute for the username from the claim. 572 ''; 573 }; 574 userProfileDisplayNameAttr = mkOption { 575 type = with types; nullOr str; 576 default = null; 577 description = lib.mdDoc '' 578 Specify the name of the attribute for the display name from the claim. 579 ''; 580 }; 581 userProfileEmailAttr = mkOption { 582 type = with types; nullOr str; 583 default = null; 584 description = lib.mdDoc '' 585 Specify the name of the attribute for the email from the claim. 586 ''; 587 }; 588 scope = mkOption { 589 type = with types; nullOr str; 590 default = null; 591 description = lib.mdDoc '' 592 Specify the OAuth scope. 593 ''; 594 }; 595 providerName = mkOption { 596 type = with types; nullOr str; 597 default = null; 598 description = lib.mdDoc '' 599 Specify the name to be displayed for this strategy. 600 ''; 601 }; 602 rolesClaim = mkOption { 603 type = with types; nullOr str; 604 default = null; 605 description = lib.mdDoc '' 606 Specify the role claim name. 607 ''; 608 }; 609 accessRole = mkOption { 610 type = with types; nullOr str; 611 default = null; 612 description = lib.mdDoc '' 613 Specify role which should be included in the ID token roles claim to grant access 614 ''; 615 }; 616 clientID = mkOption { 617 type = types.str; 618 description = lib.mdDoc '' 619 Specify the OAuth client ID. 620 ''; 621 }; 622 clientSecret = mkOption { 623 type = types.str; 624 description = lib.mdDoc '' 625 Specify the OAuth client secret. 626 ''; 627 }; 628 }; 629 }); 630 default = null; 631 description = lib.mdDoc "Configure the OAuth integration."; 632 }; 633 facebook = mkOption { 634 type = types.nullOr (types.submodule { 635 options = { 636 clientID = mkOption { 637 type = types.str; 638 description = lib.mdDoc '' 639 Facebook API client ID. 640 ''; 641 }; 642 clientSecret = mkOption { 643 type = types.str; 644 description = lib.mdDoc '' 645 Facebook API client secret. 646 ''; 647 }; 648 }; 649 }); 650 default = null; 651 description = lib.mdDoc "Configure the facebook third-party integration"; 652 }; 653 twitter = mkOption { 654 type = types.nullOr (types.submodule { 655 options = { 656 consumerKey = mkOption { 657 type = types.str; 658 description = lib.mdDoc '' 659 Twitter API consumer key. 660 ''; 661 }; 662 consumerSecret = mkOption { 663 type = types.str; 664 description = lib.mdDoc '' 665 Twitter API consumer secret. 666 ''; 667 }; 668 }; 669 }); 670 default = null; 671 description = lib.mdDoc "Configure the Twitter third-party integration."; 672 }; 673 github = mkOption { 674 type = types.nullOr (types.submodule { 675 options = { 676 clientID = mkOption { 677 type = types.str; 678 description = lib.mdDoc '' 679 GitHub API client ID. 680 ''; 681 }; 682 clientSecret = mkOption { 683 type = types.str; 684 description = lib.mdDoc '' 685 Github API client secret. 686 ''; 687 }; 688 }; 689 }); 690 default = null; 691 description = lib.mdDoc "Configure the GitHub third-party integration."; 692 }; 693 gitlab = mkOption { 694 type = types.nullOr (types.submodule { 695 options = { 696 baseURL = mkOption { 697 type = types.str; 698 default = ""; 699 description = lib.mdDoc '' 700 GitLab API authentication endpoint. 701 Only needed for other endpoints than gitlab.com. 702 ''; 703 }; 704 clientID = mkOption { 705 type = types.str; 706 description = lib.mdDoc '' 707 GitLab API client ID. 708 ''; 709 }; 710 clientSecret = mkOption { 711 type = types.str; 712 description = lib.mdDoc '' 713 GitLab API client secret. 714 ''; 715 }; 716 scope = mkOption { 717 type = types.enum [ "api" "read_user" ]; 718 default = "api"; 719 description = lib.mdDoc '' 720 GitLab API requested scope. 721 GitLab snippet import/export requires api scope. 722 ''; 723 }; 724 }; 725 }); 726 default = null; 727 description = lib.mdDoc "Configure the GitLab third-party integration."; 728 }; 729 mattermost = mkOption { 730 type = types.nullOr (types.submodule { 731 options = { 732 baseURL = mkOption { 733 type = types.str; 734 description = lib.mdDoc '' 735 Mattermost authentication endpoint. 736 ''; 737 }; 738 clientID = mkOption { 739 type = types.str; 740 description = lib.mdDoc '' 741 Mattermost API client ID. 742 ''; 743 }; 744 clientSecret = mkOption { 745 type = types.str; 746 description = lib.mdDoc '' 747 Mattermost API client secret. 748 ''; 749 }; 750 }; 751 }); 752 default = null; 753 description = lib.mdDoc "Configure the Mattermost third-party integration."; 754 }; 755 dropbox = mkOption { 756 type = types.nullOr (types.submodule { 757 options = { 758 clientID = mkOption { 759 type = types.str; 760 description = lib.mdDoc '' 761 Dropbox API client ID. 762 ''; 763 }; 764 clientSecret = mkOption { 765 type = types.str; 766 description = lib.mdDoc '' 767 Dropbox API client secret. 768 ''; 769 }; 770 appKey = mkOption { 771 type = types.str; 772 description = lib.mdDoc '' 773 Dropbox app key. 774 ''; 775 }; 776 }; 777 }); 778 default = null; 779 description = lib.mdDoc "Configure the Dropbox third-party integration."; 780 }; 781 google = mkOption { 782 type = types.nullOr (types.submodule { 783 options = { 784 clientID = mkOption { 785 type = types.str; 786 description = lib.mdDoc '' 787 Google API client ID. 788 ''; 789 }; 790 clientSecret = mkOption { 791 type = types.str; 792 description = lib.mdDoc '' 793 Google API client secret. 794 ''; 795 }; 796 }; 797 }); 798 default = null; 799 description = lib.mdDoc "Configure the Google third-party integration."; 800 }; 801 ldap = mkOption { 802 type = types.nullOr (types.submodule { 803 options = { 804 providerName = mkOption { 805 type = types.str; 806 default = ""; 807 description = lib.mdDoc '' 808 Optional name to be displayed at login form, indicating the LDAP provider. 809 ''; 810 }; 811 url = mkOption { 812 type = types.str; 813 example = "ldap://localhost"; 814 description = lib.mdDoc '' 815 URL of LDAP server. 816 ''; 817 }; 818 bindDn = mkOption { 819 type = types.str; 820 description = lib.mdDoc '' 821 Bind DN for LDAP access. 822 ''; 823 }; 824 bindCredentials = mkOption { 825 type = types.str; 826 description = lib.mdDoc '' 827 Bind credentials for LDAP access. 828 ''; 829 }; 830 searchBase = mkOption { 831 type = types.str; 832 example = "o=users,dc=example,dc=com"; 833 description = lib.mdDoc '' 834 LDAP directory to begin search from. 835 ''; 836 }; 837 searchFilter = mkOption { 838 type = types.str; 839 example = "(uid={{username}})"; 840 description = lib.mdDoc '' 841 LDAP filter to search with. 842 ''; 843 }; 844 searchAttributes = mkOption { 845 type = types.nullOr (types.listOf types.str); 846 default = null; 847 example = [ "displayName" "mail" ]; 848 description = lib.mdDoc '' 849 LDAP attributes to search with. 850 ''; 851 }; 852 userNameField = mkOption { 853 type = types.str; 854 default = ""; 855 description = lib.mdDoc '' 856 LDAP field which is used as the username on HedgeDoc. 857 By default {option}`useridField` is used. 858 ''; 859 }; 860 useridField = mkOption { 861 type = types.str; 862 example = "uid"; 863 description = lib.mdDoc '' 864 LDAP field which is a unique identifier for users on HedgeDoc. 865 ''; 866 }; 867 tlsca = mkOption { 868 type = types.str; 869 default = "/etc/ssl/certs/ca-certificates.crt"; 870 example = "server-cert.pem,root.pem"; 871 description = lib.mdDoc '' 872 Root CA for LDAP TLS in PEM format. 873 ''; 874 }; 875 }; 876 }); 877 default = null; 878 description = lib.mdDoc "Configure the LDAP integration."; 879 }; 880 saml = mkOption { 881 type = types.nullOr (types.submodule { 882 options = { 883 idpSsoUrl = mkOption { 884 type = types.str; 885 example = "https://idp.example.com/sso"; 886 description = lib.mdDoc '' 887 IdP authentication endpoint. 888 ''; 889 }; 890 idpCert = mkOption { 891 type = types.path; 892 example = "/path/to/cert.pem"; 893 description = lib.mdDoc '' 894 Path to IdP certificate file in PEM format. 895 ''; 896 }; 897 issuer = mkOption { 898 type = types.str; 899 default = ""; 900 description = lib.mdDoc '' 901 Optional identity of the service provider. 902 This defaults to the server URL. 903 ''; 904 }; 905 identifierFormat = mkOption { 906 type = types.str; 907 default = "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"; 908 description = lib.mdDoc '' 909 Optional name identifier format. 910 ''; 911 }; 912 groupAttribute = mkOption { 913 type = types.str; 914 default = ""; 915 example = "memberOf"; 916 description = lib.mdDoc '' 917 Optional attribute name for group list. 918 ''; 919 }; 920 externalGroups = mkOption { 921 type = types.listOf types.str; 922 default = []; 923 example = [ "Temporary-staff" "External-users" ]; 924 description = lib.mdDoc '' 925 Excluded group names. 926 ''; 927 }; 928 requiredGroups = mkOption { 929 type = types.listOf types.str; 930 default = []; 931 example = [ "Hedgedoc-Users" ]; 932 description = lib.mdDoc '' 933 Required group names. 934 ''; 935 }; 936 providerName = mkOption { 937 type = types.str; 938 default = ""; 939 example = "My institution"; 940 description = lib.mdDoc '' 941 Optional name to be displayed at login form indicating the SAML provider. 942 ''; 943 }; 944 attribute = { 945 id = mkOption { 946 type = types.str; 947 default = ""; 948 description = lib.mdDoc '' 949 Attribute map for `id'. 950 Defaults to `NameID' of SAML response. 951 ''; 952 }; 953 username = mkOption { 954 type = types.str; 955 default = ""; 956 description = lib.mdDoc '' 957 Attribute map for `username'. 958 Defaults to `NameID' of SAML response. 959 ''; 960 }; 961 email = mkOption { 962 type = types.str; 963 default = ""; 964 description = lib.mdDoc '' 965 Attribute map for `email`. 966 Defaults to `NameID` of SAML response if 967 {option}`identifierFormat` has 968 the default value. 969 ''; 970 }; 971 }; 972 }; 973 }); 974 default = null; 975 description = lib.mdDoc "Configure the SAML integration."; 976 }; 977 }; in lib.mkOption { 978 type = lib.types.submodule { 979 freeformType = settingsFormat.type; 980 inherit options; 981 }; 982 description = lib.mdDoc '' 983 HedgeDoc configuration, see 984 <https://docs.hedgedoc.org/configuration/> 985 for documentation. 986 ''; 987 }; 988 989 environmentFile = mkOption { 990 type = with types; nullOr path; 991 default = null; 992 example = "/var/lib/hedgedoc/hedgedoc.env"; 993 description = lib.mdDoc '' 994 Environment file as defined in {manpage}`systemd.exec(5)`. 995 996 Secrets may be passed to the service without adding them to the world-readable 997 Nix store, by specifying placeholder variables as the option value in Nix and 998 setting these variables accordingly in the environment file. 999 1000 ``` 1001 # snippet of HedgeDoc-related config 1002 services.hedgedoc.configuration.dbURL = "postgres://hedgedoc:\''${DB_PASSWORD}@db-host:5432/hedgedocdb"; 1003 services.hedgedoc.configuration.minio.secretKey = "$MINIO_SECRET_KEY"; 1004 ``` 1005 1006 ``` 1007 # content of the environment file 1008 DB_PASSWORD=verysecretdbpassword 1009 MINIO_SECRET_KEY=verysecretminiokey 1010 ``` 1011 1012 Note that this file needs to be available on the host on which 1013 `HedgeDoc` is running. 1014 ''; 1015 }; 1016 1017 package = mkOption { 1018 type = types.package; 1019 default = pkgs.hedgedoc; 1020 defaultText = literalExpression "pkgs.hedgedoc"; 1021 description = lib.mdDoc '' 1022 Package that provides HedgeDoc. 1023 ''; 1024 }; 1025 1026 }; 1027 1028 config = mkIf cfg.enable { 1029 assertions = [ 1030 { assertion = cfg.settings.db == {} -> ( 1031 cfg.settings.dbURL != "" && cfg.settings.dbURL != null 1032 ); 1033 message = "Database configuration for HedgeDoc missing."; } 1034 ]; 1035 users.groups.${name} = {}; 1036 users.users.${name} = { 1037 description = "HedgeDoc service user"; 1038 group = name; 1039 extraGroups = cfg.groups; 1040 home = cfg.workDir; 1041 createHome = true; 1042 isSystemUser = true; 1043 }; 1044 1045 systemd.services.hedgedoc = { 1046 description = "HedgeDoc Service"; 1047 wantedBy = [ "multi-user.target" ]; 1048 after = [ "networking.target" ]; 1049 preStart = '' 1050 ${pkgs.envsubst}/bin/envsubst \ 1051 -o ${cfg.workDir}/config.json \ 1052 -i ${prettyJSON cfg.settings} 1053 mkdir -p ${cfg.settings.uploadsPath} 1054 ''; 1055 serviceConfig = { 1056 WorkingDirectory = cfg.workDir; 1057 StateDirectory = [ cfg.workDir cfg.settings.uploadsPath ]; 1058 ExecStart = "${cfg.package}/bin/hedgedoc"; 1059 EnvironmentFile = mkIf (cfg.environmentFile != null) [ cfg.environmentFile ]; 1060 Environment = [ 1061 "CMD_CONFIG_FILE=${cfg.workDir}/config.json" 1062 "NODE_ENV=production" 1063 ]; 1064 Restart = "always"; 1065 User = name; 1066 PrivateTmp = true; 1067 }; 1068 }; 1069 }; 1070}