Commit 2b5981c5 authored by Nicolas Benes's avatar Nicolas Benes
Browse files

fetchgitlab: pass private tokens/passwords

Allow the fetcher to authenticate with user name and password.
Note: as of now, GitLab ignores the user name (it must be set, but the
value does not matter) but this may change in the future:
https://gitlab.com/gitlab-org/gitlab/-/issues/212953

Credentials can be passed to the nix-daemon, for example, via a
read-protected `EnvironmentFile`:

```console
$ ls -l /to/secrets.txt
-rw------- 1 root root 100 Nov  1 10:42 /to/secrets.txt
```

In /to/secrets.txt:

```
 # for `fetchFromGitLab { private=true; ... }`
NIX_GITLAB_PRIVATE_USERNAME=whatever
NIX_GITLAB_PRIVATE_PASSWORD=glpat-the-access-token
 # for `fetchFromGitLab { private=true; varPrefix="EXAMPLE"; ... }`
NIX_EXAMPLE_GITLAB_PRIVATE_USERNAME=whatever
NIX_EXAMPLE_GITLAB_PRIVATE_PASSWORD=glpat-another-access-token
```

In /etc/nixos/configuration.nix:

```nix
{ config, pkgs, ... }:
{
  systemd.services.nix-daemon.serviceConfig.EnvironmentFile =
    "/to/secrets.txt";
}
```

GitLab supports HTTP Basic Authentication (credentials in `.netrc` file)
only if accessed via Git. Access via the GitLab API requires a custom
header (e.g. `PRIVATE-TOKEN`) instead. See:

* https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#project-access-tokens
* https://docs.gitlab.com/ee/api/rest/authentication.html#personalprojectgroup-access-tokens
parent b5a31418
Loading
Loading
Loading
Loading
+46 −0
Original line number Diff line number Diff line
@@ -21,6 +21,8 @@ lib.makeOverridable (
    deepClone ? false,
    forceFetchGit ? false,
    sparseCheckout ? [ ],
    private ? false,
    varPrefix ? null,
    ... # For hash agility
  }@args:

@@ -51,14 +53,57 @@ lib.makeOverridable (
      "tag"
      "fetchSubmodules"
      "forceFetchGit"
      "private"
      "varPrefix"
      "leaveDotGit"
      "deepClone"
    ];

    varBase = "NIX${lib.optionalString (varPrefix != null) "_${varPrefix}"}_GITLAB_PRIVATE_";
    useFetchGit =
      fetchSubmodules || leaveDotGit || deepClone || forceFetchGit || (sparseCheckout != [ ]);
    fetcher = if useFetchGit then fetchgit else fetchzip;

    privateAttrs = lib.optionalAttrs private (
      lib.throwIfNot (protocol == "https") "private token login is only supported for https" {
        netrcPhase = ''
          if [ -z "''$${varBase}USERNAME" -o -z "''$${varBase}PASSWORD" ]; then
            echo "Error: Private fetchFromGitLab requires the nix building process (nix-daemon in multi user mode) to have the ${varBase}USERNAME and ${varBase}PASSWORD env vars set." >&2
            exit 1
          fi
        ''
        + (
          if useFetchGit then
            # GitLab supports HTTP Basic Authentication only when Git is used:
            # https://docs.gitlab.com/ee/user/project/settings/project_access_tokens.html#project-access-tokens
            ''
              cat > netrc <<EOF
              machine ${domain}
                      login ''$${varBase}USERNAME
                      password ''$${varBase}PASSWORD
              EOF
            ''
          else
            # Access via the GitLab API requires a custom header and does not work
            # with HTTP Basic Authentication:
            # https://docs.gitlab.com/ee/api/#personalprojectgroup-access-tokens
            ''
              # needed because fetchurl always sets --netrc-file if a netrcPhase is present
              touch netrc

              cat > private-token <<EOF
              PRIVATE-TOKEN: ''$${varBase}PASSWORD
              EOF
              curlOpts="$curlOpts --header @./private-token"
            ''
        );
        netrcImpureEnvVars = [
          "${varBase}USERNAME"
          "${varBase}PASSWORD"
        ];
      }
    );

    gitRepoUrl = "${protocol}://${domain}/${slug}.git";

    fetcherArgs =
@@ -84,6 +129,7 @@ lib.makeOverridable (
            };
          }
      )
      // privateAttrs
      // passthruAttrs
      // {
        inherit name;