icinga-cert-service 0.18.4 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
- # -----------------------------------------------------------------------------