theforeman / smart_proxy_realm_ad_plugin

foreman-proxy realm plugin for Active Directory
GNU General Public License v3.0
9 stars 10 forks source link

Can't use existing machine accounts #22

Open wiad opened 4 years ago

wiad commented 4 years ago

When i create a new host in Foreman a new computer account is set up with a couple of serviceprincipals:

servicePrincipalName: RestrictedKrbHost/lxserv954.example.com
servicePrincipalName: RestrictedKrbHost/LXSERV954
servicePrincipalName: host/lxserv954.example.com
servicePrincipalName: host/LXSERV954

This is all good and gives us a usable keytab file on my server.

BUT if I try to reinstall or apply the realm to an existing host the realm plugin errors out with The computer account already exists, even though the account used by the plugin to interact with AD has complete permissions in the OU where the account resides.

Furthermore, if I delete the computer account and try the above again the plugin creates a new account, but it is missing the servicePrincipalName attributes which results in an incomplete keytab on the server.

martencassel commented 4 years ago

@wiad Could you attach the error log from the plugin ?

wiad commented 4 years ago

Does not say much (tried with DEBUG in foreman-proxy settings but that does not give any more regarding the error).

Adding realm to a host with existing computer account in AD:

2019-10-25T08:50:49 22bd6506 [I] Started POST /AD.EXAMPLE.COM/
2019-10-25T08:50:49 22bd6506 [I] Proxy::AdRealm: initialize...
2019-10-25T08:50:49 22bd6506 [I] Proxy::AdRealm: create... AD.EXAMPLE.COM, lxserv1105.example.com, {"update"=>"true", "hostname"=>"lxserv1105.example.com", "userclass"=>"tfolinux/ELIN/UTV", "splat"=>[], "captures"=>["AD.EXAMPLE.COM"], "realm"=>"AD.EXAMPLE.COM"}
2019-10-25T08:50:49 22bd6506 [E] The computer account LXSERV1105 already exists
2019-10-25T08:50:49 22bd6506 [I] Finished POST /AD.EXAMPLE.COM/ with 400 (217.16 ms)
wiad commented 4 years ago

I have applied the workaround for Authentication error errors described in #20, not sure if that could have something to do with this?

wiad commented 4 years ago

I'm pretty certain that the problem with the computer account missing service principals is related to #20 . It is obvious from the logs when the join succeeds directly and when the workaround kicks in and starts to loop/sleep. If the join works as intended the serviceprincipal attributes are added, otherwise not.

wiad commented 4 years ago

So the issue with missing attributes in the computer account seems solved in #20 by getting the latest radcli from source.

That leaves the question why the plugin (or radcli?) errors out when there is an existing computer account created by another tool? When comparing accounts created with msktutil (our old accounts) and foremans realm-plugin I can't really see any major differences.

wiad commented 4 years ago

So, the error message The computer account %s already exists originates from adcli and it's function validate_computer_account. This function takes a parameter allow_overwrite as argument, and if this is false/0 then we get this error message if the account already exists. So to make the realm_ad plugin work with existing computer accounts, which would be very very nice, we need to pass the ADCLI_ENROLL_ALLOW_OVERWRITE flag to adcli somehow - i'm guessing in radcli (function radenroll_join, where the flag ADCLI_ENROLL_NO_KEYTAB is already passed)?

Not really sure how to do this myself in C though.

wiad commented 4 years ago

I added the ADCLI_ENROLL_ALLOW_OVERWRITE flag in radcli and then I can add the realm to a host with an existing computer account without the plugin/adcli erroring out. However, this also means that adcli proceeds to update the account which renders the existing host keytab on the server useless.

Not sure how to solve this, I just want it to acknowledge that there already is an account and not do anything else but I don't know if that is possible.

Dragonpark commented 4 years ago

When I was looking at this issue last year, the plugin did not allow this. I modified the code and added an option 'ignore_computername_exists', which, if present, will simply skip the 'join' command if the account already exists in AD. I'm not too familiar with ruby, so I never thought to submit a pull request for this. Modified radcli_join function in provider.rb. Note that this code includes a manual patch to resolve issues with joining to domains with multiple DCs:

   def radcli_join(hostfqdn, computername, password)
      # Join computer
      enroll = Adcli::AdEnroll.new(@adconn)
      enroll.set_computer_name(computername)
      enroll.set_host_fqdn(hostfqdn)
      enroll.set_domain_ou(@ou) if @ou
      enroll.set_computer_password(password)
          begin
        enroll.join
        return true
      rescue RuntimeError => ex
        # raise ex unless ex.message =~ /Authentication error/
        if ex.message =~ /Authentication error/
          for i in 1..100
            sleep(0.3)
            begin
              if enroll.respond_to? :update
                enroll.update
              else
                enroll.password
              end
              return true
            rescue RuntimeError => ex
              raise ex unless i < 99 and ex.message =~ /Authentication error/
            end
          end
        # 
        elsif ex.message =~ /already exists/
          if ignore_computername_exists
            return true
          else
            raise ex
          end
        else
          raise ex
        end
      end
    end

Modified the attr_reader in provider.rb to include the new option:

attr_reader :realm, :keytab_path, :principal, :domain_controller, :domain, :ou, :computername_prefix, :computername_hash, :computername_use_fqdn, :ignore_computername_exists

Added the line below to the initialize section in provider.rb:

@ignore_computername_exists = options.fetch(:ignore_computername_exists, false)

Added the below to settings.d/realm_ad.yml

# Optional: Ignore computer account already exists error (should only be used for computers already joined to Active Directory)
:ignore_computername_exists: true

Again, I don't do ruby so there may be issues with the code though, so please verify in a test environment first.

EDIT: I believe the following line should be added to configuration_loader.rb, but was not when I did this over a year ago.

ignore_computername_exists: settings[:ignore_computername_exists]
wiad commented 4 years ago

Thank you @Dragonpark , I tested your workaround and it works! (i didn't bother with making ignore_computername_exists configurable so I can't say if those parts of the code works, but if this is turned into a PR I guess it might be a good idea to include that. )

Dragonpark commented 4 years ago

I think that was the intent, so I may end up doing so. I believe I added it as there were hosts that were already joined to AD and I wanted that to be reflected in Satellite. This way I could temporarily enable the option as needed and then disable it again.

Sent with GitHawk