Outline: Difference between revisions

From NixOS Wiki
imported>Onny
mNo edit summary
Klinger (talk | contribs)
No edit summary
 
(9 intermediate revisions by 2 users not shown)
Line 4: Line 4:


The most minimal local installation of Outline can be enabled with the following configuration
The most minimal local installation of Outline can be enabled with the following configuration
{{note|Previous example used the <code>mockPassword</code> connector, which is [https://github.com/dexidp/dex/blob/8e07edc188222fed79a6db7c8e3e57e6d8e82df7/server/server.go#L566 an undocumented configuration option] in Dex, used solely to [https://github.com/dexidp/dex/blob/8e07edc188222fed79a6db7c8e3e57e6d8e82df7/server/handlers_test.go#L250 run some tests]. Use [https://dexidp.io/docs/connectors/local/ staticPasswords] configuration instead, which allows using a hash for the password and setting a real email for the user which can be used to send notifications if an SMTP server is configured. Keep in mind, if you created your first user with the previous method (mockPassword), that user is your only <code>admin</code> user and you need to keep it around in order to confer <code>admin</code> privileges to the next user.}}


{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
Line 18: Line 20:
     publicUrl = "http://localhost:3000";
     publicUrl = "http://localhost:3000";
     forceHttps = false;
     forceHttps = false;
 
     storage.storageType = "local";
    # Defined but not used in a minimal setup. Uploading files will
    # only work if you configure a sotrage backend (see below).
     storage = {
      accessKey = "outline";
      secretKeyFile = "${pkgs.writeText "minio-secret" "test123"}";
      region = config.services.minio.region;
      uploadBucketUrl = "http://127.0.0.1:9000";
      uploadBucketName = "outline";
    };
 
     oidcAuthentication = {
     oidcAuthentication = {
       # Parts taken from
       # Parts taken from
Line 47: Line 39:
     settings = {
     settings = {
       issuer = "http://dex.localhost";
       issuer = "http://dex.localhost";
       storage = {
       storage.type = "sqlite3";
        type = "sqlite3";
        config.file = "/var/lib/dex/db.sqlite3";
      };
       web.http = "127.0.0.1:5556";
       web.http = "127.0.0.1:5556";
      enablePasswordDB = true;
       staticClients = [
       staticClients = [
         {
         {
Line 60: Line 50:
         }
         }
       ];
       ];
       connectors = [
       staticPasswords = [
         {
         {
           type = "mockPassword";
           email = "user.email@example.com";
           id = "mock";
           # bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2)
           name = "Example";
           hash = "10$TDh68T5XUK10$TDh68T5XUK10$TDh68T5XUK";
           config = {
           username = "test";
            username = "admin";
          # easily generated with `$ uuidgen`
            password = "password";
          userID = "6D196B03-8A28-4D6E-B849-9298168CBA34";
          };
         }
         }
       ];  
       ];
     };
     };
  };
  systemd.services.dex = {
    serviceConfig.StateDirectory = "dex";
   };
   };


Line 97: Line 82:
</nowiki>}}
</nowiki>}}


Outline is available at http://localhost . Choose login provider "Dex" and authenticate with the example mock login <code>admin</code> and <code>password</code>.
Outline is available at http://localhost . Choose login provider "Dex" and authenticate with the example mock login <code>user.email@example.com</code> and <code>password</code>.


== Configuration ==
=== SSL example ===


=== Storage backend ===
Similar as before but this time with Nginx handling SSL.
 
{{file|/etc/nixos/configuration.nix|nix|<nowiki>
{ config, pkgs, lib, ... }: {
 
  services.nginx = {
    enable = true;
    recommendedProxySettings = true;
    recommendedTlsSettings = true;
    virtualHosts = {
      "outline.example.tld" = {
        onlySSL = true;
        useACMEHost = "example.tld"; # assuming security.acme.certs."example.tld" with `extraDomainNames = [ "outline.example.tld" ]`
        locations."/" = {
          proxyPass = "http://localhost:${toString config.services.outline.port}";
          proxyWebsockets = true;
          extraConfig = ''
                proxy_set_header X-Scheme $scheme;
          '';
        };
      };
      "dex.example.tld" = {
        onlySSL = true;
        useACMEHost = "example.tld";
        locations."/" = {
          proxyPass = "http://${config.services.dex.settings.web.http}";
          proxyWebsockets = true;
        };
      };
    };
  };
 
 
  services.outline = {
    enable = true;
    publicUrl = "https://outline.example.tld";
    port = 3003; # using 3003 instead of default 3000 just in case another service is already using 3000
    forceHttps = false;
    storage.storageType = "local";
    oidcAuthentication = {
      authUrl = "https://dex.example.tld/auth";
      tokenUrl = "https://dex.example.tld/token";
      userinfoUrl = "https://dex.example.tld/userinfo";
      clientId = "outline";
      clientSecretFile = (builtins.elemAt config.services.dex.settings.staticClients 0).secretFile;
      scopes = [ "openid" "email" "profile" ];
      usernameClaim = "preferred_username";
      displayName = "Dex";
    };
  };
 
  services.dex = {
    enable = true;
    settings = {
      issuer = "https://dex.example.tld";
      storage.type = "sqlite3";
      web.http = "127.0.0.1:5556";
      staticClients = [
        {
          id = "outline";
          name = "Outline Client";
          redirectURIs = [ "https://outline.example.tld/auth/oidc.callback" ];
          secretFile = "${pkgs.writeText "outline-oidc-secret" "test123"}";
        }
      ];
      staticPasswords = [
        {
          email = "user.email@example.com";
          # bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2)
          hash = "10$TDh68T5XUK10$TDh68T5XUK10$TDh68T5XUK";
          username = "test";
          # easily generated with `$ uuidgen`
          userID = "6D196B03-8A28-4D6E-B849-9298168CBA34";
        }
      ];
    };
  };


It is possible to host a S3-compatible object storage using [[Minio]]. The following configuration enables a minimal, local Minio instance:


{{file|/etc/nixos/configuration.nix|nix|<nowiki>
services.minio = {
  enable = true;
  listenAddress = "127.0.0.1:9000";
  consoleAddress = "127.0.0.1:9001";
  # Storing secrets world-readable in the Nix store is not recommended.
  # This is only for demonstration purpose.
  rootCredentialsFile = "${pkgs.writeText "minio-secret" "test123"}";
};
</nowiki>}}
</nowiki>}}


Login into the Minio web console on http://127.0.0.1:9001 using the default credentials with user <code>minioadmin</code> and password <code>minioadmin</code>.
== Troubleshooting ==


* Create a new bucket and name it, for example <code>outline</code>.
=== Option storageType does not exist ===
* Create a new user. For demonstration purpose call it <code>outline</code> with the password <code>outline123</code>


Bucket name (<code>outline</code>), user (or accessKey: <code>outline</code>) and password (or the content of secretKeyFile: <code>outline123</code>) have to match the storage configuration of the Outline module above.
If you see an error that says something like <code>option "services.outline.storage.storageType" does not exist"</code> you may need to update your channels (<code>nix-channel --update</code>)


== See also ==
== See also ==
Line 128: Line 178:
* [[Dokuwiki]], simple PHP- and web-based wiki software which uses file based storage for its content.
* [[Dokuwiki]], simple PHP- and web-based wiki software which uses file based storage for its content.


[[Category:Server]]
[[Category:Web Applications]]
[[Category:Web Applications]]

Latest revision as of 19:16, 24 April 2024

Outline is a modern web based wiki and knowledge base for teams.

Setup

The most minimal local installation of Outline can be enabled with the following configuration

Note: Previous example used the mockPassword connector, which is an undocumented configuration option in Dex, used solely to run some tests. Use staticPasswords configuration instead, which allows using a hash for the password and setting a real email for the user which can be used to send notifications if an SMTP server is configured. Keep in mind, if you created your first user with the previous method (mockPassword), that user is your only admin user and you need to keep it around in order to confer admin privileges to the next user.
/etc/nixos/configuration.nix
{ config, pkgs, lib, ... }: {

networking.extraHosts = ''
  127.0.0.1 dex.localhost
'';

services = {

  outline = {
    enable = true;
    publicUrl = "http://localhost:3000";
    forceHttps = false;
    storage.storageType = "local";
    oidcAuthentication = {
      # Parts taken from
      # http://dex.localhost/.well-known/openid-configuration
      authUrl = "http://dex.localhost/auth";
      tokenUrl = "http://dex.localhost/token";
      userinfoUrl = "http://dex.localhost/userinfo";
      clientId = "outline";
      clientSecretFile = (builtins.elemAt config.services.dex.settings.staticClients 0).secretFile;
      scopes = [ "openid" "email" "profile" ];
      usernameClaim = "preferred_username";
      displayName = "Dex";
    };
  };

  dex = {
    enable = true;
    settings = {
      issuer = "http://dex.localhost";
      storage.type = "sqlite3";
      web.http = "127.0.0.1:5556";
      enablePasswordDB = true;
      staticClients = [
        {
          id = "outline";
          name = "Outline Client";
          redirectURIs = [ "http://localhost:3000/auth/oidc.callback" ];
          secretFile = "${pkgs.writeText "outline-oidc-secret" "test123"}";
        }
      ];
      staticPasswords = [
        {
          email = "user.email@example.com";
          # bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2)
          hash = "10$TDh68T5XUK10$TDh68T5XUK10$TDh68T5XUK";
          username = "test";
          # easily generated with `$ uuidgen`
          userID = "6D196B03-8A28-4D6E-B849-9298168CBA34";
        }
      ];
    };
  };

  nginx = {
    enable = true;
    virtualHosts = {
      "localhost" = {
        locations."/" = {
          proxyPass = "${config.services.outline.publicUrl}";
        };
      };
      "dex.localhost" = {
        locations."/" = {
          proxyPass = "http://${config.services.dex.settings.web.http}";
        };
      };
    };
  };

};

Outline is available at http://localhost . Choose login provider "Dex" and authenticate with the example mock login user.email@example.com and password.

SSL example

Similar as before but this time with Nginx handling SSL.

/etc/nixos/configuration.nix
{ config, pkgs, lib, ... }: {

  services.nginx = {
    enable = true;
    recommendedProxySettings = true;
    recommendedTlsSettings = true;
    virtualHosts = {
      "outline.example.tld" = {
        onlySSL = true;
        useACMEHost = "example.tld"; # assuming security.acme.certs."example.tld" with `extraDomainNames = [ "outline.example.tld" ]`
        locations."/" = {
          proxyPass = "http://localhost:${toString config.services.outline.port}";
          proxyWebsockets = true;
          extraConfig = ''
                proxy_set_header X-Scheme $scheme;
          '';
        };
      };
      "dex.example.tld" = {
        onlySSL = true;
        useACMEHost = "example.tld";
        locations."/" = {
          proxyPass = "http://${config.services.dex.settings.web.http}";
          proxyWebsockets = true;
        };
      };
    };
  };


  services.outline = {
    enable = true;
    publicUrl = "https://outline.example.tld";
    port = 3003; # using 3003 instead of default 3000 just in case another service is already using 3000
    forceHttps = false;
    storage.storageType = "local";
    oidcAuthentication = {
      authUrl = "https://dex.example.tld/auth";
      tokenUrl = "https://dex.example.tld/token";
      userinfoUrl = "https://dex.example.tld/userinfo";
      clientId = "outline";
      clientSecretFile = (builtins.elemAt config.services.dex.settings.staticClients 0).secretFile;
      scopes = [ "openid" "email" "profile" ];
      usernameClaim = "preferred_username";
      displayName = "Dex";
    };
  };

  services.dex = {
    enable = true;
    settings = {
      issuer = "https://dex.example.tld";
      storage.type = "sqlite3";
      web.http = "127.0.0.1:5556";
      staticClients = [
        {
          id = "outline";
          name = "Outline Client";
          redirectURIs = [ "https://outline.example.tld/auth/oidc.callback" ];
          secretFile = "${pkgs.writeText "outline-oidc-secret" "test123"}";
        }
      ];
      staticPasswords = [
        {
          email = "user.email@example.com";
          # bcrypt hash of the string "password": $(echo password | htpasswd -BinC 10 admin | cut -d: -f2)
          hash = "10$TDh68T5XUK10$TDh68T5XUK10$TDh68T5XUK";
          username = "test";
          # easily generated with `$ uuidgen`
          userID = "6D196B03-8A28-4D6E-B849-9298168CBA34";
        }
      ];
    };
  };

Troubleshooting

Option storageType does not exist

If you see an error that says something like option "services.outline.storage.storageType" does not exist" you may need to update your channels (nix-channel --update)

See also

  • Mediawiki, PHP- and web-based wiki software.
  • Dokuwiki, simple PHP- and web-based wiki software which uses file based storage for its content.