Skip to content

Commit ac8610b

Browse files
committed
generate computer name
1 parent 74f5702 commit ac8610b

File tree

4 files changed

+132
-37
lines changed

4 files changed

+132
-37
lines changed

config/realm_ad.yml.example

+15-2
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,23 @@
22
# Authentication for Kerberos-based Realms
33
:realm: EXAMPLE.COM
44

5-
:keytab_path: /etc/foreman-proxy/realm_ad.keytab
5+
# Kerberos pricipal used to authenticate against Active Directory
66
:principal: realm-proxy@EXAMPLE.COM
77

8+
# Path to the keytab used to authenticate against Active Directory
9+
:keytab_path: /etc/foreman-proxy/realm_ad.keytab
10+
11+
# FQDN of the Domain Controller
12+
:domain_controller: dc.example.com
13+
814
# Optional: OU where the machine account shall be placed
915
#:ou: OU=Linux,OU=Servers,DC=example,DC=com
1016

11-
:domain_controller: dc.example.com
17+
# Optional: Prefix for the computername
18+
#:computername_prefix: ''
19+
20+
# Optional: Generate the computername by calculating the SHA256 hexdigest of the hostname
21+
#:computername_hash: false
22+
23+
# Optional: use the fqdn of the host to generate the computername
24+
#:computername_use_fqdn: false

lib/smart_proxy_realm_ad/configuration_loader.rb

+8-5
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,14 @@ def load_dependency_injection_wirings(container_instance, settings)
88
container_instance.dependency :realm_provider_impl,
99
lambda {
1010
::Proxy::AdRealm::Provider.new(
11-
settings[:realm],
12-
settings[:keytab_path],
13-
settings[:principal],
14-
settings[:domain_controller],
15-
settings[:ou]
11+
realm: settings[:realm],
12+
keytab_path: settings[:keytab_path],
13+
principal: settings[:principal],
14+
domain_controller: settings[:domain_controller],
15+
ou: settings[:ou],
16+
computername_prefix: settings[:computername_prefix],
17+
computername_hash: settings[:computername_hash],
18+
computername_use_fqdn: settings[:computername_use_fqdn]
1619
)
1720
}
1821
end

lib/smart_proxy_realm_ad/provider.rb

+42-27
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,26 @@
11
require 'proxy/kerberos'
22
require 'radcli'
3+
require 'digest'
34

45
module Proxy::AdRealm
56
class Provider
67
include Proxy::Log
78
include Proxy::Util
89
include Proxy::Kerberos
910

10-
def initialize(realm, keytab_path, principal, domain_controller, ou)
11-
@realm = realm
12-
@keytab_path = keytab_path
13-
@principal = principal
14-
@domain_controller = domain_controller
15-
@domain = realm.downcase
16-
@ou = ou
17-
logger.info "Proxy::AdRealm: initialize... #{@realm}, #{@keytab_path}, #{@principal}, #{@domain_controller}, #{@domain}, #{@ou}"
11+
attr_reader :realm, :keytab_path, :principal, :domain_controller, :domain, :ou, :computername_prefix, :computername_hash, :computername_use_fqdn
12+
13+
def initialize(options = {})
14+
@realm = options[:realm]
15+
@keytab_path = options[:keytab_path]
16+
@principal = options[:principal]
17+
@domain_controller = options[:domain_controller]
18+
@domain = options[:realm].downcase
19+
@ou = options[:ou]
20+
@computername_prefix = options[:computername_prefix]
21+
@computername_hash = options.fetch(:computername_hash, false)
22+
@computername_use_fqdn = options.fetch(:computername_use_fqdn, false)
23+
logger.info 'Proxy::AdRealm: initialize...'
1824
end
1925

2026
def check_realm(realm)
@@ -33,10 +39,12 @@ def create(realm, hostfqdn, params)
3339
password = generate_password
3440
result = { randompassword: password }
3541

42+
computername = hostfqdn_to_computername(hostfqdn)
43+
3644
if params[:rebuild] == 'true'
37-
do_host_rebuild(hostfqdn, password)
45+
radcli_password(computername, password)
3846
else
39-
do_host_create(hostfqdn, password)
47+
radcli_join(hostfqdn, computername, password)
4048
end
4149

4250
JSON.pretty_generate(result)
@@ -46,24 +54,31 @@ def delete(realm, hostfqdn)
4654
logger.info "Proxy::AdRealm: delete... #{realm}, #{hostfqdn}"
4755
kinit_radcli_connect
4856
check_realm(realm)
49-
radcli_delete(hostfqdn)
57+
computername = hostfqdn_to_computername(hostfqdn)
58+
radcli_delete(computername)
5059
end
5160

5261
private
5362

54-
def hostfqdn_to_hostname(host_fqdn)
55-
host_fqdn_split = host_fqdn.split('.')
56-
host_fqdn_split.first
57-
end
63+
def hostfqdn_to_computername(hostfqdn)
64+
computername = hostfqdn
65+
66+
# strip the domain from the host
67+
computername = computername.split('.').first unless computername_use_fqdn
68+
69+
# generate the SHA256 hexdigest from the computername
70+
computername = Digest::SHA256.hexdigest(computername) if computername_hash
71+
72+
# apply prefix if it has not already been applied
73+
computername = computername_prefix + computername if apply_computername_prefix?(computername)
5874

59-
def do_host_create(hostfqdn, password)
60-
hostname = hostfqdn_to_hostname(hostfqdn)
61-
radcli_join(hostfqdn, hostname, password)
75+
# limit length to 15 characters and upcase the computername
76+
# see https://support.microsoft.com/en-us/kb/909264
77+
computername[0, 15].upcase
6278
end
6379

64-
def do_host_rebuild(hostfqdn, password)
65-
hostname = hostfqdn_to_hostname hostfqdn
66-
radcli_password(hostname, password)
80+
def apply_computername_prefix?(computername)
81+
!computername_prefix.nil? && !computername_prefix.empty? && (computername_hash || !computername[0, computername_prefix.size].casecmp(computername_prefix).zero?)
6782
end
6883

6984
def kinit_radcli_connect
@@ -81,10 +96,10 @@ def radcli_connect
8196
conn
8297
end
8398

84-
def radcli_join(hostfqdn, hostname, password)
99+
def radcli_join(hostfqdn, computername, password)
85100
# Join computer
86101
enroll = Adcli::AdEnroll.new(@adconn)
87-
enroll.set_computer_name(hostname)
102+
enroll.set_computer_name(computername)
88103
enroll.set_host_fqdn(hostfqdn)
89104
enroll.set_domain_ou(@ou) if @ou
90105
enroll.set_computer_password(password)
@@ -96,19 +111,19 @@ def generate_password
96111
Array.new(20) { characters.sample }.join
97112
end
98113

99-
def radcli_password(hostname, password)
114+
def radcli_password(computername, password)
100115
# Reset a computer's password
101116
enroll = Adcli::AdEnroll.new(@adconn)
102-
enroll.set_computer_name(hostname)
117+
enroll.set_computer_name(computername)
103118
enroll.set_domain_ou(@ou) if @ou
104119
enroll.set_computer_password(password)
105120
enroll.password
106121
end
107122

108-
def radcli_delete(hostname)
123+
def radcli_delete(computername)
109124
# Delete a computer's account
110125
enroll = Adcli::AdEnroll.new(@adconn)
111-
enroll.set_computer_name(hostname)
126+
enroll.set_computer_name(computername)
112127
enroll.set_domain_ou(@ou) if @ou
113128
enroll.delete
114129
end

test/ad_provider_test.rb

+67-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,36 @@
66
class RealmAdTest < Test::Unit::TestCase
77
def setup
88
@realm = 'test_realm'
9-
@provider = Proxy::AdRealm::Provider.new('example.com', 'keytab_path', 'principal', 'domain-controller', nil)
9+
@provider = Proxy::AdRealm::Provider.new(
10+
realm: 'example.com',
11+
keytab_path: 'keytab_path',
12+
principal: 'principal',
13+
domain_controller: 'domain-controller',
14+
ou: nil,
15+
computername_prefix: nil,
16+
computername_hash: false,
17+
computername_use_fqdn: false
18+
)
1019
end
1120

1221
def test_create_host
1322
hostname = 'host.example.com'
14-
params = {}
15-
params[:rebuild] = 'false'
23+
computername = 'HOST'
24+
params = {
25+
rebuild: 'false'
26+
}
27+
@provider.expects(:check_realm).with(@realm)
28+
@provider.expects(:kinit_radcli_connect)
29+
@provider.expects(:generate_password).returns('password')
30+
@provider.expects(:radcli_join).with(hostname, computername, 'password')
31+
@provider.create(@realm, hostname, params)
32+
end
33+
34+
def test_create_host_generates_password
35+
hostname = 'host.example.com'
36+
params = {
37+
rebuild: 'false'
38+
}
1639
@provider.expects(:check_realm).with(@realm)
1740
@provider.expects(:kinit_radcli_connect)
1841
@provider.expects(:radcli_join)
@@ -21,6 +44,47 @@ def test_create_host
2144
assert_equal 20, response['randompassword'].size
2245
end
2346

47+
def test_create_host_prefixes_computername
48+
hostname = 'host.example.com'
49+
computername = 'ORG-HOST'
50+
params = {
51+
rebuild: 'false'
52+
}
53+
@provider.stubs(:computername_prefix).returns('ORG-')
54+
@provider.expects(:check_realm).with(@realm)
55+
@provider.expects(:kinit_radcli_connect)
56+
@provider.expects(:generate_password).returns('password')
57+
@provider.expects(:radcli_join).with(hostname, computername, 'password')
58+
@provider.create(@realm, hostname, params)
59+
end
60+
61+
def test_create_host_limits_computername_to_15_characters
62+
hostname = 'superlonghostname.example.com'
63+
computername = 'SUPERLONGHOSTNA'
64+
params = {
65+
rebuild: 'false'
66+
}
67+
@provider.expects(:check_realm).with(@realm)
68+
@provider.expects(:kinit_radcli_connect)
69+
@provider.expects(:generate_password).returns('password')
70+
@provider.expects(:radcli_join).with(hostname, computername, 'password')
71+
@provider.create(@realm, hostname, params)
72+
end
73+
74+
def test_create_host_applies_sha256_to_computername
75+
hostname = 'host.example.com'
76+
computername = '4740AE6347B0172'
77+
params = {
78+
rebuild: 'false'
79+
}
80+
@provider.stubs(:computername_hash).returns(true)
81+
@provider.expects(:check_realm).with(@realm)
82+
@provider.expects(:kinit_radcli_connect)
83+
@provider.expects(:generate_password).returns('password')
84+
@provider.expects(:radcli_join).with(hostname, computername, 'password')
85+
@provider.create(@realm, hostname, params)
86+
end
87+
2488
def test_create_with_unrecognized_realm_raises_exception
2589
assert_raises(Exception) { @provider.create('unknown_realm', 'a_host', {}) }
2690
end

0 commit comments

Comments
 (0)