@@ -30,24 +30,19 @@ def do_authorize_with_trusted_hosts
3030 # HTTP: test the reverse DNS entry of the remote IP
3131 trusted_hosts = Proxy ::SETTINGS . trusted_hosts
3232 if trusted_hosts
33- logger . debug "verifying remote client #{ request . env [ 'REMOTE_ADDR' ] } against trusted_hosts #{ trusted_hosts } "
33+ fqdn = ( https? ( request ) ? https_cert_cn ( request ) : remote_fqdn ( Proxy :: SETTINGS . forward_verify ) ) . downcase
3434
35- if [ 'yes' , 'on' , 1 ] . include? request . env [ 'HTTPS' ] . to_s
36- fqdn = https_cert_cn
37- else
38- fqdn = remote_fqdn ( Proxy ::SETTINGS . forward_verify )
39- end
40- fqdn = fqdn . downcase
35+ logger . debug "verifying remote client #{ fqdn } against trusted_hosts #{ trusted_hosts } "
4136
42- unless Proxy :: SETTINGS . trusted_hosts . include? ( fqdn )
37+ unless trusted_hosts . include? ( fqdn )
4338 log_halt 403 , "Untrusted client #{ fqdn } attempted to access #{ request . path_info } . Check :trusted_hosts: in settings.yml"
4439 end
4540 end
4641 end
4742
4843 def do_authorize_with_ssl_client
49- if [ 'yes' , 'on' , '1' ] . include? request . env [ 'HTTPS' ] . to_s
50- if request . env [ 'SSL_CLIENT_CERT' ] . to_s . empty?
44+ if https? ( request )
45+ if https_client_cert_raw ( request ) . empty?
5146 log_halt 403 , "No client SSL certificate supplied"
5247 end
5348 else
@@ -60,6 +55,84 @@ def do_authorize_any
6055 do_authorize_with_trusted_hosts
6156 do_authorize_with_ssl_client
6257 end
58+
59+ private
60+
61+ def https? ( request )
62+ [ 'yes' , 'on' , 1 ] . include? ( request . env [ 'HTTPS' ] . to_s )
63+ end
64+
65+ def https_client_cert_raw ( request )
66+ request . env [ 'SSL_CLIENT_CERT' ] . to_s
67+ end
68+
69+ # read the HTTPS client certificate from the environment and extract its CN
70+ def https_cert_cn ( request )
71+ log_halt 403 , 'No HTTPS environment' unless https? ( request )
72+
73+ certificate_raw = https_client_cert_raw ( request )
74+ certificate = parse_openssl_cert ( certificate_raw )
75+ log_halt 403 , 'could not read client cert from environment' unless certificate
76+
77+ cn = get_cn_from_certificate ( certificate )
78+ log_halt 403 , 'could not read CN from the client certificate' unless certificate
79+
80+ cn
81+ rescue OpenSSL ::X509 ::CertificateError => e
82+ log_halt 403 , "could not parse the client certificate\n \n #{ e . message } "
83+ end
84+
85+ # reverse lookup an IP address while verifying it via forward resolv
86+ def remote_fqdn ( forward_verify = true )
87+ ip = request . env [ 'REMOTE_ADDR' ]
88+ log_halt 403 , 'could not get remote address from environment' if ip . empty?
89+
90+ begin
91+ dns = resolv
92+ fqdn = dns . getname ( ip )
93+ rescue Resolv ::ResolvError => e
94+ log_halt 403 , "unable to resolve hostname for ip address #{ ip } \n \n #{ e . message } "
95+ end
96+
97+ if forward_verify
98+ begin
99+ forward = dns . getaddresses ( fqdn )
100+ rescue Resolv ::ResolvError => e
101+ log_halt 403 , "could not forward verify the remote hostname - #{ fqdn } (#{ ip } )\n \n #{ e . message } "
102+ end
103+
104+ if forward . include? ( ip )
105+ fqdn
106+ else
107+ log_halt 403 , "untrusted client has no matching forward DNS lookup - #{ fqdn } (#{ ip } )"
108+ end
109+ else
110+ fqdn
111+ end
112+ end
113+
114+ def parse_openssl_cert ( certificate_raw )
115+ return if certificate_raw . nil? || certificate_raw . empty?
116+
117+ OpenSSL ::X509 ::Certificate . new ( certificate_raw )
118+ end
119+
120+ def get_cn_from_certificate ( certificate )
121+ return unless certificate &.subject
122+
123+ cn = certificate . subject . to_a . find { |oid | oid == 'CN' }
124+ return unless cn
125+
126+ cn [ 2 ]
127+ end
128+ end
129+
130+ def authorize!
131+ include Helpers
132+
133+ before do
134+ do_authorize_with_any
135+ end
63136 end
64137
65138 def authorize_with_trusted_hosts
0 commit comments