DirectoryTree / LdapRecord-Laravel

Multi-domain LDAP Authentication & Management for Laravel.
https://ldaprecord.com/docs/laravel/v3
MIT License
509 stars 54 forks source link

Seemingly random failure retrieving on lastLogon attribute #477

Closed rcraig12 closed 2 years ago

rcraig12 commented 2 years ago

Environment:

Describe the bug:

public function allUsersReport(){

        $accounts = new Collection();
        $connections = config( 'portal.connections' );

        foreach ( array_keys( $connections ) as $connection ){

            $result = User::on( $connection )
                ->setDn( config( 'portal.connections.' . $connection.'.MSO365RootOU' ) )
                ->select( 'userPrincipalName', 'Mail', 'Description', 'distinguishedName', 'whenCreated', 'lastLogon')
                ->paginate();

            //print_r($result);

            if ( $result->count() <> 0 ){

                $accounts = $accounts->merge( $result );

            }

        }

        return view( 'reports.allusers', compact( ['accounts'] ) );

    }

I have multi domain and for the most part the fields appear to be resolving the data from active directory correctly. However for some reason the lastlogon field is not consistant and across 6000 users almost half fail on the attribute. I thought that it could have been connection specific or indeed OU specific but the error appears across users in the same domain and within the same OU.

Any ideas?

rcraig12 commented 2 years ago
[2022-09-29 16:19:25] local.INFO: LDAP (ldap://az-uks-dc02.litana.antonine.local:389) - Operation: Binding - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:25] local.INFO: LDAP (ldap://az-uks-dc02.litana.antonine.local:389) - Operation: Bound - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:25] local.INFO: LDAP (ldap://az-uks-dc02.litana.antonine.local:389) - Operation: Search - Base DN: dc=litana,dc=antonine,dc=local - Filter: (&(objectclass=\74\6f\70)(objectclass=\70\65\72\73\6f\6e)(objectclass=\6f\72\67\61\6e\69\7a\61\74\69\6f\6e\61\6c\70\65\72\73\6f\6e)(objectclass=\75\73\65\72)(objectguid=\bb\fd\e1\42\40\c5\cb\44\9e\e2\db\16\98\be\c9\59)(!(objectclass=\63\6f\6d\70\75\74\65\72))) - Selected: (objectguid,*) - Time Elapsed: 76.78  
[2022-09-29 16:19:25] local.INFO: LDAP (ldap://az-uks-dc01.antonine.local:389) - Operation: Binding - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:26] local.INFO: LDAP (ldap://az-uks-dc01.antonine.local:389) - Operation: Bound - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:26] local.INFO: LDAP (ldap://az-uks-dc01.antonine.local:389) - Operation: Paginate - Base DN: dc=antonine,dc=local - Filter: (&(objectclass=\74\6f\70)(objectclass=\70\65\72\73\6f\6e)(objectclass=\6f\72\67\61\6e\69\7a\61\74\69\6f\6e\61\6c\70\65\72\73\6f\6e)(objectclass=\75\73\65\72)(!(objectclass=\63\6f\6d\70\75\74\65\72))) - Selected: (objectguid,userPrincipalName,Mail,Description,distinguishedName,whenCreated,lastLogon,objectclass) - Time Elapsed: 49.94  
[2022-09-29 16:19:27] local.INFO: LDAP (ldap://az-uks-dc02.litana.antonine.local:389) - Operation: Paginate - Base DN: dc=litana,dc=antonine,dc=local - Filter: (&(objectclass=\74\6f\70)(objectclass=\70\65\72\73\6f\6e)(objectclass=\6f\72\67\61\6e\69\7a\61\74\69\6f\6e\61\6c\70\65\72\73\6f\6e)(objectclass=\75\73\65\72)(!(objectclass=\63\6f\6d\70\75\74\65\72))) - Selected: (objectguid,userPrincipalName,Mail,Description,distinguishedName,whenCreated,lastLogon,objectclass) - Time Elapsed: 1404.19  
[2022-09-29 16:19:27] local.INFO: LDAP (ldap://az-uks-dc03.colanica.antonine.local:389) - Operation: Binding - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:27] local.INFO: LDAP (ldap://az-uks-dc03.colanica.antonine.local:389) - Operation: Bound - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:28] local.INFO: LDAP (ldap://az-uks-dc03.colanica.antonine.local:389) - Operation: Paginate - Base DN: dc=colanica,dc=antonine,dc=local - Filter: (&(objectclass=\74\6f\70)(objectclass=\70\65\72\73\6f\6e)(objectclass=\6f\72\67\61\6e\69\7a\61\74\69\6f\6e\61\6c\70\65\72\73\6f\6e)(objectclass=\75\73\65\72)(!(objectclass=\63\6f\6d\70\75\74\65\72))) - Selected: (objectguid,userPrincipalName,Mail,Description,distinguishedName,whenCreated,lastLogon,objectclass) - Time Elapsed: 755.7  
[2022-09-29 16:19:28] local.INFO: LDAP (ldap://az-uks-dc04.cibra.antonine.local:389) - Operation: Binding - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:28] local.INFO: LDAP (ldap://az-uks-dc04.cibra.antonine.local:389) - Operation: Bound - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:28] local.INFO: LDAP (ldap://az-uks-dc04.cibra.antonine.local:389) - Operation: Paginate - Base DN: DC=cibra,DC=antonine,DC=local - Filter: (&(objectclass=\74\6f\70)(objectclass=\70\65\72\73\6f\6e)(objectclass=\6f\72\67\61\6e\69\7a\61\74\69\6f\6e\61\6c\70\65\72\73\6f\6e)(objectclass=\75\73\65\72)(!(objectclass=\63\6f\6d\70\75\74\65\72))) - Selected: (objectguid,userPrincipalName,Mail,Description,distinguishedName,whenCreated,lastLogon,objectclass) - Time Elapsed: 355.34  
[2022-09-29 16:19:28] local.INFO: LDAP (ldap://az-uks-dc05.pexa.antonine.local:389) - Operation: Binding - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:28] local.INFO: LDAP (ldap://az-uks-dc05.pexa.antonine.local:389) - Operation: Bound - Username: CN=Azure Monitor,CN=Managed Service Accounts,DC=antonine,DC=local  
[2022-09-29 16:19:29] local.INFO: LDAP (ldap://az-uks-dc05.pexa.antonine.local:389) - Operation: Paginate - Base DN: DC=pexa,DC=antonine,dc=local - Filter: (&(objectclass=\74\6f\70)(objectclass=\70\65\72\73\6f\6e)(objectclass=\6f\72\67\61\6e\69\7a\61\74\69\6f\6e\61\6c\70\65\72\73\6f\6e)(objectclass=\75\73\65\72)(!(objectclass=\63\6f\6d\70\75\74\65\72))) - Selected: (objectguid,userPrincipalName,Mail,Description,distinguishedName,whenCreated,lastLogon,objectclass) - Time Elapsed: 147.65  

in case this helps any....

stevebauman commented 2 years ago

Hi there @rcraig12,

6000 users almost half fail on the attribute.

Can you explain "fail"? Do you receive an exception? Or is the attribute simply not included in results of the query?

rcraig12 commented 2 years ago

Hi @stevebauman ,

When I say fail I am asserting that the value returned is 0 (zero) instead of a timestamp. So the attribute is returned in the query just no value associated with it when I review the associative array. Manually checking the directory object attributes there is definitely a proper value there. Very puzzling.

stevebauman commented 2 years ago

Thanks for clarifying that @rcraig12.

I believe this is a replication issue, as I see you have multiple domain controllers in your logs.

To my knowledge, lastLogon is only updated on the domain controller to where the login request actually was sent to. I.e: If I login via DC1, lastLogon would be 0 on DC2:

https://serverfault.com/questions/734615/lastlogon-vs-lastlogontimestamp-in-active-directory

However, I believe lastLogonTimestamp is replicated between domain controllers.

Can you try selecting that inside of your query?

rcraig12 commented 2 years ago

Thanks @stevebauman !

That makes sense and a doh moment all at the same time lol.

Now I have to refactor this code to query all DC's in the domain for that attribute which will be onerous :( That is to say in this instance i need to have the accurate last logon time so as to make sure the account has been accessed in the last 6 months. So decisions can be made on pruning M365 license use . Hope that makes sense.

Thanks for taking the time to look at this.

stevebauman commented 2 years ago

Happy to help @rcraig12 🙏

i need to have the accurate last logon time so as to make sure the account has been accessed in the last 6 months.

Are you able to use the lastLogonTimestamp attribute instead?

stevebauman commented 2 years ago

https://techcommunity.microsoft.com/t5/ask-the-directory-services-team/8220-the-lastlogontimestamp-attribute-8221-8211-8220-what-it-was/ba-p/396204

How it worked in Windows 2000

Prior to Windows Server 2003 administrators had to query the lastLogon attribute to determine the most recent logon of user or computer account. This process was time consuming as the lastLogon attribute is updated only on the DC that validates the logon request. The lastLogon attribute is not replicated. So in the past to determine the most recent logon of a user or computer account the lastLogon attribute had to be queried on all domain controllers (at least in concept) and then the most recent date for lastLogon had to be determined from all the results returned. In Windows 2003 and higher lastLogon is still has the same behavior. It is updated only on the validating DC and is not replicated.

How it works in Windows Server 2003 and later

In contrast the lastLogontimeStamp attribute is replicated so all DC's have the same value for the attribute (after replication convergence). Therefore you can query a single DC to find all the users or all the computers that have not logged in within a certain time.

rcraig12 commented 2 years ago

yup so i am dumping both attributes and so there can be at least a timestamp in one of the two attributes. if both are blank that too tells a story so all good now. Once again thanks for spending the time :)

stevebauman commented 2 years ago

Ok sounds good! And no problem, happy to help 😄