summary refs log tree commit diff
path: root/nixos
diff options
context:
space:
mode:
Diffstat (limited to 'nixos')
-rw-r--r--nixos/doc/manual/configuration/configuration.xml1
-rw-r--r--nixos/doc/manual/default.nix1
-rw-r--r--nixos/doc/manual/release-notes/rl-unstable.xml8
-rw-r--r--nixos/modules/module-list.nix1
-rw-r--r--nixos/modules/rename.nix3
-rw-r--r--nixos/modules/security/acme.nix2
-rw-r--r--nixos/modules/security/oath.nix50
-rw-r--r--nixos/modules/security/pam.nix17
-rw-r--r--nixos/modules/services/hardware/sane.nix17
-rw-r--r--nixos/modules/services/misc/defaultUnicornConfig.rb1
-rw-r--r--nixos/modules/services/misc/gitlab.nix390
-rw-r--r--nixos/modules/services/misc/gitlab.xml103
-rw-r--r--nixos/modules/services/misc/nix-daemon.nix2
-rw-r--r--nixos/modules/services/network-filesystems/samba.nix20
-rw-r--r--nixos/modules/services/printing/cupsd.nix2
-rw-r--r--nixos/modules/system/activation/switch-to-configuration.pl14
-rw-r--r--nixos/modules/virtualisation/ec2-data.nix24
-rw-r--r--nixos/tests/printing.nix5
18 files changed, 495 insertions, 166 deletions
diff --git a/nixos/doc/manual/configuration/configuration.xml b/nixos/doc/manual/configuration/configuration.xml
index 1e488b59343e..ff00c4feb31a 100644
--- a/nixos/doc/manual/configuration/configuration.xml
+++ b/nixos/doc/manual/configuration/configuration.xml
@@ -26,6 +26,7 @@ effect after you run <command>nixos-rebuild</command>.</para>
 
 <!-- FIXME: auto-include NixOS module docs -->
 <xi:include href="postgresql.xml" />
+<xi:include href="gitlab.xml" />
 <xi:include href="acme.xml" />
 <xi:include href="nixos.xml" />
 
diff --git a/nixos/doc/manual/default.nix b/nixos/doc/manual/default.nix
index b4eb3cde81bf..4eda7f94ab4a 100644
--- a/nixos/doc/manual/default.nix
+++ b/nixos/doc/manual/default.nix
@@ -56,6 +56,7 @@ let
       cp -prd $sources/* . # */
       chmod -R u+w .
       cp ${../../modules/services/databases/postgresql.xml} configuration/postgresql.xml
+      cp ${../../modules/services/misc/gitlab.xml} configuration/gitlab.xml
       cp ${../../modules/security/acme.xml} configuration/acme.xml
       cp ${../../modules/misc/nixos.xml} configuration/nixos.xml
       ln -s ${optionsDocBook} options-db.xml
diff --git a/nixos/doc/manual/release-notes/rl-unstable.xml b/nixos/doc/manual/release-notes/rl-unstable.xml
index 22e605718e33..5bad7f63b61e 100644
--- a/nixos/doc/manual/release-notes/rl-unstable.xml
+++ b/nixos/doc/manual/release-notes/rl-unstable.xml
@@ -231,6 +231,14 @@ programs.ibus.plugins = with pkgs; [ ibus-anthy mozc ];
     overriden by anything else.</para>
   </listitem>
 
+  <listitem>
+    <para>Large parts of the <literal>services.gitlab</literal> module has been
+    been rewritten. There are new configuration options available. The
+    <literal>stateDir</literal> option was renamned to
+    <literal>statePath</literal> and the <literal>satellitesDir</literal> option
+    was removed. Please review the currently available options.</para>
+  </listitem>
+
 </itemizedlist>
 
 
diff --git a/nixos/modules/module-list.nix b/nixos/modules/module-list.nix
index c2bcc361c447..f41e17189a42 100644
--- a/nixos/modules/module-list.nix
+++ b/nixos/modules/module-list.nix
@@ -93,6 +93,7 @@
   ./security/ca.nix
   ./security/duosec.nix
   ./security/grsecurity.nix
+  ./security/oath.nix
   ./security/pam.nix
   ./security/pam_usb.nix
   ./security/pam_mount.nix
diff --git a/nixos/modules/rename.nix b/nixos/modules/rename.nix
index 6d5ee6fc84e9..85435884b199 100644
--- a/nixos/modules/rename.nix
+++ b/nixos/modules/rename.nix
@@ -28,6 +28,9 @@ with lib;
     (mkRenamedOptionModule [ "services" "subsonic" "host" ] [ "services" "subsonic" "listenAddress" ])
     (mkRenamedOptionModule [ "jobs" ] [ "systemd" "services" ])
 
+    (mkRenamedOptionModule [ "services" "gitlab" "stateDir" ] [ "services" "gitlab" "statePath" ])
+    (mkRemovedOptionModule [ "services" "gitlab" "satelliteDir" ])
+
     # Old Grub-related options.
     (mkRenamedOptionModule [ "boot" "initrd" "extraKernelModules" ] [ "boot" "initrd" "kernelModules" ])
     (mkRenamedOptionModule [ "boot" "extraKernelParams" ] [ "boot" "kernelParams" ])
diff --git a/nixos/modules/security/acme.nix b/nixos/modules/security/acme.nix
index 3d25e811e670..c5cd0fb60ee9 100644
--- a/nixos/modules/security/acme.nix
+++ b/nixos/modules/security/acme.nix
@@ -56,7 +56,7 @@ let
 
       plugins = mkOption {
         type = types.listOf (types.enum [
-          "cert.der" "cert.pem" "chain.pem" "external_pem.sh"
+          "cert.der" "cert.pem" "chain.pem" "external.sh"
           "fullchain.pem" "full.pem" "key.der" "key.pem" "account_key.json"
         ]);
         default = [ "fullchain.pem" "key.pem" "account_key.json" ];
diff --git a/nixos/modules/security/oath.nix b/nixos/modules/security/oath.nix
new file mode 100644
index 000000000000..20f3e2dd9f83
--- /dev/null
+++ b/nixos/modules/security/oath.nix
@@ -0,0 +1,50 @@
+# This module provides configuration for the OATH PAM modules.
+
+{ config, lib, pkgs, ... }:
+
+with lib;
+
+{
+  options = {
+
+    security.pam.oath = {
+      enable = mkOption {
+        type = types.bool;
+        default = false;
+        description = ''
+          Enable the OATH (one-time password) PAM module.
+        '';
+      };
+
+      digits = mkOption {
+        type = types.enum [ 6 7 8 ];
+        default = 6;
+        description = ''
+          Specify the length of the one-time password in number of
+          digits.
+        '';
+      };
+
+      window = mkOption {
+        type = types.int;
+        default = 5;
+        description = ''
+          Specify the number of one-time passwords to check in order
+          to accommodate for situations where the system and the
+          client are slightly out of sync (iteration for HOTP or time
+          steps for TOTP).
+        '';
+      };
+
+      usersFile = mkOption {
+        type = types.path;
+        default = "/etc/users.oath";
+        description = ''
+          Set the path to file where the user's credentials are
+          stored. This file must not be world readable!
+        '';
+      };
+    };
+
+  };
+}
diff --git a/nixos/modules/security/pam.nix b/nixos/modules/security/pam.nix
index 2ee8a803d2fe..021c561af759 100644
--- a/nixos/modules/security/pam.nix
+++ b/nixos/modules/security/pam.nix
@@ -75,7 +75,7 @@ let
       };
 
       oathAuth = mkOption {
-        default = config.security.pam.enableOATH;
+        default = config.security.pam.oath.enable;
         type = types.bool;
         description = ''
           If set, the OATH Toolkit will be used.
@@ -259,8 +259,8 @@ let
               "auth sufficient pam_unix.so ${optionalString cfg.allowNullPassword "nullok"} likeauth try_first_pass"}
           ${optionalString cfg.otpwAuth
               "auth sufficient ${pkgs.otpw}/lib/security/pam_otpw.so"}
-          ${optionalString cfg.oathAuth
-              "auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=5 usersfile=/etc/users.oath"}
+          ${let oath = config.security.pam.oath; in optionalString cfg.oathAuth
+              "auth sufficient ${pkgs.oathToolkit}/lib/security/pam_oath.so window=${toString oath.window} usersfile=${toString oath.usersFile} digits=${toString oath.digits}"}
           ${optionalString config.users.ldap.enable
               "auth sufficient ${pam_ldap}/lib/security/pam_ldap.so use_first_pass"}
           ${optionalString config.krb5.enable ''
@@ -302,8 +302,6 @@ let
               "session optional ${pam_krb5}/lib/security/pam_krb5.so"}
           ${optionalString cfg.otpwAuth
               "session optional ${pkgs.otpw}/lib/security/pam_otpw.so"}
-          ${optionalString cfg.oathAuth
-              "session optional ${pkgs.oathToolkit}/lib/security/pam_oath.so window=5 usersfile=/etc/users.oath"}
           ${optionalString cfg.startSession
               "session optional ${pkgs.systemd}/lib/security/pam_systemd.so"}
           ${optionalString cfg.forwardXAuth
@@ -405,13 +403,6 @@ in
       '';
     };
 
-    security.pam.enableOATH = mkOption {
-      default = false;
-      description = ''
-        Enable the OATH (one-time password) PAM module.
-      '';
-    };
-
     security.pam.enableU2F = mkOption {
       default = false;
       description = ''
@@ -446,7 +437,7 @@ in
       ++ optional config.users.ldap.enable pam_ldap
       ++ optionals config.krb5.enable [pam_krb5 pam_ccreds]
       ++ optionals config.security.pam.enableOTPW [ pkgs.otpw ]
-      ++ optionals config.security.pam.enableOATH [ pkgs.oathToolkit ]
+      ++ optionals config.security.pam.oath.enable [ pkgs.oathToolkit ]
       ++ optionals config.security.pam.enableU2F [ pkgs.pam_u2f ]
       ++ optionals config.security.pam.enableEcryptfs [ pkgs.ecryptfs ];
 
diff --git a/nixos/modules/services/hardware/sane.nix b/nixos/modules/services/hardware/sane.nix
index 56504cd2361d..a34037403123 100644
--- a/nixos/modules/services/hardware/sane.nix
+++ b/nixos/modules/services/hardware/sane.nix
@@ -21,7 +21,13 @@ in
     hardware.sane.enable = mkOption {
       type = types.bool;
       default = false;
-      description = "Enable support for SANE scanners.";
+      description = ''
+        Enable support for SANE scanners.
+
+        <note><para>
+          Users in the "scanner" group will gain access to the scanner.
+        </para></note>
+      '';
     };
 
     hardware.sane.snapshot = mkOption {
@@ -33,7 +39,14 @@ in
     hardware.sane.extraBackends = mkOption {
       type = types.listOf types.path;
       default = [];
-      description = "Packages providing extra SANE backends to enable.";
+      description = ''
+        Packages providing extra SANE backends to enable.
+
+        <note><para>
+          The example contains the package for HP scanners.
+        </para></note>
+      '';
+      example = literalExample "[ pkgs.hplipWithPlugin ]";
     };
 
     hardware.sane.configDir = mkOption {
diff --git a/nixos/modules/services/misc/defaultUnicornConfig.rb b/nixos/modules/services/misc/defaultUnicornConfig.rb
index 81abaf336dc0..84622622db70 100644
--- a/nixos/modules/services/misc/defaultUnicornConfig.rb
+++ b/nixos/modules/services/misc/defaultUnicornConfig.rb
@@ -187,7 +187,6 @@ working_directory ENV["GITLAB_PATH"]
 pid ENV["UNICORN_PATH"] + "/tmp/pids/unicorn.pid"
 
 listen ENV["UNICORN_PATH"] + "/tmp/sockets/gitlab.socket", :backlog => 1024
-listen "127.0.0.1:8080", :tcp_nopush => true
 
 timeout 60
 
diff --git a/nixos/modules/services/misc/gitlab.nix b/nixos/modules/services/misc/gitlab.nix
index 949357ab20f4..cc50bfbea531 100644
--- a/nixos/modules/services/misc/gitlab.nix
+++ b/nixos/modules/services/misc/gitlab.nix
@@ -7,10 +7,13 @@ with lib;
 let
   cfg = config.services.gitlab;
 
-  ruby = pkgs.gitlab.ruby;
+  ruby = cfg.packages.gitlab.ruby;
   bundler = pkgs.bundler;
 
-  gemHome = "${pkgs.gitlab.env}/${ruby.gemPath}";
+  gemHome = "${cfg.packages.gitlab.env}/${ruby.gemPath}";
+
+  gitlabSocket = "${cfg.statePath}/tmp/sockets/gitlab.socket";
+  pathUrlQuote = url: replaceStrings ["/"] ["%2F"] url;
 
   databaseYml = ''
     production:
@@ -21,14 +24,15 @@ let
       username: ${cfg.databaseUsername}
       encoding: utf8
   '';
+
   gitlabShellYml = ''
-    user: gitlab
-    gitlab_url: "http://${cfg.host}:${toString cfg.port}/"
+    user: ${cfg.user}
+    gitlab_url: "http+unix://${pathUrlQuote gitlabSocket}"
     http_settings:
       self_signed_cert: false
-    repos_path: "${cfg.stateDir}/repositories"
-    secret_file: "${cfg.stateDir}/config/gitlab_shell_secret"
-    log_file: "${cfg.stateDir}/log/gitlab-shell.log"
+    repos_path: "${cfg.statePath}/repositories"
+    secret_file: "${cfg.statePath}/config/gitlab_shell_secret"
+    log_file: "${cfg.statePath}/log/gitlab-shell.log"
     redis:
       bin: ${pkgs.redis}/bin/redis-cli
       host: 127.0.0.1
@@ -37,33 +41,102 @@ let
       namespace: resque:gitlab
   '';
 
+  gitlabConfig = {
+    # These are the default settings from config/gitlab.example.yml
+    production = flip recursiveUpdate cfg.extraConfig {
+      gitlab = {
+        host = cfg.host;
+        port = cfg.port;
+        https = cfg.https;
+        user = cfg.user;
+        email_enabled = true;
+        email_display_name = "GitLab";
+        email_reply_to = "noreply@localhost";
+        default_theme = 2;
+        default_projects_features = {
+          issues = true;
+          merge_requests = true;
+          wiki = true;
+          snippets = false;
+          builds = true;
+        };
+      };
+      artifacts = {
+        enabled = true;
+      };
+      lfs = {
+        enabled = true;
+      };
+      gravatar = {
+        enabled = true;
+      };
+      cron_jobs = {
+        stuck_ci_builds_worker = {
+          cron = "0 0 * * *";
+        };
+      };
+      gitlab_ci = {
+        builds_path = "${cfg.statePath}/builds";
+      };
+      ldap = {
+        enabled = false;
+      };
+      omniauth = {
+        enabled = false;
+      };
+      shared = {
+        path = "${cfg.statePath}/shared";
+      };
+      backup = {
+        path = "${cfg.backupPath}";
+      };
+      gitlab_shell = {
+        path = "${cfg.packages.gitlab-shell}";
+        repos_path = "${cfg.statePath}/repositories";
+        hooks_path = "${cfg.statePath}/shell/hooks";
+        secret_file = "${cfg.statePath}/config/gitlab_shell_secret";
+        upload_pack = true;
+        receive_pack = true;
+      };
+      git = {
+        bin_path = "git";
+        max_size = 20971520; # 20MB
+        timeout = 10;
+      };
+      extra = {};
+    };
+  };
+
+  gitlabEnv = {
+    HOME = "${cfg.statePath}/home";
+    GEM_HOME = gemHome;
+    BUNDLE_GEMFILE = "${cfg.packages.gitlab}/share/gitlab/Gemfile";
+    UNICORN_PATH = "${cfg.statePath}/";
+    GITLAB_PATH = "${cfg.packages.gitlab}/share/gitlab/";
+    GITLAB_STATE_PATH = "${cfg.statePath}";
+    GITLAB_UPLOADS_PATH = "${cfg.statePath}/uploads";
+    GITLAB_LOG_PATH = "${cfg.statePath}/log";
+    GITLAB_SHELL_PATH = "${cfg.packages.gitlab-shell}";
+    GITLAB_SHELL_CONFIG_PATH = "${cfg.statePath}/shell/config.yml";
+    GITLAB_SHELL_SECRET_PATH = "${cfg.statePath}/config/gitlab_shell_secret";
+    GITLAB_SHELL_HOOKS_PATH = "${cfg.statePath}/shell/hooks";
+    RAILS_ENV = "production";
+  };
+
   unicornConfig = builtins.readFile ./defaultUnicornConfig.rb;
 
   gitlab-runner = pkgs.stdenv.mkDerivation rec {
     name = "gitlab-runner";
-    buildInputs = [ pkgs.gitlab pkgs.bundler pkgs.makeWrapper ];
+    buildInputs = [ cfg.packages.gitlab bundler pkgs.makeWrapper ];
     phases = "installPhase fixupPhase";
     buildPhase = "";
     installPhase = ''
       mkdir -p $out/bin
-      makeWrapper ${bundler}/bin/bundle $out/bin/gitlab-runner\
-          --set RAKEOPT '"-f ${pkgs.gitlab}/share/gitlab/Rakefile"'\
-          --set GEM_HOME '${gemHome}'\
-          --set UNICORN_PATH "${cfg.stateDir}/"\
-          --set GITLAB_PATH "${pkgs.gitlab}/share/gitlab/"\
-          --set GITLAB_APPLICATION_LOG_PATH "${cfg.stateDir}/log/application.log"\
-          --set GITLAB_SATELLITES_PATH "${cfg.stateDir}/satellites"\
-          --set GITLAB_SHELL_PATH "${pkgs.gitlab-shell}"\
-          --set GITLAB_REPOSITORIES_PATH "${cfg.stateDir}/repositories"\
-          --set GITLAB_SHELL_HOOKS_PATH "${cfg.stateDir}/shell/hooks"\
-          --set BUNDLE_GEMFILE "${pkgs.gitlab}/share/gitlab/Gemfile"\
-          --set GITLAB_EMAIL_FROM "${cfg.emailFrom}"\
-          --set GITLAB_SHELL_CONFIG_PATH "${cfg.stateDir}/shell/config.yml"\
-          --set GITLAB_SHELL_SECRET_PATH "${cfg.stateDir}/config/gitlab_shell_secret"\
-          --set GITLAB_HOST "${cfg.host}"\
-          --set GITLAB_PORT "${toString cfg.port}"\
-          --set GITLAB_BACKUP_PATH "${cfg.backupPath}"\
-          --set RAILS_ENV "production"
+      makeWrapper ${bundler}/bin/bundle $out/bin/gitlab-runner \
+          ${concatStrings (mapAttrsToList (name: value: "--set ${name} '\"${value}\"' ") gitlabEnv)} \
+          --set GITLAB_CONFIG_PATH '"${cfg.statePath}/config"' \
+          --set PATH '"${pkgs.nodejs}/bin:${pkgs.gzip}/bin:${config.services.postgresql.package}/bin:$PATH"' \
+          --set RAKEOPT '"-f ${cfg.packages.gitlab}/share/gitlab/Rakefile"'
     '';
   };
 
@@ -79,13 +152,25 @@ in {
         '';
       };
 
-      satelliteDir = mkOption {
-        type = types.str;
-        default = "/var/gitlab/git-satellites";
-        description = "Gitlab directory to store checked out git trees requires for operation.";
+      packages.gitlab = mkOption {
+        type = types.package;
+        default = pkgs.gitlab;
+        description = "Reference to the gitlab package";
+      };
+
+      packages.gitlab-shell = mkOption {
+        type = types.package;
+        default = pkgs.gitlab-shell;
+        description = "Reference to the gitlab-shell package";
+      };
+
+      packages.gitlab-workhorse = mkOption {
+        type = types.package;
+        default = pkgs.gitlab-workhorse;
+        description = "Reference to the gitlab-workhorse package";
       };
 
-      stateDir = mkOption {
+      statePath = mkOption {
         type = types.str;
         default = "/var/gitlab/state";
         description = "Gitlab state directory, logs are stored here.";
@@ -93,7 +178,7 @@ in {
 
       backupPath = mkOption {
         type = types.str;
-        default = cfg.stateDir + "/backup";
+        default = cfg.statePath + "/backup";
         description = "Gitlab path for backups.";
       };
 
@@ -136,14 +221,67 @@ in {
       port = mkOption {
         type = types.int;
         default = 8080;
-        description = "Gitlab server listening port.";
+        description = ''
+          Gitlab server port for copy-paste URLs, e.g. 80 or 443 if you're
+          service over https.
+        '';
+      };
+
+      https = mkOption {
+        type = types.bool;
+        default = false;
+        description = "Whether gitlab prints URLs with https as scheme.";
+      };
+
+      user = mkOption {
+        type = types.str;
+        default = "gitlab";
+        description = "User to run gitlab and all related services.";
+      };
+
+      group = mkOption {
+        type = types.str;
+        default = "gitlab";
+        description = "Group to run gitlab and all related services.";
+      };
+
+      initialRootEmail = mkOption {
+        type = types.str;
+        default = "admin@local.host";
+        description = ''
+          Initial email address of the root account if this is a new install.
+        '';
+      };
+
+      initialRootPassword = mkOption {
+        type = types.str;
+        default = "UseNixOS!";
+        description = ''
+          Initial password of the root account if this is a new install.
+        '';
+      };
+
+      extraConfig = mkOption {
+        type = types.attrs;
+        default = {};
+        example = {
+          gitlab = {
+            default_projects_features = {
+              builds = false;
+            };
+          };
+        };
+        description = ''
+          Extra options to be merged into config/gitlab.yml as nix
+          attribute set.
+        '';
       };
     };
   };
 
   config = mkIf cfg.enable {
 
-    environment.systemPackages = [ pkgs.git gitlab-runner pkgs.gitlab-shell ];
+    environment.systemPackages = [ pkgs.git gitlab-runner cfg.packages.gitlab-shell ];
 
     assertions = [
       { assertion = cfg.databasePassword != "";
@@ -159,39 +297,24 @@ in {
     services.postfix.enable = mkDefault true;
 
     users.extraUsers = [
-      { name = "gitlab";
-        group = "gitlab";
-        home = "${cfg.stateDir}/home";
+      { name = cfg.user;
+        group = cfg.group;
+        home = "${cfg.statePath}/home";
         shell = "${pkgs.bash}/bin/bash";
         uid = config.ids.uids.gitlab;
-      } ];
+      }
+    ];
 
     users.extraGroups = [
-      { name = "gitlab";
+      { name = cfg.group;
         gid = config.ids.gids.gitlab;
-      } ];
+      }
+    ];
 
     systemd.services.gitlab-sidekiq = {
       after = [ "network.target" "redis.service" ];
       wantedBy = [ "multi-user.target" ];
-      environment.HOME = "${cfg.stateDir}/home";
-      environment.GEM_HOME = gemHome;
-      environment.UNICORN_PATH = "${cfg.stateDir}/";
-      environment.GITLAB_PATH = "${pkgs.gitlab}/share/gitlab/";
-      environment.GITLAB_APPLICATION_LOG_PATH = "${cfg.stateDir}/log/application.log";
-      environment.GITLAB_SATELLITES_PATH = "${cfg.stateDir}/satellites";
-      environment.GITLAB_SHELL_PATH = "${pkgs.gitlab-shell}";
-      environment.GITLAB_REPOSITORIES_PATH = "${cfg.stateDir}/repositories";
-      environment.GITLAB_SHELL_HOOKS_PATH = "${cfg.stateDir}/shell/hooks";
-      environment.BUNDLE_GEMFILE = "${pkgs.gitlab}/share/gitlab/Gemfile";
-      environment.GITLAB_EMAIL_FROM = "${cfg.emailFrom}";
-      environment.GITLAB_SHELL_CONFIG_PATH = "${cfg.stateDir}/shell/config.yml";
-      environment.GITLAB_SHELL_SECRET_PATH = "${cfg.stateDir}/config/gitlab_shell_secret";
-      environment.GITLAB_HOST = "${cfg.host}";
-      environment.GITLAB_PORT = "${toString cfg.port}";
-      environment.GITLAB_DATABASE_HOST = "${cfg.databaseHost}";
-      environment.GITLAB_DATABASE_PASSWORD = "${cfg.databasePassword}";
-      environment.RAILS_ENV = "production";
+      environment = gitlabEnv;
       path = with pkgs; [
         config.services.postgresql.package
         gitAndTools.git
@@ -201,116 +324,131 @@ in {
       ];
       serviceConfig = {
         Type = "simple";
-        User = "gitlab";
-        Group = "gitlab";
+        User = cfg.user;
+        Group = cfg.group;
         TimeoutSec = "300";
-        WorkingDirectory = "${pkgs.gitlab}/share/gitlab";
-        ExecStart="${bundler}/bin/bundle exec \"sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.stateDir}/tmp/sidekiq.pid\"";
+        WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
+        ExecStart="${bundler}/bin/bundle exec \"sidekiq -q post_receive -q mailer -q system_hook -q project_web_hook -q gitlab_shell -q common -q default -e production -P ${cfg.statePath}/tmp/sidekiq.pid\"";
       };
     };
 
-    systemd.services.gitlab-git-http-server = {
+    systemd.services.gitlab-workhorse = {
       after = [ "network.target" "gitlab.service" ];
       wantedBy = [ "multi-user.target" ];
-      environment.HOME = "${cfg.stateDir}/home";
+      environment.HOME = gitlabEnv.HOME;
+      environment.GITLAB_SHELL_CONFIG_PATH = gitlabEnv.GITLAB_SHELL_CONFIG_PATH;
       path = with pkgs; [
         gitAndTools.git
         openssh
       ];
+      preStart = ''
+        mkdir -p /run/gitlab
+        chown ${cfg.user}:${cfg.group} /run/gitlab
+      '';
       serviceConfig = {
+        PermissionsStartOnly = true; # preStart must be run as root
         Type = "simple";
-        User = "gitlab";
-        Group = "gitlab";
+        User = cfg.user;
+        Group = cfg.group;
         TimeoutSec = "300";
-        ExecStart = "${pkgs.gitlab-git-http-server}/bin/gitlab-git-http-server -listenUmask 0 -listenNetwork unix -listenAddr ${cfg.stateDir}/tmp/sockets/gitlab-git-http-server.socket -authBackend http://localhost:8080 ${cfg.stateDir}/repositories";
+        ExecStart =
+          "${cfg.packages.gitlab-workhorse}/bin/gitlab-workhorse "
+          + "-listenUmask 0 "
+          + "-listenNetwork unix "
+          + "-listenAddr /run/gitlab/gitlab-workhorse.socket "
+          + "-authSocket ${gitlabSocket} "
+          + "-documentRoot ${cfg.packages.gitlab}/share/gitlab/public";
       };
     };
 
     systemd.services.gitlab = {
       after = [ "network.target" "postgresql.service" "redis.service" ];
       wantedBy = [ "multi-user.target" ];
-      environment.HOME = "${cfg.stateDir}/home";
-      environment.GEM_HOME = gemHome;
-      environment.UNICORN_PATH = "${cfg.stateDir}/";
-      environment.GITLAB_PATH = "${pkgs.gitlab}/share/gitlab/";
-      environment.GITLAB_APPLICATION_LOG_PATH = "${cfg.stateDir}/log/application.log";
-      environment.GITLAB_SATELLITES_PATH = "${cfg.stateDir}/satellites";
-      environment.GITLAB_SHELL_PATH = "${pkgs.gitlab-shell}";
-      environment.GITLAB_SHELL_CONFIG_PATH = "${cfg.stateDir}/shell/config.yml";
-      environment.GITLAB_SHELL_SECRET_PATH = "${cfg.stateDir}/config/gitlab_shell_secret";
-      environment.GITLAB_REPOSITORIES_PATH = "${cfg.stateDir}/repositories";
-      environment.GITLAB_SHELL_HOOKS_PATH = "${cfg.stateDir}/shell/hooks";
-      environment.BUNDLE_GEMFILE = "${pkgs.gitlab}/share/gitlab/Gemfile";
-      environment.GITLAB_EMAIL_FROM = "${cfg.emailFrom}";
-      environment.GITLAB_HOST = "${cfg.host}";
-      environment.GITLAB_PORT = "${toString cfg.port}";
-      environment.GITLAB_DATABASE_HOST = "${cfg.databaseHost}";
-      environment.GITLAB_DATABASE_PASSWORD = "${cfg.databasePassword}";
-      environment.RAILS_ENV = "production";
+      environment = gitlabEnv;
       path = with pkgs; [
         config.services.postgresql.package
         gitAndTools.git
-        ruby
         openssh
         nodejs
       ];
       preStart = ''
-        # TODO: use env vars
-        mkdir -p ${cfg.stateDir}
-        mkdir -p ${cfg.stateDir}/log
-        mkdir -p ${cfg.stateDir}/satellites
-        mkdir -p ${cfg.stateDir}/repositories
-        mkdir -p ${cfg.stateDir}/shell/hooks
-        mkdir -p ${cfg.stateDir}/tmp/pids
-        mkdir -p ${cfg.stateDir}/tmp/sockets
-        rm -rf ${cfg.stateDir}/config
-        mkdir -p ${cfg.stateDir}/config
-        # TODO: What exactly is gitlab-shell doing with the secret?
-        tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 20 > ${cfg.stateDir}/config/gitlab_shell_secret
-        mkdir -p ${cfg.stateDir}/home/.ssh
-        touch ${cfg.stateDir}/home/.ssh/authorized_keys
-
-        cp -rf ${pkgs.gitlab}/share/gitlab/config ${cfg.stateDir}/
-        cp ${pkgs.gitlab}/share/gitlab/VERSION ${cfg.stateDir}/VERSION
+        mkdir -p ${cfg.backupPath}
+        mkdir -p ${cfg.statePath}/builds
+        mkdir -p ${cfg.statePath}/repositories
+        mkdir -p ${gitlabConfig.production.shared.path}/artifacts
+        mkdir -p ${gitlabConfig.production.shared.path}/lfs-objects
+        mkdir -p ${cfg.statePath}/log
+        mkdir -p ${cfg.statePath}/shell
+        mkdir -p ${cfg.statePath}/tmp/pids
+        mkdir -p ${cfg.statePath}/tmp/sockets
+
+        rm -rf ${cfg.statePath}/config ${cfg.statePath}/shell/hooks
+        mkdir -p ${cfg.statePath}/config ${cfg.statePath}/shell
 
-        ln -fs ${pkgs.writeText "database.yml" databaseYml} ${cfg.stateDir}/config/database.yml
-        ln -fs ${pkgs.writeText "unicorn.rb" unicornConfig} ${cfg.stateDir}/config/unicorn.rb
-
-        chown -R gitlab:gitlab ${cfg.stateDir}/
-        chmod -R 755 ${cfg.stateDir}/
+        # TODO: What exactly is gitlab-shell doing with the secret?
+        tr -dc _A-Z-a-z-0-9 < /dev/urandom | head -c 20 > ${cfg.statePath}/config/gitlab_shell_secret
+
+        # The uploads directory is hardcoded somewhere deep in rails. It is
+        # symlinked in the gitlab package to /run/gitlab/uploads to make it
+        # configurable
+        mkdir -p /run/gitlab
+        mkdir -p ${cfg.statePath}/uploads
+        ln -sf ${cfg.statePath}/uploads /run/gitlab/uploads
+        chown -R ${cfg.user}:${cfg.group} /run/gitlab
+
+        # Prepare home directory
+        mkdir -p ${gitlabEnv.HOME}/.ssh
+        touch ${gitlabEnv.HOME}/.ssh/authorized_keys
+        chown -R ${cfg.user}:${cfg.group} ${gitlabEnv.HOME}/
+        chmod -R u+rwX,go-rwx+X ${gitlabEnv.HOME}/
+
+        cp -rf ${cfg.packages.gitlab}/share/gitlab/config.dist/* ${cfg.statePath}/config
+        ln -sf ${cfg.statePath}/config /run/gitlab/config
+        cp ${cfg.packages.gitlab}/share/gitlab/VERSION ${cfg.statePath}/VERSION
+
+        # JSON is a subset of YAML
+        ln -fs ${pkgs.writeText "gitlab.yml" (builtins.toJSON gitlabConfig)} ${cfg.statePath}/config/gitlab.yml
+        ln -fs ${pkgs.writeText "database.yml" databaseYml} ${cfg.statePath}/config/database.yml
+        ln -fs ${pkgs.writeText "unicorn.rb" unicornConfig} ${cfg.statePath}/config/unicorn.rb
+
+        chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}/
+        chmod -R ug+rwX,o-rwx+X ${cfg.statePath}/
+
+        # Install the shell required to push repositories
+        ln -fs ${pkgs.writeText "config.yml" gitlabShellYml} "$GITLAB_SHELL_CONFIG_PATH"
+        ln -fs ${cfg.packages.gitlab-shell}/hooks "$GITLAB_SHELL_HOOKS_PATH"
+        ${cfg.packages.gitlab-shell}/bin/install
 
         if [ "${cfg.databaseHost}" = "127.0.0.1" ]; then
-          if ! test -e "${cfg.stateDir}/db-created"; then
+          if ! test -e "${cfg.statePath}/db-created"; then
             psql postgres -c "CREATE ROLE gitlab WITH LOGIN NOCREATEDB NOCREATEROLE NOCREATEUSER ENCRYPTED PASSWORD '${cfg.databasePassword}'"
             ${config.services.postgresql.package}/bin/createdb --owner gitlab gitlab || true
-            touch "${cfg.stateDir}/db-created"
+            touch "${cfg.statePath}/db-created"
 
-            # force=yes disables the manual-interaction yes/no prompt
-            # which breaks without an stdin.
-            force=yes ${bundler}/bin/bundle exec rake -f ${pkgs.gitlab}/share/gitlab/Rakefile gitlab:setup RAILS_ENV=production
+            # The gitlab:setup task is horribly broken somehow, these two tasks will do the same for setting up the initial database
+            ${gitlab-runner}/bin/gitlab-runner exec rake db:migrate RAILS_ENV=production
+            ${gitlab-runner}/bin/gitlab-runner exec rake db:seed_fu RAILS_ENV=production \
+              GITLAB_ROOT_PASSWORD="${cfg.initialRootPassword}" GITLAB_ROOT_EMAIL="${cfg.initialRootEmail}";
           fi
         fi
 
-      ${bundler}/bin/bundle exec rake -f ${pkgs.gitlab}/share/gitlab/Rakefile db:migrate RAILS_ENV=production
-      # Install the shell required to push repositories
-      ln -fs ${pkgs.writeText "config.yml" gitlabShellYml} ${cfg.stateDir}/shell/config.yml
-      export GITLAB_SHELL_CONFIG_PATH=""${cfg.stateDir}/shell/config.yml
-      ${pkgs.gitlab-shell}/bin/install
+        # Always do the db migrations just to be sure the database is up-to-date
+        ${gitlab-runner}/bin/gitlab-runner exec rake db:migrate RAILS_ENV=production
 
-      # Change permissions in the last step because some of the
-      # intermediary scripts like to create directories as root.
-      chown -R gitlab:gitlab ${cfg.stateDir}/
-      chmod -R 755 ${cfg.stateDir}/
+        # Change permissions in the last step because some of the
+        # intermediary scripts like to create directories as root.
+        chown -R ${cfg.user}:${cfg.group} ${cfg.statePath}
+        chmod -R u+rwX,go-rwx+X ${cfg.statePath}
       '';
 
       serviceConfig = {
         PermissionsStartOnly = true; # preStart must be run as root
         Type = "simple";
-        User = "gitlab";
-        Group = "gitlab";
+        User = cfg.user;
+        Group = cfg.group;
         TimeoutSec = "300";
-        WorkingDirectory = "${pkgs.gitlab}/share/gitlab";
-        ExecStart="${bundler}/bin/bundle exec \"unicorn -c ${cfg.stateDir}/config/unicorn.rb -E production\"";
+        WorkingDirectory = "${cfg.packages.gitlab}/share/gitlab";
+        ExecStart="${bundler}/bin/bundle exec \"unicorn -c ${cfg.statePath}/config/unicorn.rb -E production\"";
       };
 
     };
diff --git a/nixos/modules/services/misc/gitlab.xml b/nixos/modules/services/misc/gitlab.xml
new file mode 100644
index 000000000000..b630fe421130
--- /dev/null
+++ b/nixos/modules/services/misc/gitlab.xml
@@ -0,0 +1,103 @@
+<chapter xmlns="http://docbook.org/ns/docbook"
+         xmlns:xlink="http://www.w3.org/1999/xlink"
+         xmlns:xi="http://www.w3.org/2001/XInclude"
+         version="5.0"
+         xml:id="module-services-gitlab">
+
+<title>Gitlab</title>
+
+<para>Gitlab is a feature-rich git hosting service.</para>
+
+<section><title>Prerequisites</title>
+
+<para>The gitlab service exposes only an Unix socket at
+<literal>/run/gitlab/gitlab-workhorse.socket</literal>. You need to configure a
+webserver to proxy HTTP requests to the socket.</para>
+
+<para>For instance, this could be used for Nginx:
+
+<programlisting>
+services.nginx.httpConfig = ''
+  server {
+    server_name git.example.com;
+    listen 443 ssl spdy;
+    listen [::]:443 ssl spdy;
+
+    ssl_certificate /var/lib/acme/git.example.com/fullchain.pem;
+    ssl_certificate_key /var/lib/acme/git.example.com/key.pem;
+
+    location / {
+      proxy_http_version 1.1;
+      proxy_set_header    Host                $http_host;
+      proxy_set_header    X-Real-IP           $remote_addr;
+      proxy_set_header    X-Forwarded-Ssl     on;
+      proxy_set_header    X-Forwarded-For     $proxy_add_x_forwarded_for;
+      proxy_set_header    X-Forwarded-Proto   $scheme;
+
+      proxy_pass http://unix:/run/gitlab/gitlab-workhorse.socket;
+    }
+  }
+'';
+</programlisting>
+</para>
+
+</section>
+
+<section><title>Configuring</title>
+
+<para>Gitlab depends on both PostgreSQL and Redis and will automatically enable
+both services. In the case of PostgreSQL, a database and a role will be created.
+</para>
+
+<para>The default state dir is /var/gitlab/state. This is where all data like
+the repositories and uploads will be stored.</para>
+
+<para>A basic configuration could look like this:
+
+<programlisting>
+services.gitlab = {
+  enable = true;
+  databasePassword = "eXaMpl3";
+  initialRootPassword = "UseNixOS!";
+  https = true;
+  host = "git.example.com";
+  port = 443;
+  user = "git";
+  group = "git";
+  extraConfig = {
+    gitlab = {
+      default_projects_features = { builds = false; };
+    };
+  };
+};
+</programlisting>
+</para>
+
+<para>Refer to <xref linkend="ch-options" /> for all available configuration
+options for the <literal>services.gitlab</literal> module.</para>
+
+</section>
+
+<section><title>Maintenance</title>
+
+<para>You can run all Gitlab related commands like rake tasks with
+<literal>gitlab-runner</literal> which will be available on the system
+when gitlab is enabled. You will have to run the commands as the user that
+you configured to run gitlab.</para>
+
+<para>For instance, to backup a Gitlab instance:
+
+<programlisting>
+$ sudo -u git -H gitlab-runner exec rake gitlab:backup:create
+</programlisting>
+
+A list of all availabe rake tasks can be obtained by running:
+
+<programlisting>
+$ sudo -u git -H gitlab-runner exec rake -T
+</programlisting>
+</para>
+
+</section>
+
+</chapter>
diff --git a/nixos/modules/services/misc/nix-daemon.nix b/nixos/modules/services/misc/nix-daemon.nix
index 8e984727a80e..cf2ee20801ab 100644
--- a/nixos/modules/services/misc/nix-daemon.nix
+++ b/nixos/modules/services/misc/nix-daemon.nix
@@ -367,6 +367,8 @@ in
           // { CURL_CA_BUNDLE = "/etc/ssl/certs/ca-certificates.crt"; }
           // config.networking.proxy.envVars;
 
+        unitConfig.RequiresMountsFor = "/nix/store";
+
         serviceConfig =
           { Nice = cfg.daemonNiceLevel;
             IOSchedulingPriority = cfg.daemonIONiceLevel;
diff --git a/nixos/modules/services/network-filesystems/samba.nix b/nixos/modules/services/network-filesystems/samba.nix
index 576e5c9e87a3..a186982ec9c0 100644
--- a/nixos/modules/services/network-filesystems/samba.nix
+++ b/nixos/modules/services/network-filesystems/samba.nix
@@ -79,6 +79,14 @@ in
         description = ''
           Whether to enable Samba, which provides file and print
           services to Windows clients through the SMB/CIFS protocol.
+
+          <note>
+            <para>If you use the firewall consider adding the following:</para>
+            <programlisting>
+              networking.firewall.allowedTCPPorts = [ 139 445 ];
+              networking.firewall.allowedUDPPorts = [ 137 138 ];
+            </programlisting>
+          </note>
         '';
       };
 
@@ -86,7 +94,7 @@ in
         type = types.package;
         default = pkgs.samba;
         defaultText = "pkgs.samba";
-        example = literalExample "pkgs.samba4";
+        example = literalExample "pkgs.samba3";
         description = ''
           Defines which package should be used for the samba server.
         '';
@@ -118,6 +126,10 @@ in
         description = ''
           Additional global section and extra section lines go in here.
         '';
+        example = ''
+          guest account = nobody
+          map to guest = bad user
+        '';
       };
 
       configText = mkOption {
@@ -154,9 +166,11 @@ in
         '';
         type = types.attrsOf (types.attrsOf types.unspecified);
         example =
-          { srv =
-             { path = "/srv";
+          { public =
+             { path = "/srv/public";
                "read only" = true;
+               browseable = "yes";
+               "guest ok" = "yes";
                 comment = "Public samba share.";
              };
           };
diff --git a/nixos/modules/services/printing/cupsd.nix b/nixos/modules/services/printing/cupsd.nix
index 98fb33e3256c..80d11565e47d 100644
--- a/nixos/modules/services/printing/cupsd.nix
+++ b/nixos/modules/services/printing/cupsd.nix
@@ -310,7 +310,7 @@ in
               [ ! -e "/var/lib/cups/$i" ] && ln -s "${rootdir}/etc/cups/$i" "/var/lib/cups/$i"
             done
             ${optionalString cfg.gutenprint ''
-              ${gutenprint}/bin/cups-genppdupdate
+              ${gutenprint}/bin/cups-genppdupdate -p /etc/cups/ppd
             ''}
           '';
       };
diff --git a/nixos/modules/system/activation/switch-to-configuration.pl b/nixos/modules/system/activation/switch-to-configuration.pl
index d9048427801f..093b7f1ff225 100644
--- a/nixos/modules/system/activation/switch-to-configuration.pl
+++ b/nixos/modules/system/activation/switch-to-configuration.pl
@@ -261,12 +261,12 @@ while (my ($unit, $state) = each %{$activePrev}) {
 
 sub pathToUnitName {
     my ($path) = @_;
-    die unless substr($path, 0, 1) eq "/";
-    return "-" if $path eq "/";
-    $path = substr($path, 1);
-    $path =~ s/\//-/g;
-    # FIXME: handle - and unprintable characters.
-    return $path;
+    open my $cmd, "-|", "systemd-escape", "--suffix=mount", "-p", $path
+        or die "Unable to escape $path!\n";
+    my $escaped = join "", <$cmd>;
+    chomp $escaped;
+    close $cmd or die;
+    return $escaped;
 }
 
 sub unique {
@@ -290,7 +290,7 @@ my ($newFss, $newSwaps) = parseFstab "$out/etc/fstab";
 foreach my $mountPoint (keys %$prevFss) {
     my $prev = $prevFss->{$mountPoint};
     my $new = $newFss->{$mountPoint};
-    my $unit = pathToUnitName($mountPoint) . ".mount";
+    my $unit = pathToUnitName($mountPoint);
     if (!defined $new) {
         # Filesystem entry disappeared, so unmount it.
         $unitsToStop{$unit} = 1;
diff --git a/nixos/modules/virtualisation/ec2-data.nix b/nixos/modules/virtualisation/ec2-data.nix
index bee262612680..db3dd9949c12 100644
--- a/nixos/modules/virtualisation/ec2-data.nix
+++ b/nixos/modules/virtualisation/ec2-data.nix
@@ -43,18 +43,20 @@ with lib;
 
             mkdir -m 0755 -p /etc/ssh
 
-            key="$(sed 's/|/\n/g; s/SSH_HOST_DSA_KEY://; t; d' $userData)"
-            key_pub="$(sed 's/SSH_HOST_DSA_KEY_PUB://; t; d' $userData)"
-            if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_dsa_key ]; then
-                (umask 077; echo "$key" > /etc/ssh/ssh_host_dsa_key)
-                echo "$key_pub" > /etc/ssh/ssh_host_dsa_key.pub
-            fi
+            if [ -s "$userData" ]; then
+              key="$(sed 's/|/\n/g; s/SSH_HOST_DSA_KEY://; t; d' $userData)"
+              key_pub="$(sed 's/SSH_HOST_DSA_KEY_PUB://; t; d' $userData)"
+              if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_dsa_key ]; then
+                  (umask 077; echo "$key" > /etc/ssh/ssh_host_dsa_key)
+                  echo "$key_pub" > /etc/ssh/ssh_host_dsa_key.pub
+              fi
 
-            key="$(sed 's/|/\n/g; s/SSH_HOST_ED25519_KEY://; t; d' $userData)"
-            key_pub="$(sed 's/SSH_HOST_ED25519_KEY_PUB://; t; d' $userData)"
-            if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_ed25519_key ]; then
-                (umask 077; echo "$key" > /etc/ssh/ssh_host_ed25519_key)
-                echo "$key_pub" > /etc/ssh/ssh_host_ed25519_key.pub
+              key="$(sed 's/|/\n/g; s/SSH_HOST_ED25519_KEY://; t; d' $userData)"
+              key_pub="$(sed 's/SSH_HOST_ED25519_KEY_PUB://; t; d' $userData)"
+              if [ -n "$key" -a -n "$key_pub" -a ! -e /etc/ssh/ssh_host_ed25519_key ]; then
+                  (umask 077; echo "$key" > /etc/ssh/ssh_host_ed25519_key)
+                  echo "$key_pub" > /etc/ssh/ssh_host_ed25519_key.pub
+              fi
             fi
           '';
 
diff --git a/nixos/tests/printing.nix b/nixos/tests/printing.nix
index 02980cee2fbd..10d69b446cd7 100644
--- a/nixos/tests/printing.nix
+++ b/nixos/tests/printing.nix
@@ -78,7 +78,7 @@ import ./make-test.nix ({pkgs, ... }: {
               # (showing that the right filters have been applied).  Of
               # course, since there is no actual USB printer attached, the
               # file will stay in the queue forever.
-              $server->waitForFile("/var/spool/cups/d00001-001");
+              $server->waitForFile("/var/spool/cups/d*-001");
               $server->sleep(10);
               $server->succeed("lpq -a") =~ /$fn/ or die;
 
@@ -90,6 +90,9 @@ import ./make-test.nix ({pkgs, ... }: {
               Machine::retry sub {
                 return 1 if $server->succeed("lpq -a") =~ /no entries/;
               };
+              # The queue is empty already, so this should be safe.
+              # Otherwise, pairs of "c*"-"d*-001" files might persist.
+              $server->execute("rm /var/spool/cups/*");
           };
       }
     '';