Is Active Directory support a thing in NixOS?

For a few days now I have been searching for information related to joining a NiXOS computer to Active Directory. I found information with regards to using LDAP for user authentication, but I am not sure whether that would work well with my current Active Directory server.

I am starting to worry that this is just not supported.
Is there anything equivalent to realm join in NixOS?
What would I be missing if I just authenticated through LDAP to my AD server?

There are very few things that have gotten me excited as discovering NixOS. I am currently trying to reproduce an equivalent setup to my Silverblue at my house for my family, but I also believe it could solve some work issues once I learn it well enough to trust my ability to solve problems with it. I am trying to learn to search the documentation so I don’ t have to ask a question every time I need to so something, but it seems there is a steep curve at the beginning.

I also start to get worried when it seems like NixOS may not support some feature that would normally be considered standard. Surely someone out there has NixOS machines joined to an Active Directory domain.

1 Like

Hi there,
I have absolutely no experience in AD stuff, but if I Google around with the term “Nixos active directory” the first results are this blog post Setting up LDAP Authentication with NixOS and this reddit post Reddit - Dive into anything . Be aware that both are quite old, so there are likely some changes and around that area. But both state out, that there is a Nixos module for the use case NixOS Search

So as I am not an AD person, I am not 100% sure if that fully covers your use case, but it seems very promising to me on the first look. I hope that helps you. :slight_smile:

1 Like

The tl;dr: Whatever you can do on $DISTRO you can do on NixOS.

I’m guessing sssd and samba are what you need to achieve this and they are available too on NixOS.

4 Likes

Thanks for the replies!

I think realmd is also required.
I found this PR for realmd, but it doesn’t show up in a package search and the PR makes it look like the services were never implemented for Nix.

I may try to see if I can get what I need using an LDAP directory server rather than an AD server for my house, but this would make it very difficult to use at work.
There is also a possibility sometimes for communicating with an AD server solely through LDAP, but I believe there are disadvantages to that method (I am not sure what they are).

I use this https://github.com/buckley310/nixos-config/blob/e46bedb0f3e2f9a1e2a2fb2c0ff22fe254aa1e21/modules/ad-domain.nix

As mentioned, realmd isnt suported, so I add machines using kinit [ad-username] and net ads join -k

1 Like

Side note: I have 23.05 machines currently using AD, but I have not tested the domain join process since the release of 23.05. So if the samba config is outdated I wouldn’t know. That’s happened before. With my above module, samba is only used for the domain joining process, whereas sssd takes care of all the regular domain membership stuff.

Hello! That is actually my PR, I was working on it last year but it was taking time and once my Fall semester of Uni started I put it on the back burner. Now that you mention it, I can pick work back up. AFAIK its almost complete.

Thanks for sharing your config with me! I am anxious to try it.

I appreciate your hard work. Although I am not sure I know the benefits of doing it this way vs the way that @buckley did it; I am sure that since realmd is the way that most distributions connect many people will be happy to have that option.

I would probably switch to realmd when it becomes available. realmd seems to be simpler, and generally considered the default choice now (don’t quote me on that).

If nothing else, it will avoid me duplicating the domain settings to samba just for the one-time-use ad join command to work.

The good news is that all of the samba stuff works well. I seem to be able to join and leave without any difficulty.

I can’t seem to get id to resolve and my sssd keeps complaining about the domain.
I noticed that it appears that you have your ad domain the same as your dns domain. Mine is not setup that way, because I have always had trouble with that configuration in the past. This is leading to some confusion on my part about which domain is supposed to be set where. I modified the [sssd] settiings in your module to try and duplicate my working silverblue machine, but something still just isn’t setting up right.

      services.sssd = {
        enable = true;
        sshAuthorizedKeysIntegration = true;
        config = ''
          [sssd]
          services = nss, pam, ssh
          config_file_version = 2
          domains = ${cfg.longname}
          
          [be]
          debug_level=9
          
          [nss]
          #debug_level=6
          
          [pam]
          #debug_level=6
          
          [domain/${cfg.longname}]
          default_shell = /run/current-system/sw/bin/bash
          id_provider = ad
          cache_credentials = True
          krb5_store_password_if_offline = True
          ldap_sasl_mech = gssapi
          access_provider = ad
          fallback_homedir = /home/%u.%d
          ad_gpo_access_control = permissive
          ad_gpo_ignore_unreadable = True
          ldap_user_extra_attrs = altSecurityIdentities:altSecurityIdentities
          ldap_user_ssh_public_key = altSecurityIdentities
          ldap_use_tokengroups = True
          use_fully_qualified_names = False
          ldap_id_mapping = True
          ad_domain = $(cfg.longname}
        '';
      };

sssd sometimes complains that there is no SRV record and originally this was a problem, because I am installing on virtmanager within my silverblue until I can get this working. Since my virtmanager is setup for NAT, I am having to manually add all of things normally delivered by my dhcp. dig now confirms that there is SRV record for my domain, but I still can’t resolve a uid.

I also tried setting the security.ipa package that I found on nixos.org, but it requires ipa-join and when I try to run that it complains that my host name is not fully qualified. I tried setting it explicitly, but then ipa-join starts going down the options one at a time telling me that they aren’t set as if nothing is my configuration.nix for security.ipa has taken effect.

1 Like

I found the problem with the above modifications.

ad_domain = $(cfg.longname}

should have been

ad_domain = ${cfg.longname}

Now I am having a kerberos error that is probably because I am using a non-standard root-certificate.

I changed my krb5.conf to point to my personal ca.crt and now things work!

I am going to work on this module some and post it in case other want it.

Thanks!!!

2 Likes

Next question: :slight_smile: How are you getting your home directories created? Have you also managed to get oddjob working?

Try security.pam.services.systemd-user.makeHomeDir = true;

The ad-domain.nix I ended up with wasn’t wholely different from the one I got from @buckley . I added a few more parameters to the sssd.conf and I removed the networking.domain and networking.search from this module.

{ config, lib, pkgs, ... }:

let
  cfg = config.sconfig.ad-domain;
in
{
  options.sconfig.ad-domain = with lib; with types;
    {
      enable = mkEnableOption "Join Domain with SSSD";
      longname = mkOption {
        type = str;
        example = "example.com";
      };
      shortname = mkOption {
        type = str;
        example = "EXAMPLE";
      };
    };

  config = lib.mkIf cfg.enable
    {
      #networking.domain = cfg.longname;
      #networking.search = [ (cfg.longname) ];
      security.pam.services.sshd.makeHomeDir = true;
      krb5 = {
        enable = true;
         libdefaults.default_realm = lib.toUpper cfg.longname;
      };
      services.sssd = {
        enable = true;
        sshAuthorizedKeysIntegration = true;
        config = ''
          [sssd]
          services = nss, pam, ssh
          config_file_version = 2
          domains = ${cfg.longname}
          
          [domain/${cfg.longname}]
          default_shell = /run/current-system/sw/bin/bash
          id_provider = ad
          ldap_sasl_authid = ${lib.toUpper config.networking.hostName}
          cache_credentials = True
          krb5_real = ${lib.toUpper cfg.longname}
          krb5_store_password_if_offline = True
          ldap_sasl_mech = gssapi
          access_provider = ad
          fallback_homedir = /home/%u.%d
          ad_gpo_access_control = permissive
          ad_gpo_ignore_unreadable = True
          ldap_user_extra_attrs = altSecurityIdentities:altSecurityIdentities
          ldap_user_ssh_public_key = altSecurityIdentities
          ldap_use_tokengroups = True
          use_fully_qualified_names = False
          ldap_id_mapping = True
          ad_domain = ${cfg.longname}
        '';
      };
      # Samba is configured, but just for the "net" command, to
      # join the domain. A better join method probably exists.
      # `net ads join -U Administrator`
      environment.systemPackages = [ pkgs.samba4Full ];
      systemd.services.samba-smbd.enable = lib.mkDefault false;
      services.samba = {
        enable = true;
        enableNmbd = lib.mkDefault false;
        enableWinbindd = lib.mkDefault false;
        package = pkgs.samba4Full;
        securityType = "ads";
        extraConfig = ''
          realm = ${lib.toUpper cfg.longname}
          workgroup = ${lib.toUpper cfg.shortname}
          client use spnego = yes
          restrict anonymous = 2
          server signing = mandatory
          client signing = mandatory
          kerberos method = secrets and keytab
        '';
      };
    };
}

I did add a bit more to my own krb5.conf (in my configuration.nix), but I can’t figure out how much of it is personal configuration vs should end up in this module (or as parameters to this module):

  krb5.libdefaults = {
    dns_lookup_realm = "false";
    ticket_lifetime = "24h";
    forwardable = "true";
    rdns = "false";
    pkinit_anchors = "FILE:/etc/nixos/certificates/ca.crt";
    spake_preauth_groups = "edwards25519";
    dns_canonicalize_hostname = "fallback";
    default_ccache_name = "KEYRING:persistent:%{uid}";
    udp_preference_limit = "0";
  };

I am a bit worried as to whether my kerberos ticket will get reissued, but I suppose I won’t know that for a week. I didn’t use kinit for net ads join, I just did net ads join -U Administrator, but I can’t remember whether I had to kinit to get sssd working or if connecting with net ads join pulled the kerberos ticket for me.

Anyway, this works at least for week. :slight_smile: I suppose we will see what happens next.

You should check the packages for sssd and realms. Easier to configure than LDAP

Unfortunately, I don’t think realms is available yet. There is a security.ipa, but that did not work for me.
I believe @SohamG is working on support, but I do not think it is ready yet.

Fortunately, with NixOS nothing is hard once it has been done. Using @buckley 's modules my entire household is now running NixOS and happily joined to my AD domain. After the first one, it was extremely easy to setup, because I just copied my config off the shared drive and rebuild. I did move my hostname into an id.nix so that I don’t mess things up by putting it on the shared drive.

I also made a few changes to buckley’s scripts, but I am not sure how necessary they were. I posted all of my changes above. There was one minor change after the above post to make sure the netbios name is the correct size.

So my final modules/ad-domain.nix was:

{ config, lib, pkgs, ... }:

let
  cfg = config.sconfig.ad-domain;
in
{
  options.sconfig.ad-domain = with lib; with types;
    {
      enable = mkEnableOption "Join Domain with SSSD";
      longname = mkOption {
        type = str;
        example = "example.com";
      };
      shortname = mkOption {
        type = str;
        example = "EXAMPLE";
      };
    };

  config = lib.mkIf cfg.enable
    {
      #networking.domain = cfg.longname;
      #networking.search = [ (cfg.longname) ];
      security.pam.services.sshd.makeHomeDir = true;
      krb5 = {
        enable = true;
         libdefaults.default_realm = lib.toUpper cfg.longname;
      };
      services.sssd = {
        enable = true;
        sshAuthorizedKeysIntegration = true;
        config = ''
          [sssd]
          services = nss, pam, ssh
          config_file_version = 2
          domains = ${cfg.longname}
          
          [domain/${cfg.longname}]
          default_shell = /run/current-system/sw/bin/bash
          id_provider = ad
          ldap_sasl_authid = ${builtins.substring 0 15 (lib.toUpper config.networking.hostName)}
          cache_credentials = True
          krb5_real = ${lib.toUpper cfg.longname}
          krb5_store_password_if_offline = True
          ldap_sasl_mech = gssapi
          access_provider = ad
          fallback_homedir = /home/%u.%d
          ad_gpo_access_control = permissive
          ad_gpo_ignore_unreadable = True
          ldap_user_extra_attrs = altSecurityIdentities:altSecurityIdentities
          ldap_user_ssh_public_key = altSecurityIdentities
          ldap_use_tokengroups = True
          use_fully_qualified_names = False
          ldap_id_mapping = True
          ad_domain = ${cfg.longname}
        '';
      };
      # Samba is configured, but just for the "net" command, to
      # join the domain. A better join method probably exists.
      # `net ads join -U Administrator`
      environment.systemPackages = [ pkgs.samba4Full ];
      systemd.services.samba-smbd.enable = lib.mkDefault false;
      services.samba = {
        enable = true;
        enableNmbd = lib.mkDefault false;
        enableWinbindd = lib.mkDefault false;
        package = pkgs.samba4Full;
        securityType = "ads";
        extraConfig = ''
          realm = ${lib.toUpper cfg.longname}
          workgroup = ${lib.toUpper cfg.shortname}
          client use spnego = yes
          restrict anonymous = 2
          server signing = mandatory
          client signing = mandatory
          kerberos method = secrets and keytab
        '';
      };
    };
}

my added krb5 section in my configuration.nix (for custom CA) was:

  krb5.libdefaults = {
    dns_lookup_realm = "false";
    ticket_lifetime = "24h";
    forwardable = "true";
    rdns = "false";
    pkinit_anchors = "FILE:/etc/nixos/certificates/ca.crt";
    spake_preauth_groups = "edwards25519";
    dns_canonicalize_hostname = "fallback";
    default_ccache_name = "KEYRING:persistent:%{uid}";
    udp_preference_limit = "0";
  };

My configuration.nix to configure the module:

  # Configure ad domain
  sconfig.ad-domain = {
    enable = true;
    longname = "youdomain.com";
    shortname = "SHORTNAME";
  };

My join command is:
sudo net ads join -n SHORTNAME -U Administrator

This seem to just work.