Hydra OIDC guide

· 453 words · 2 min read

Previously, we covered how to set up Kanidm's read-only LDAP interface and get SSO working on Hydra. That's great and all, but LADP is a bit too old school. In this patch, @lheckemann and @ners added support for OpenID Connect to Hydra.

Hydra setup

Since the PR is not merged yet, bring in the patch manually (ignore this step if it is merged by the time you read this):

# in flake.nix
{
  inputs.hydra.url = "github:ners/hydra/oidc";
  inputs.hydra.inputs.nixpkgs.follows = "nixpkgs";
  ...
}

# in hydra config
{ inputs, pkgs, ... }:

{
  services.hydra = {
    package = inputs.hydra.packages.${pkgs.stdenv.hostPlatform.system}.default;
  };
}

Kanidm config

Assuming your login user name is somebody, the Hydra user and admin groups are hydra.users and hydra.admins, and your Hydra instance is at https://hydra.example.com, the Kanidm config should looks like this:

{
  # i only added configs related to hydra here
  # and its assumed that the kanidm domain is set to sso.example.com (where cookie is valid)
  # and kanidm is running at https://sso.example.com
  services.kanidm.provision = {
    groups = {
      "hydra.admins" = { };
      "hydra.users" = { };
    };

    persons = {
      somebody = {
        displayName = "Somebody";
        legalName = "Real Name";
        mailAddresses = [ "somebody@example.com" ];
        groups = [
          "hydra.admins"
          "hydra.users"
        ];
      };
    };

    systems.oauth2 = {
      hydra = {
        displayName = "Hydra";
        allowInsecureClientDisablePkce = true;
        originUrl = "https://hydra.example.com/oidc-login";
        originLanding = "https://hydra.example.com/";
        basicSecretFile = ""; # path to your secret file
        preferShortUsername = true;
        scopeMaps."hydra.users" = [
          "openid"
          "email"
          "profile"
          "groups"
        ];
      };
    };
  };
}

At the time of writing, Hydra OIDC implementation does not support PKCE, so allowInsecureClientDisablePkce must be set to prevent login failure.

Hydra config

{
  # i'm using sops for secret management
  # but you can also use other solutions
  sops.secrets.hydra = {
    owner = "hydra";
    group = "hydra";
    mode = "440";
  };

  services.hydra.extraConfig = ''
    enable_hydra_login = 0                                                     # disable default hydra login page
    enable_oidc_login = 1                                                      # enable OIDC
    oidc_client_id = "hydra"                                                   # this should match the client id in kanidm config
    oidc_scope = "openid email profile groups"                                 # scopes to request
    oidc_auth_uri = "https://sso.example.com/ui/oauth2"                        # kanidm oauth2 auth endpoint
    oidc_token_uri = "https://sso.example.com/oauth2/token"                    # kanidm oauth2 token endpoint
    oidc_userinfo_uri = "https://sso.example.com/oauth2/openid/hydra/userinfo" # kanidm userinfo endpoint, client id must match
    include ${config.sops.secrets.hydra.path}                                  # should contain `oidc_client_secret = <secret>`

    # change the role mapping according to your needs
    # but the the format is:
    # <group in kanidm> = <role in hydra>
    # where group in kanidm is in the form of
    # <group>@<kanidm cookie domain>
    <oidc_role_mapping>
      hydra.admins@sso.example.com = admin
      hydra.admins@sso.example.com = bump-to-front
      hydra.users@sso.example.com = cancel-build
      hydra.users@sso.example.com = eval-jobset
      hydra.users@sso.example.com = create-projects
      hydra.users@sso.example.com = restart-jobs
    </oidc_role_mapping>
  '';
}