Skip to content

Instantly share code, notes, and snippets.

@KostyaEsmukov
Last active October 4, 2021 18:29
Show Gist options
  • Save KostyaEsmukov/ce7c9f3af846be4119b7d269d9895651 to your computer and use it in GitHub Desktop.
Save KostyaEsmukov/ce7c9f3af846be4119b7d269d9895651 to your computer and use it in GitHub Desktop.

Lab: Active Directory as LDAP for Linux

  • Goal: Create a minimal working setup where a Linux machine retrieves users from Active Directory via LDAP.
  • Requirements: Vagrant, VirtualBox.

Setup

Put all of the attached files somewhere and switch to that directory.

Generate an SSH key:

ssh-keygen -t rsa -b 4096 -o -a 100 -C "myuser_ldap" -f './id_rsa'

Start vagrant:

vagrant up

Wait until it completes. Note that AD is not getting installed during the Vagrant provisioning (I didn't manage to get it to work).

Create an AD forest:

vagrant winrm dc -c "C:\vagrant\provision_ad_1.ps1"

You might get a WinRMAuthorizationError -- that's ok, just let the machine reboot.

After it is booted, create an AD user and groups:

vagrant winrm dc -c "C:\vagrant\provision_ad_2.ps1"

Ensure that LDAP works on the Linux machine:

$ vagrant ssh ldap -- getent passwd | grep my
myuser:*:20001:20000:myuser:/home/myuser:/bin/bash

Create sshPublicKey schema key

So far that myuser cannot login. Linux doesn't understand the AD users' passwords, and we didn't fill the Unix shadow entry for it. We will use ssh keys for authentication instead of the passwords. For that we need to create a custom attribute in AD.

See https://social.technet.microsoft.com/wiki/contents/articles/51121.active-directory-how-to-add-custom-attribute-to-schema.aspx

Open RDP:

$ vagrant rdp dc

Fix vagrant user permissions

The vagrant Windows image contains a vagrant user which doesn't have necessary permissions to change the AD schema.

Fix that:

  • Press Win
  • Search for Active Directory Users and Computers
  • Users
  • vagrant
  • Properties
  • Member of
  • Add

Groups: domain admins, group policy creator, schema admins

Sign out from the account and login back again.

Create the attribute

Open cmd as admin, type the following to gain the ability to use the AD schema management snap-in:

regsvr32.exe schmmgmt.dll

Then enter mmc -> Add snap-in -> AD schema.

Select Attributes -> Create Attribute -> - Common Name: sshPublicKey - OID: see https://docs.microsoft.com/en-us/windows/desktop/AD/obtaining-an-object-identifier-from-microsoft , the value I used was 1.2.840.113556.1.8000.2554.21570.45726.62644.19815.40515.182734.3613927.1.1 - Syntax: Unicode String - Multi-Valued (tick)

Select Classes -> posixAccount -> Properties -> Attributes -> Add -> sshPublicKey

Add ssh key to myuser

Open PowerShell, type this:

Set-ADUser -Identity myuser `
  -Add @{sshPublicKey=${C:\vagrant\id_rsa.pub}}

Check

Ensure a local user works:

$ ssh vagrant@127.0.0.1 -p 2200
...
vagrant@ldap:~$ sudo -i
root@ldap:~#

Now check the LDAP user:

$ ssh -i ./id_rsa myuser@127.0.0.1 -p 2200
...
myuser@ldap:~$ sudo -i
root@ldap:~#

Simulate a network partition:

iptables -I INPUT -i eth1 -j DROP
iptables -I OUTPUT -o eth1 -j DROP

Try to connect with an LDAP account:

$ ssh -i ./id_rsa myuser@127.0.0.1 -p 2200
myuser@127.0.0.1: Permission denied (publickey).

Now try a local user:

$ ssh vagrant@127.0.0.1 -p 2200
...
vagrant@ldap:~$

Restore the connectivity:

iptables -D INPUT -i eth1 -j DROP
iptables -D OUTPUT -o eth1 -j DROP

Ensure it works again:

$ ssh -i ./id_rsa myuser@127.0.0.1 -p 2200
...
myuser@ldap:~$

Block the account in AD:

Disable-ADAccount -Identity myuser

Ensure that it doesn't work anymore:

$ ssh -i ./id_rsa myuser@127.0.0.1 -p 2200
myuser@127.0.0.1: Permission denied (publickey).

Looks like the lab goal is achieved 🎉.

$ErrorActionPreference = "Stop"
Install-WindowsFeature -name AD-Domain-Services -IncludeManagementTools
Import-Module ADDSDeployment
Import-Module ActiveDirectory
Install-ADDSForest -DomainName mydomain.local -DomainMode Win2012 `
-DomainNetbiosName MYDOMAIN -ForestMode Win2012 -InstallDns `
-SafeModeAdministratorPassword (ConvertTo-SecureString "AdminP@ss123qaz123" -AsPlainText -Force) `
-Force
# ^^^ this command would reboot the machine
$ErrorActionPreference = "Stop"
Import-Module ADDSDeployment
Import-Module ActiveDirectory
New-ADUser `
-ChangePasswordAtLogon $false `
-Enabled $true `
-Name ldapreader `
-AccountPassword (ConvertTo-SecureString "rvV08O2mzmsDkiPN" -AsPlainText -Force) `
-Path "DC=mydomain,DC=local" -PassThru
New-ADOrganizationalUnit -Name MyOu
New-ADGroup `
-Name ldapuser `
-Path "OU=MyOu,DC=mydomain,DC=local" `
-GroupScope Global -PassThru
Set-ADGroup -Identity ldapuser -Replace @{gidNumber="20000"}
Set-ADGroup -Identity ldapuser -Add @{objectClass="posixGroup"}
New-ADGroup `
-Name ldapsudo `
-Path "OU=MyOu,DC=mydomain,DC=local" `
-GroupScope Global -PassThru
Set-ADGroup -Identity ldapsudo -Replace @{gidNumber="20001"}
Set-ADGroup -Identity ldapsudo -Add @{objectClass="posixGroup"}
New-ADUser `
-ChangePasswordAtLogon $false `
-Enabled $true `
-Name myuser `
-AccountPassword (ConvertTo-SecureString "MyP@ss123qaz123" -AsPlainText -Force) `
-Path "OU=MyOu,DC=mydomain,DC=local" -PassThru
Set-ADUser -Identity myuser `
-Replace @{uid="myuser";uidNumber="20001";gidNumber="20000";homeDirectory="/home/myuser";loginShell="/bin/bash"}
Set-ADUser -Identity myuser `
-Add @{objectClass="posixAccount"}
Set-ADGroup -Identity ldapsudo -Add @{MemberUid="myuser"}
Vagrant.configure("2") do |config|
config.vm.define "dc" do |m|
m.vm.hostname = "dc"
m.vm.network "private_network", ip: "192.168.99.11"
m.vm.network "forwarded_port", guest: 3389, host: 13389
m.vm.box = "opentable/win-2012r2-standard-amd64-nocm"
m.vm.provider "virtualbox" do |vb|
vb.memory = "4096"
vb.cpus = 2
end
# I tried to make the AD provision automatic but didn't succeed
# in a short amount of time:
#
# Option 1:
# m.vm.provision "shell", path: "provision_a.ps1"
# Causes:
# An authorization error occurred while connecting to WinRM.
#
# User: vagrant
# Endpoint: http://127.0.0.1:55985/wsman
# Message: WinRM::WinRMAuthorizationError
#
# Option 2:
# m.vm.provision "shell", path: "provision_a.ps1", reboot: true
# ... + `-NoRebootOnCompletion`
# Causes:
# ==> dc: Forcing shutdown of VM...
# ==> dc: Destroying VM and associated drives...
# /opt/vagrant/embedded/gems/2.2.4/gems/winrm-2.3.1/lib/winrm/http/response_handler.rb:59:in `raise_if_auth_error': WinRM::WinRMAuthorizationError (WinRM::WinRMAuthorizationError)
# from /opt/vagrant/embedded/gems/2.2.4/gems/winrm-2.3.1/lib/winrm/http/response_handler.rb:51:in `raise_if_error'
# from /opt/vagrant/embedded/gems/2.2.4/gems/winrm-2.3.1/lib/winrm/http/response_handler.rb:34:in `parse_to_xml'
#
# So the easiest solution is just to provision AD manually.
end
config.vm.define "ldap" do |m|
m.vm.hostname = "ldap"
m.vm.network "private_network", ip: "192.168.99.12"
m.vm.box = "debian/stretch64"
m.vm.provider "virtualbox" do |vb|
vb.memory = "512"
end
m.vm.provision "shell", inline: <<-SHELL
apt-get update
cat << EOF | debconf-set-selections
libnss-ldapd libnss-ldapd/nsswitch multiselect passwd, group, shadow
libnss-ldapd:amd64 libnss-ldapd/nsswitch multiselect passwd, group, shadow
nslcd nslcd/ldap-uris string ldap://192.168.99.11/
nslcd nslcd/ldap-base string ou=MyOu,dc=mydomain,dc=local
nslcd nslcd/ldap-binddn string cn=ldapreader,dc=mydomain,dc=local
nslcd nslcd/ldap-bindpw password rvV08O2mzmsDkiPN
EOF
DEBIAN_FRONTEND=noninteractive apt-get install -yq \
libnss-ldapd libpam-ldapd
cat > /etc/sudoers.d/ldap <<EOF
%ldapsudo ALL=(ALL) NOPASSWD: ALL
EOF
# https://wiki.debian.org/LDAP/PAM#Creating_home_directory_on_login
cat > /usr/share/pam-configs/mkhomedir <<EOF
Name: Create home directory during login
Default: yes
Priority: 900
Session-Type: Additional
Session:
required pam_mkhomedir.so umask=0022 skel=/etc/skel
EOF
DEBIAN_FRONTEND=noninteractive pam-auth-update --force
cat > /usr/bin/ldap_sshkey <<'EOF'
#!/bin/bash
set -euo pipefail
_SAFE_USERNAME=`echo -n $1 | sed 's/[^a-zA-Z0-9.]//g'`
timeout 10s ldapsearch -LLL -o ldif-wrap=no \
-H ldap://192.168.99.11/ \
-b ou=MyOu,dc=mydomain,dc=local \
-D cn=ldapreader,dc=mydomain,dc=local \
-w rvV08O2mzmsDkiPN \
"(&(objectClass=posixAccount)(userAccountControl=512)(uid=${_SAFE_USERNAME}))" \
sshPublicKey | grep '^sshPublicKey:' | sed 's/^sshPublicKey: *//'
EOF
chmod +x /usr/bin/ldap_sshkey
cat >> /etc/ssh/sshd_config <<EOF
AuthorizedKeysCommand /usr/bin/ldap_sshkey
AuthorizedKeysCommandUser nobody
EOF
systemctl restart ssh
systemctl stop nscd
rm -Rf /var/cache/nscd/*
systemctl start nscd
SHELL
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment