icinga-cert-service 0.18.4 → 0.20.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9c3dc1c0ef720fdba4d9853fd323f36ade465f6682a227bfdfc9e4614982386c
4
- data.tar.gz: 9aa415a38d990a51bb0656f9bee4d6599427b089f367948bf8b84fb4fd6b748e
3
+ metadata.gz: 859374a9a0babbeac29da3e0bbaaf8728d1577f62da0084f96e9137bcab41937
4
+ data.tar.gz: 91077b678f06f00b26748ecbf32ae26dd9325fc72d37a9acfa2d4a445255f9cc
5
5
  SHA512:
6
- metadata.gz: 39231fd95943f5bcfa1b8d5ebb0d2fa6a06eb3a6cce1b274281dfc9d89f9bdef4e1ff5fff061ddaa9bd19cd5d3a086423008bfa04fca082388feab5801a15c69
7
- data.tar.gz: 2b0bcb1eb4e7fd4083c24ae4eb6066c96e9eddbf3898806c5e6cf6bf010c6e76ee8e98b9b1abfb3988593e5a4451089acfa36325a3093a20c37d4e9455ae8d7e
6
+ metadata.gz: ba2d51aeb1f6f670e17d369910f145ea5c9b6546352bacfba4f91810e6cac836f3fcb3b10e9c0b80a653f8910298d195cc21099ab4dd3d17041f75088d6fd083
7
+ data.tar.gz: 472ecdd2c342c81d9d803210f16c946737d95f97cbf90de84354280203e9de6985c02197234b50802f68d5f1fa9ab2d0fb4590cbfc2b7bda024b61b7ff80543d
@@ -20,7 +20,9 @@ require_relative '../lib/logging'
20
20
  # -----------------------------------------------------------------------------
21
21
 
22
22
  module Sinatra
23
+
23
24
  class CertServiceRest < Base
25
+
24
26
  register Sinatra::BasicAuth
25
27
 
26
28
  include Logging
@@ -36,7 +38,14 @@ module Sinatra
36
38
 
37
39
  config_file = ENV.fetch('CONFIG_FILE' , '/etc/icinga2-cert-service.yaml')
38
40
 
41
+ #unless( File.exist?(config_file) )
42
+ # logger.debug( format('INFO: The configuration file \'%s\' is missing. I use the default parameter.', config_file ) )
43
+ #else
44
+ # logger.debug( format('INFO: Use the configuration file \'%s\'', config_file ) )
45
+ #end
46
+
39
47
  configure do
48
+
40
49
  set :environment, :production
41
50
 
42
51
  # default configuration
@@ -169,6 +178,41 @@ module Sinatra
169
178
  end
170
179
  end
171
180
 
181
+ # create an PKI ticket
182
+ #
183
+ protect 'API' do
184
+ get '/v2/ticket/:host' do
185
+ result = ics.pki_ticket( host: params[:host], request: request.env )
186
+
187
+ # logger.debug(result)
188
+
189
+ result_status, result_message = result.values
190
+
191
+ status result_status
192
+ result_message + "\n"
193
+ end
194
+ end
195
+
196
+ # enable endpoint
197
+ #
198
+ protect 'API' do
199
+ get '/v2/enable_endpoint/:host' do
200
+ result = ics.enable_endpoint( host: params[:host], request: request.env )
201
+
202
+ # logger.debug(result)
203
+
204
+ result_status = result.dig(:status).to_i
205
+
206
+ status result_status
207
+
208
+ JSON.pretty_generate(result) + "\n"
209
+
210
+ # result_status, result_message = result.values
211
+ #
212
+ # status result_status
213
+ # result_message + "\n"
214
+ end
215
+ end
172
216
 
173
217
  # download an certificate
174
218
  #
@@ -176,7 +220,7 @@ module Sinatra
176
220
  get '/v2/cert/:host' do
177
221
  result = ics.check_certificate( host: params[:host], request: request.env )
178
222
 
179
- logger.debug(result)
223
+ # logger.debug(result)
180
224
 
181
225
  result_status = result.dig(:status).to_i
182
226
 
@@ -238,7 +282,7 @@ module Sinatra
238
282
 
239
283
  result = ics.download(file_name: params[:file_name], request: request.env)
240
284
 
241
- logger.debug(result)
285
+ # logger.debug(result)
242
286
 
243
287
  result_status = result.dig(:status).to_i
244
288
 
@@ -1,4 +1,6 @@
1
- #!/bin/sh
1
+ #!/bin/bash
2
+
3
+ set -e
2
4
 
3
5
  DESTINATION_DIR="/usr/local/icinga2-cert-service"
4
6
  SOURCE_DIR="/tmp/ruby-icinga-cert-service"
@@ -11,23 +13,53 @@ echo "install icinga2-cert-service .."
11
13
 
12
14
  cd ${SOURCE_DIR}
13
15
 
16
+ echo "[i] update gems"
14
17
  bundle update --quiet
15
- gem uninstall --quiet io-console bundler
16
18
 
17
19
  for i in lib bin templates assets
18
20
  do
19
- cp -a ${SOURCE_DIR}/${i} ${DESTINATION_DIR}/
21
+ cp -av ${SOURCE_DIR}/${i} ${DESTINATION_DIR}/
20
22
  done
21
23
 
22
- if [[ -e /sbin/openrc-run ]]
24
+ if [[ -e /.dockerenv ]]
23
25
  then
24
- cat << EOF >> /etc/conf.d/icinga2-cert-service
26
+ echo "[i] docker environment ..."
27
+
28
+ gem uninstall --quiet io-console bundler 2> /dev/null
29
+
30
+ else
31
+
32
+ if [[ ! -e /.dockerenv ]] && [[ ! -e /etc/icinga2-cert-service.yaml ]]
33
+ then
34
+ echo "[i] install config"
35
+ cp -v config_example.yaml /etc/icinga2-cert-service.yaml
36
+ fi
37
+
38
+
39
+ if [[ $(command -v openrc-run | wc -l) -eq 1 ]]
40
+ then
41
+ echo "[i] install openrc init and configuration files"
42
+
43
+ cp ${SOURCE_DIR}/init-script/openrc/icinga2-cert-service /etc/init.d/
44
+
45
+ cat << EOF > /etc/conf.d/icinga2-cert-service
25
46
 
26
47
  # Icinga2 cert service
27
48
  CERT_SERVICE_BIN="/usr/local/icinga2-cert-service/bin/icinga2-cert-service.rb"
28
49
 
29
50
  EOF
30
- cp ${SOURCE_DIR}/init-script/openrc/icinga2-cert-service /etc/init.d/
51
+ fi
52
+
53
+ if [[ $(command -v systemctl | wc -l) -eq 1 ]]
54
+ then
55
+ echo "[i] install system unit file"
56
+
57
+ cp ${SOURCE_DIR}/init-script/systemd/icinga-cert-service.service /etc/systemd/system/
58
+
59
+ systemctl daemon-reload
60
+ systemctl enable icinga-cert-service
61
+ systemctl start icinga-cert-service
62
+ fi
31
63
  fi
32
64
 
33
65
  export CERT_SERVICE=${DESTINATION_DIR}
@@ -76,13 +76,13 @@ module IcingaCertService
76
76
  @tmp_directory = '/tmp/icinga-pki'
77
77
 
78
78
  version = IcingaCertService::VERSION
79
- date = '2018-08-20'
79
+ date = '2019-05-12'
80
80
  detect_version
81
81
 
82
82
  logger.info('-----------------------------------------------------------------')
83
83
  logger.info(' certificate service for Icinga2')
84
84
  logger.info(format(' Version %s (%s)', version, date))
85
- logger.info(' Copyright 2017-2018 Bodo Schulz')
85
+ logger.info(' Copyright 2017-2020 Bodo Schulz')
86
86
  logger.info(format(' Icinga2 base version %s', @icinga_version))
87
87
  logger.info('-----------------------------------------------------------------')
88
88
  logger.info('')
@@ -126,7 +126,8 @@ module IcingaCertService
126
126
  @icinga_version = parts['v'].to_s.strip if(parts)
127
127
  end
128
128
 
129
- rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH => e
129
+ rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH, Errno::EADDRNOTAVAIL => e
130
+ logger.debug( "#{e}" )
130
131
  sleep( sleep_between_retries )
131
132
  retry
132
133
  rescue RestClient::ExceptionWithResponse => e
@@ -305,6 +306,22 @@ module IcingaCertService
305
306
  { status: 200, message: 'service restarted' }
306
307
  end
307
308
 
309
+ def read_ticket_salt
310
+
311
+ file_name = '/etc/icinga2/constants.conf'
312
+
313
+ return { status: 500, message: format( 'icinga2 not successful configured! file %s missing', file_name ) } unless( File.exist?(file_name) )
314
+
315
+ file = File.open(file_name, 'r')
316
+ contents = file.read
317
+
318
+ salt = contents.scan(/const TicketSalt(.*)=(.*)"(?<salt>.+\S)"/)
319
+ # salt = salt.flatten.first
320
+
321
+ salt.flatten.first
322
+ end
323
+
324
+
308
325
  # returns the hostname of itself
309
326
  #
310
327
  def icinga2_server_name
@@ -26,10 +26,12 @@ module IcingaCertService
26
26
  #
27
27
  def create_certificate( params )
28
28
 
29
- host = params.dig(:host)
30
- api_user = params.dig(:request, 'HTTP_X_API_USER')
31
- api_password = params.dig(:request, 'HTTP_X_API_PASSWORD')
32
- remote_addr = params.dig(:request, 'REMOTE_ADDR')
29
+ host = params.dig(:host)
30
+ api_user = params.dig(:request, 'HTTP_X_API_USER')
31
+ api_password = params.dig(:request, 'HTTP_X_API_PASSWORD')
32
+ api_hostname = params.dig(:request, 'HTTP_X_API_HOSTNAME')
33
+ api_ticketsalt = params.dig(:request, 'HTTP_X_API_TICKETSALT')
34
+ remote_addr = params.dig(:request, 'REMOTE_ADDR')
33
35
 
34
36
  return { status: 500, message: 'no hostname' } if( host.nil? )
35
37
  return { status: 500, message: 'missing API Credentials - API_USER' } if( api_user.nil?)
@@ -41,64 +43,78 @@ module IcingaCertService
41
43
 
42
44
  logger.info(format('got certificate request from %s', remote_addr))
43
45
 
44
- if( @icinga_master.nil? )
45
- begin
46
- server_name = icinga2_server_name
47
- rescue => e
48
- logger.error(e)
49
- server_name = @icinga_master
50
- else
51
- server_ip = icinga2_server_ip
52
- end
53
- else
54
- server_name = @icinga_master
55
- begin
56
- server_ip = icinga2_server_ip(server_name)
57
- rescue => e
58
- logger.error(server_name)
59
- logger.error(e)
60
-
61
- server_ip = '127.0.0.1'
62
- end
63
- end
46
+ server_ip, server_name = icinga_server_name
47
+
48
+ # if( @icinga_master.nil? )
49
+ # begin
50
+ # server_name = icinga2_server_name
51
+ # rescue => e
52
+ # logger.error(e)
53
+ # server_name = @icinga_master
54
+ # else
55
+ # server_ip = icinga2_server_ip
56
+ # end
57
+ # else
58
+ # server_name = @icinga_master
59
+ # begin
60
+ # server_ip = icinga2_server_ip(server_name)
61
+ # rescue => e
62
+ # logger.error(server_name)
63
+ # logger.error(e)
64
+ #
65
+ # server_ip = '127.0.0.1'
66
+ # end
67
+ # end
68
+
69
+ status, message = ensure_master_certificate_exists( server_name, host ).values
70
+
71
+ return { status: status, message: message } unless( status == 200 )
64
72
 
65
73
  pki_base_directory = '/etc/icinga2/pki'
66
74
  pki_base_directory = '/var/lib/icinga2/certs' if( @icinga_version != '2.7' )
67
75
 
68
- return { status: 500, message: 'no PKI directory found. Please configure first the Icinga2 Master!' } if( pki_base_directory.nil? )
69
-
70
76
  pki_master_key = format('%s/%s.key', pki_base_directory, server_name)
71
77
  pki_master_csr = format('%s/%s.csr', pki_base_directory, server_name)
72
78
  pki_master_crt = format('%s/%s.crt', pki_base_directory, server_name)
73
79
  pki_master_ca = format('%s/ca.crt', pki_base_directory)
74
80
 
75
- return { status: 500, message: 'no PKI directory found. Please configure first the Icinga2 Master!' } unless( File.exist?(pki_base_directory) )
76
-
77
- zone_base_directory = '/etc/icinga2/zone.d'
78
-
79
- FileUtils.mkpath( format('%s/global-templates', zone_base_directory) )
80
- FileUtils.mkpath( format('%s/%s', zone_base_directory, host) )
81
-
81
+ #pki_base_directory = '/etc/icinga2/pki'
82
+ #pki_base_directory = '/var/lib/icinga2/certs' if( @icinga_version != '2.7' )
82
83
  #
83
- unless File.exist?(format('%s/global-templates/services.conf', zone_base_directory) )
84
-
85
- if( File.exist?('/etc/icinga2/conf.d/services.conf') )
86
- FileUtils.mv('/etc/icinga2/conf.d/services.conf', format('%s/global-templates/services.conf', zone_base_directory))
87
- else
88
- logger.error('missing services.conf under /etc/icinga2/conf.d')
89
- end
90
- end
91
-
92
- logger.debug(format('search PKI files for the Master \'%s\'', server_name))
93
-
94
- if( !File.exist?(pki_master_key) || !File.exist?(pki_master_csr) || !File.exist?(pki_master_crt) )
95
- logger.error('missing file')
96
- logger.debug(pki_master_key)
97
- logger.debug(pki_master_csr)
98
- logger.debug(pki_master_crt)
99
-
100
- return { status: 500, message: format('missing PKI for Icinga2 Master \'%s\'', server_name) }
101
- end
84
+ #return { status: 500, message: 'no PKI directory found. Please configure first the Icinga2 Master!' } if( pki_base_directory.nil? )
85
+ #
86
+ #pki_master_key = format('%s/%s.key', pki_base_directory, server_name)
87
+ #pki_master_csr = format('%s/%s.csr', pki_base_directory, server_name)
88
+ #pki_master_crt = format('%s/%s.crt', pki_base_directory, server_name)
89
+ #pki_master_ca = format('%s/ca.crt', pki_base_directory)
90
+ #
91
+ #return { status: 500, message: 'no PKI directory found. Please configure first the Icinga2 Master!' } unless( File.exist?(pki_base_directory) )
92
+ #
93
+ #zone_base_directory = '/etc/icinga2/zone.d'
94
+ #
95
+ #FileUtils.mkpath( format('%s/global-templates', zone_base_directory) )
96
+ #FileUtils.mkpath( format('%s/%s', zone_base_directory, host) )
97
+ #
98
+ ##
99
+ #unless File.exist?(format('%s/global-templates/services.conf', zone_base_directory) )
100
+ #
101
+ # if( File.exist?('/etc/icinga2/conf.d/services.conf') )
102
+ # FileUtils.mv('/etc/icinga2/conf.d/services.conf', format('%s/global-templates/services.conf', zone_base_directory))
103
+ # else
104
+ # logger.error('missing services.conf under /etc/icinga2/conf.d')
105
+ # end
106
+ #end
107
+ #
108
+ #logger.debug(format('search PKI files for the Master \'%s\'', server_name))
109
+ #
110
+ #if( !File.exist?(pki_master_key) || !File.exist?(pki_master_csr) || !File.exist?(pki_master_crt) )
111
+ # logger.error('missing file')
112
+ # logger.debug(pki_master_key)
113
+ # logger.debug(pki_master_csr)
114
+ # logger.debug(pki_master_crt)
115
+ #
116
+ # return { status: 500, message: format('missing PKI for Icinga2 Master \'%s\'', server_name) }
117
+ #end
102
118
 
103
119
  tmp_host_directory = format('%s/%s', @tmp_directory, host)
104
120
  # uid = File.stat('/etc/icinga2/conf.d').uid
@@ -115,7 +131,7 @@ module IcingaCertService
115
131
  pki_satellite_key = format('%s/%s.key', tmp_host_directory, host)
116
132
  pki_satellite_csr = format('%s/%s.csr', tmp_host_directory, host)
117
133
  pki_satellite_crt = format('%s/%s.crt', tmp_host_directory, host)
118
- pki_ticket = '%PKI_TICKET%'
134
+ pki_ticket = '%PKI_TICKET%'
119
135
 
120
136
  commands = []
121
137
 
@@ -221,6 +237,133 @@ module IcingaCertService
221
237
  end
222
238
 
223
239
 
240
+ def pki_ticket( params )
241
+
242
+ host = params.dig(:host)
243
+ api_user = params.dig(:request, 'HTTP_X_API_USER')
244
+ api_password = params.dig(:request, 'HTTP_X_API_PASSWORD')
245
+ api_hostname = params.dig(:request, 'HTTP_X_API_HOSTNAME')
246
+ api_ticketsalt = params.dig(:request, 'HTTP_X_API_TICKETSALT')
247
+ remote_addr = params.dig(:request, 'REMOTE_ADDR')
248
+ real_ip = params.dig(:request, 'HTTP_X_REAL_IP')
249
+ forwarded_for = params.dig(:request, 'HTTP_X_FORWARDED_FOR')
250
+
251
+ logger.error('no hostname') if( host.nil? )
252
+ logger.error('missing API Credentials - API_USER') if( api_user.nil? )
253
+ logger.error('missing API Credentials - API_PASSWORD') if( api_password.nil? )
254
+
255
+ return { status: 401, message: 'no hostname' } if( host.nil? )
256
+ return { status: 401, message: 'missing API Credentials - API_USER' } if( api_user.nil?)
257
+ return { status: 401, message: 'missing API Credentials - API_PASSWORD' } if( api_password.nil? )
258
+
259
+ password = read_api_credentials( api_user: api_user )
260
+ salt_passend = ( read_ticket_salt == api_ticketsalt )
261
+
262
+ logger.error('wrong API Credentials') if( password.nil? || api_password != password )
263
+ logger.error('wrong Icinga2 Version (the master required => 2.8)') if( @icinga_version == '2.7' )
264
+
265
+ return { status: 401, message: 'wrong API Credentials' } if( password.nil? || api_password != password )
266
+
267
+ auth_params = { remote_addr: remote_addr, real_ip: real_ip, forwarded_for: forwarded_for, host: host, salt_passend: salt_passend }
268
+
269
+ # logger.debug( " - auth_params : #{auth_params}" )
270
+ # logger.debug( " - api ticketsalt : #{api_ticketsalt}" )
271
+ # logger.debug( " - ticketsalt passed: #{salt_passend}" )
272
+
273
+ authorized, remote_short, host_short = is_remote_clients_authorized( auth_params ).values
274
+
275
+ if( authorized == false )
276
+ logger.error(format('This client (%s/%s) cannot get an pki ticket for %s', remote_addr, real_ip, host ) ) unless( host_short == remote_short )
277
+
278
+ return { status: 409, message: format('This client cannot get an pki ticket for %s', host ) } unless( host_short == remote_short )
279
+ end
280
+
281
+ logger.info(format('got ticket request from %s (%s)', host, real_ip))
282
+
283
+ server_ip, server_name = icinga_server_name
284
+
285
+ status, message = ensure_master_certificate_exists( server_name, host ).values
286
+
287
+ return { status: status, message: message } unless( status == 200 )
288
+
289
+ command = format('icinga2 pki ticket --cn %s', host)
290
+
291
+ result = exec_command(cmd: command)
292
+
293
+ logger.debug( "#{result} (#{result.class})")
294
+
295
+ exec_code, exec_message = result.values
296
+
297
+ logger.debug( format( ' - [%s] %s', exec_code, exec_message.strip ) )
298
+
299
+ if( exec_code != true )
300
+ logger.error(exec_message)
301
+ logger.error(format(' command \'%s\'', command))
302
+ logger.error(format(' returned with exit-code \'%s\'', exec_code))
303
+
304
+ return { status: 500, message: format('Internal Error: \'%s\' - \'cmd %s\'', exec_message, command) }
305
+ end
306
+
307
+ { status: 200, message: exec_message.strip }
308
+ end
309
+
310
+
311
+ def enable_endpoint( params )
312
+
313
+ host = params.dig(:host)
314
+ api_user = params.dig(:request, 'HTTP_X_API_USER')
315
+ api_password = params.dig(:request, 'HTTP_X_API_PASSWORD')
316
+ api_hostname = params.dig(:request, 'HTTP_X_API_HOSTNAME')
317
+ api_ticketsalt = params.dig(:request, 'HTTP_X_API_TICKETSALT')
318
+ remote_addr = params.dig(:request, 'REMOTE_ADDR')
319
+ real_ip = params.dig(:request, 'HTTP_X_REAL_IP')
320
+ forwarded_for = params.dig(:request, 'HTTP_X_FORWARDED_FOR')
321
+
322
+ logger.error('no hostname') if( host.nil? )
323
+ logger.error('missing API Credentials - API_USER') if( api_user.nil? )
324
+ logger.error('missing API Credentials - API_PASSWORD') if( api_password.nil? )
325
+
326
+ return { status: 401, message: 'no hostname' } if( host.nil? )
327
+ return { status: 401, message: 'missing API Credentials - API_USER' } if( api_user.nil?)
328
+ return { status: 401, message: 'missing API Credentials - API_PASSWORD' } if( api_password.nil? )
329
+
330
+ password = read_api_credentials( api_user: api_user )
331
+ salt_passend = ( read_ticket_salt == api_ticketsalt )
332
+
333
+ logger.error('wrong API Credentials') if( password.nil? || api_password != password )
334
+ logger.error('wrong Icinga2 Version (the master required => 2.8)') if( @icinga_version == '2.7' )
335
+
336
+ return { status: 401, message: 'wrong API Credentials' } if( password.nil? || api_password != password )
337
+
338
+ auth_params = { remote_addr: remote_addr, real_ip: real_ip, forwarded_for: forwarded_for, host: host, salt_passend: salt_passend }
339
+
340
+ authorized, remote_short, host_short = is_remote_clients_authorized( auth_params ).values
341
+
342
+ if( authorized == false )
343
+ logger.error(format('This client (%s/%s) cannot enable an endpoint for %s', remote_addr, real_ip, host ) ) unless( host_short == remote_short )
344
+
345
+ return { status: 409, message: format('This client cannot enable an endpoint for %s', host ) } unless( host_short == remote_short )
346
+ end
347
+
348
+ # add params to create the endpoint not in zones.d
349
+ #
350
+ params[:satellite] = false
351
+
352
+ # add API User for this Endpoint
353
+ #
354
+ # add_api_user(params)
355
+
356
+ # add Endpoint (and API User)
357
+ # and create a backup of the generated files
358
+ #
359
+ result = add_endpoint(host: host, satellite: false)
360
+
361
+ logger.debug( result )
362
+
363
+ result
364
+ end
365
+
366
+
224
367
  # check the certificate Data
225
368
  #
226
369
  # @param [Hash, #read] params
@@ -241,11 +384,13 @@ module IcingaCertService
241
384
  #
242
385
  def check_certificate( params )
243
386
 
244
- host = params.dig(:host)
245
- checksum = params.dig(:request, 'HTTP_X_CHECKSUM')
246
- api_user = params.dig(:request, 'HTTP_X_API_USER')
247
- api_password = params.dig(:request, 'HTTP_X_API_PASSWORD')
248
- remote_addr = params.dig(:request, 'REMOTE_ADDR')
387
+ host = params.dig(:host)
388
+ checksum = params.dig(:request, 'HTTP_X_CHECKSUM')
389
+ api_user = params.dig(:request, 'HTTP_X_API_USER')
390
+ api_password = params.dig(:request, 'HTTP_X_API_PASSWORD')
391
+ api_hostname = params.dig(:request, 'HTTP_X_API_HOSTNAME')
392
+ api_ticketsalt = params.dig(:request, 'HTTP_X_API_TICKETSALT')
393
+ remote_addr = params.dig(:request, 'REMOTE_ADDR')
249
394
 
250
395
  return { status: 500, message: 'no valid data to get the certificate' } if( host.nil? || checksum.nil? )
251
396
 
@@ -335,12 +480,14 @@ module IcingaCertService
335
480
  #
336
481
  def sign_certificate( params )
337
482
 
338
- host = params.dig(:host)
339
- api_user = params.dig(:request, 'HTTP_X_API_USER')
340
- api_password = params.dig(:request, 'HTTP_X_API_PASSWORD')
341
- remote_addr = params.dig(:request, 'REMOTE_ADDR')
342
- real_ip = params.dig(:request, 'HTTP_X_REAL_IP')
343
- forwarded_for = params.dig(:request, 'HTTP_X_FORWARDED_FOR')
483
+ host = params.dig(:host)
484
+ api_user = params.dig(:request, 'HTTP_X_API_USER')
485
+ api_password = params.dig(:request, 'HTTP_X_API_PASSWORD')
486
+ api_hostname = params.dig(:request, 'HTTP_X_API_HOSTNAME')
487
+ api_ticketsalt = params.dig(:request, 'HTTP_X_API_TICKETSALT')
488
+ remote_addr = params.dig(:request, 'REMOTE_ADDR')
489
+ real_ip = params.dig(:request, 'HTTP_X_REAL_IP')
490
+ forwarded_for = params.dig(:request, 'HTTP_X_FORWARDED_FOR')
344
491
 
345
492
  # logger.debug(params)
346
493
 
@@ -352,7 +499,8 @@ module IcingaCertService
352
499
  return { status: 401, message: 'missing API Credentials - API_USER' } if( api_user.nil?)
353
500
  return { status: 401, message: 'missing API Credentials - API_PASSWORD' } if( api_password.nil? )
354
501
 
355
- password = read_api_credentials( api_user: api_user )
502
+ password = read_api_credentials( api_user: api_user )
503
+ salt_passend = ( read_ticket_salt == api_ticketsalt )
356
504
 
357
505
  logger.error('wrong API Credentials') if( password.nil? || api_password != password )
358
506
  logger.error('wrong Icinga2 Version (the master required => 2.8)') if( @icinga_version == '2.7' )
@@ -360,40 +508,51 @@ module IcingaCertService
360
508
  return { status: 401, message: 'wrong API Credentials' } if( password.nil? || api_password != password )
361
509
  return { status: 401, message: 'wrong Icinga2 Version (the master required => 2.8)' } if( @icinga_version == '2.7' )
362
510
 
363
- unless(remote_addr.nil? && real_ip.nil?)
364
- logger.info('we running behind a proxy')
511
+ auth_params = { remote_addr: remote_addr, real_ip: real_ip, forwarded_for: forwarded_for, host: host, salt_passend: salt_passend }
365
512
 
366
- logger.debug("remote addr #{remote_addr}")
367
- logger.debug("real ip #{real_ip}")
368
- logger.debug("forwarded for #{forwarded_for}")
513
+ authorized, remote_short, host_short = is_remote_clients_authorized( auth_params ).values
369
514
 
370
- remote_addr = forwarded_for
371
- end
372
-
373
- unless( remote_addr.nil? )
374
- host_short = host.split('.')
375
- host_short = if( host_short.count > 0 )
376
- host_short.first
377
- else
378
- host
379
- end
380
-
381
- remote_fqdn = Resolv.getnames(remote_addr).sort.last
382
- remote_short = remote_fqdn.split('.')
383
- remote_short = if( remote_short.count > 0 )
384
- remote_short.first
385
- else
386
- remote_fqdn
387
- end
515
+ if( authorized == false )
388
516
 
389
- logger.debug( "host_short #{host_short}" )
390
- logger.debug( "remote_short #{remote_short}" )
391
-
392
- logger.error(format('This client (%s) cannot sign the certificate for %s', remote_fqdn, host ) ) unless( host_short == remote_short )
517
+ logger.error(format('This client (%s) cannot sign the certificate for %s', remote_addr, host ) ) unless( host_short == remote_short )
393
518
 
394
519
  return { status: 409, message: format('This client cannot sign the certificate for %s', host ) } unless( host_short == remote_short )
395
520
  end
396
521
 
522
+ # unless(remote_addr.nil? && real_ip.nil?)
523
+ # logger.info('we running behind a proxy')
524
+ #
525
+ # logger.debug("remote addr #{remote_addr}")
526
+ # logger.debug("real ip #{real_ip}")
527
+ # logger.debug("forwarded for #{forwarded_for}")
528
+ #
529
+ # remote_addr = forwarded_for
530
+ # end
531
+ #
532
+ # unless( remote_addr.nil? )
533
+ # host_short = host.split('.')
534
+ # host_short = if( host_short.count > 0 )
535
+ # host_short.first
536
+ # else
537
+ # host
538
+ # end
539
+ #
540
+ # remote_fqdn = Resolv.getnames(remote_addr).sort.last
541
+ # remote_short = remote_fqdn.split('.')
542
+ # remote_short = if( remote_short.count > 0 )
543
+ # remote_short.first
544
+ # else
545
+ # remote_fqdn
546
+ # end
547
+ #
548
+ # logger.debug( "host_short #{host_short}" )
549
+ # logger.debug( "remote_short #{remote_short}" )
550
+ #
551
+ # logger.error(format('This client (%s) cannot sign the certificate for %s', remote_fqdn, host ) ) unless( host_short == remote_short )
552
+ #
553
+ # return { status: 409, message: format('This client cannot sign the certificate for %s', host ) } unless( host_short == remote_short )
554
+ # end
555
+
397
556
  logger.info( format('sign certificate for %s', host) )
398
557
 
399
558
  # /etc/icinga2 # icinga2 ca list | grep icinga2-satellite-1.matrix.lan | sort -k2
@@ -462,5 +621,139 @@ module IcingaCertService
462
621
 
463
622
  { status: 204 }
464
623
  end
624
+
625
+
626
+ private
627
+ def ensure_master_certificate_exists( server_name, host )
628
+
629
+
630
+ pki_base_directory = '/etc/icinga2/pki'
631
+ pki_base_directory = '/var/lib/icinga2/certs' if( @icinga_version != '2.7' )
632
+
633
+ return { status: 500, message: 'no PKI directory found. Please configure first the Icinga2 Master!' } if( pki_base_directory.nil? )
634
+
635
+ pki_master_key = format('%s/%s.key', pki_base_directory, server_name)
636
+ pki_master_csr = format('%s/%s.csr', pki_base_directory, server_name)
637
+ pki_master_crt = format('%s/%s.crt', pki_base_directory, server_name)
638
+ #pki_master_ca = format('%s/ca.crt', pki_base_directory)
639
+
640
+ return { status: 500, message: 'no PKI directory found. Please configure first the Icinga2 Master!' } unless( File.exist?(pki_base_directory) )
641
+
642
+ zone_base_directory = '/etc/icinga2/zone.d'
643
+
644
+ FileUtils.mkpath( format('%s/global-templates', zone_base_directory) )
645
+ FileUtils.mkpath( format('%s/%s', zone_base_directory, host) )
646
+
647
+ #
648
+ unless File.exist?(format('%s/global-templates/services.conf', zone_base_directory) )
649
+
650
+ if( File.exist?('/etc/icinga2/conf.d/services.conf') )
651
+ FileUtils.mv('/etc/icinga2/conf.d/services.conf', format('%s/global-templates/services.conf', zone_base_directory))
652
+ else
653
+ logger.error('missing services.conf under /etc/icinga2/conf.d')
654
+ end
655
+ end
656
+
657
+ logger.debug(format('search PKI files for the Master \'%s\'', server_name))
658
+
659
+ if( !File.exist?(pki_master_key) || !File.exist?(pki_master_csr) || !File.exist?(pki_master_crt) )
660
+ logger.error('missing file')
661
+ logger.error(format(' - %s', pki_master_key)) if( !File.exist?(pki_master_key))
662
+ logger.error(format(' - %s', pki_master_csr)) if( !File.exist?(pki_master_csr))
663
+ logger.error(format(' - %s', pki_master_crt)) if( !File.exist?(pki_master_crt))
664
+
665
+ return { status: 500, message: format('missing PKI for Icinga2 Master \'%s\'', server_name) }
666
+ end
667
+
668
+ return { status: 200 }
669
+
670
+ end
671
+
672
+
673
+ def icinga_server_name
674
+
675
+ if( @icinga_master.nil? )
676
+ begin
677
+ server_name = icinga2_server_name
678
+ rescue => e
679
+ logger.error(e)
680
+ server_name = @icinga_master
681
+ else
682
+ server_ip = icinga2_server_ip
683
+ end
684
+ else
685
+ server_name = @icinga_master
686
+ begin
687
+ server_ip = icinga2_server_ip(server_name)
688
+ rescue => e
689
+ logger.error(server_name)
690
+ logger.error(e)
691
+
692
+ server_ip = '127.0.0.1'
693
+ end
694
+ end
695
+
696
+ [server_ip, server_name]
697
+
698
+ end
699
+
700
+
701
+ def is_remote_clients_authorized( params )
702
+
703
+ remote_addr = params.dig(:remote_addr)
704
+ remote_fqdn = nil
705
+ remote_short = nil
706
+ real_ip = params.dig(:real_ip)
707
+ forwarded_for = params.dig(:forwarded_for)
708
+ host = params.dig(:host)
709
+ salt_passend = params.dig(:salt_passend)
710
+
711
+ logger.debug("params #{params}")
712
+
713
+ result = true
714
+
715
+ unless(remote_addr.nil? && real_ip.nil?)
716
+ logger.debug('we running behind a proxy')
717
+
718
+ logger.debug(" remote addr #{remote_addr}")
719
+ logger.debug(" real ip #{real_ip}")
720
+ logger.debug(" forwarded for #{forwarded_for}")
721
+
722
+ remote_addr = forwarded_for
723
+ end
724
+
725
+ host_short = host.split('.')
726
+ host_short = if( host_short.count > 0 )
727
+ host_short.first
728
+ else
729
+ host
730
+ end
731
+
732
+ logger.debug( " host_fqdn #{host}" )
733
+ logger.debug( " host_short #{host_short}" )
734
+
735
+ if( salt_passend == false )
736
+
737
+ unless( remote_addr.nil? )
738
+ remote_fqdn = Resolv.getnames(remote_addr)
739
+ remote_fqdn = remote_fqdn.sort.last
740
+ remote_short = remote_fqdn.split('.')
741
+ remote_short = if( remote_short.count > 0 )
742
+ remote_short.first
743
+ else
744
+ remote_fqdn
745
+ end
746
+
747
+ logger.debug( " remote_fqdn #{remote_fqdn}" )
748
+ logger.debug( " remote_short #{remote_short}" )
749
+
750
+ result = false
751
+ end
752
+ end
753
+
754
+ { result: result, remote_short: remote_short, host_short: host_short }
755
+
756
+ end
757
+
465
758
  end
466
759
  end
@@ -21,8 +21,11 @@ module IcingaCertService
21
21
  #
22
22
  def add_endpoint(params)
23
23
 
24
- host = validate( params, required: true, var: 'host', type: String )
25
- satellite = validate( params, required: false, var: 'satellite', type: Boolean ) || false
24
+ logger.debug("add_endpoint(#{params})")
25
+
26
+ host = validate( params, required: true , var: 'host' , type: String )
27
+ satellite = validate( params, required: false, var: 'satellite', type: Boolean ) || false
28
+ file_name = '/etc/icinga2/zones.conf'
26
29
 
27
30
  return { status: 500, message: 'no hostname to create an endpoint' } if host.nil?
28
31
 
@@ -36,10 +39,8 @@ module IcingaCertService
36
39
 
37
40
  # logger.debug( ret )
38
41
 
39
- if( satellite )
40
- file_name = '/etc/icinga2/zones.conf'
41
- else
42
- zone_directory = format('/etc/icinga2/zones.d/%s', host)
42
+ unless( satellite )
43
+ zone_directory = format('/etc/icinga2/satellites.d/%s', host)
43
44
  file_name = format('%s/%s.conf', zone_directory, host)
44
45
 
45
46
  begin
@@ -51,6 +52,8 @@ module IcingaCertService
51
52
 
52
53
  if( File.exist?(file_name) )
53
54
 
55
+ logger.debug( format('the zone file (%s) for the endpoint %s exists', file_name, host) )
56
+
54
57
  file = File.open(file_name, 'r')
55
58
  contents = file.read
56
59
 
@@ -67,6 +70,8 @@ module IcingaCertService
67
70
 
68
71
  scan_endpoint = result.scan(/object Endpoint(.*)"(?<endpoint>.+\S)"/).flatten
69
72
 
73
+ #logger.debug( "#{scan_endpoint} (#{scan_endpoint.class})" )
74
+
70
75
  return { status: 200, message: format('the configuration for the endpoint %s already exists', host) } if( scan_endpoint.include?(host) == true )
71
76
  end
72
77
 
@@ -1,5 +1,5 @@
1
1
 
2
2
  module IcingaCertService
3
3
  # static version string
4
- VERSION = '0.18.4'.freeze
4
+ VERSION = '0.20.0'.freeze
5
5
  end
@@ -27,10 +27,11 @@ module Logging
27
27
  def configure_logger_for( classname )
28
28
 
29
29
  log_level = ENV.fetch('LOG_LEVEL', 'DEBUG' )
30
- level = log_level.dup
30
+
31
+ level = log_level.upcase.dup
31
32
 
32
33
  # DEBUG < INFO < WARN < ERROR < FATAL < UNKNOWN
33
- log_level = case level.upcase
34
+ log_level = case level
34
35
  when 'DEBUG'
35
36
  Logger::DEBUG # Low-level information for developers.
36
37
  when 'INFO'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: icinga-cert-service
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.18.4
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Bodo Schulz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2019-02-22 00:00:00.000000000 Z
11
+ date: 2020-03-20 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: etc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.1'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.1'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: openssl
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -203,7 +217,6 @@ files:
203
217
  - README.md
204
218
  - bin/icinga2-cert-service.rb
205
219
  - bin/installer.sh
206
- - bin/test.rb
207
220
  - lib/cert-service.rb
208
221
  - lib/cert-service/backup.rb
209
222
  - lib/cert-service/certificate_handler.rb
@@ -239,7 +252,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
239
252
  version: '0'
240
253
  requirements: []
241
254
  rubyforge_project:
242
- rubygems_version: 2.7.6
255
+ rubygems_version: 2.7.10
243
256
  signing_key:
244
257
  specification_version: 4
245
258
  summary: Icinga Certificate Service
@@ -1,28 +0,0 @@
1
- #!/usr/bin/env ruby
2
- #
3
- # 05.10.2016 - Bodo Schulz
4
- #
5
- #
6
- # v2.1.0
7
-
8
- # -----------------------------------------------------------------------------
9
-
10
- require 'ruby_dig' if RUBY_VERSION < '2.3'
11
-
12
- require 'sinatra/base'
13
- require 'sinatra/basic_auth'
14
- require 'json'
15
- require 'yaml'
16
-
17
- require_relative '../lib/cert-service'
18
- require_relative '../lib/logging'
19
-
20
- # -----------------------------------------------------------------------------
21
-
22
- config = {
23
- icinga_master: 'localhost'
24
- }
25
-
26
- ics = IcingaCertService::Client.new(config)
27
-
28
- # -----------------------------------------------------------------------------