icinga-cert-service 0.18.4
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 +7 -0
- data/LICENSE +504 -0
- data/README.md +344 -0
- data/bin/icinga2-cert-service.rb +278 -0
- data/bin/installer.sh +33 -0
- data/bin/test.rb +28 -0
- data/lib/cert-service.rb +323 -0
- data/lib/cert-service/backup.rb +42 -0
- data/lib/cert-service/certificate_handler.rb +466 -0
- data/lib/cert-service/configure_icinga.rb +1 -0
- data/lib/cert-service/download.rb +24 -0
- data/lib/cert-service/endpoint_handler.rb +98 -0
- data/lib/cert-service/executor.rb +34 -0
- data/lib/cert-service/in-memory-cache.rb +43 -0
- data/lib/cert-service/templates.rb +62 -0
- data/lib/cert-service/version.rb +5 -0
- data/lib/cert-service/zone_handler.rb +71 -0
- data/lib/logging.rb +61 -0
- data/lib/monkey_patches.rb +128 -0
- data/lib/util.rb +94 -0
- data/lib/validator.rb +38 -0
- metadata +246 -0
data/README.md
ADDED
@@ -0,0 +1,344 @@
|
|
1
|
+
icinga-cert-service
|
2
|
+
===================
|
3
|
+
|
4
|
+
The Icinga-Cert-Service is a small service for creating, downloading or signing an Icinga2 certificate.
|
5
|
+
The service can be used to connect Icinga2 satellites or agents dynamically to an Icinga2 master.
|
6
|
+
|
7
|
+
The Cert service is implemented in ruby and offers a simple REST API.
|
8
|
+
|
9
|
+
# Status
|
10
|
+
[][travis]
|
11
|
+
[][gemnasium]
|
12
|
+
|
13
|
+
[travis]: https://travis-ci.org/bodsch/ruby-icinga-cert-service
|
14
|
+
[gemnasium]: https://gemnasium.com/github.com/bodsch/ruby-icinga-cert-service
|
15
|
+
|
16
|
+
|
17
|
+
# Start
|
18
|
+
|
19
|
+
To start them run `ruby bin/rest-service.rb`
|
20
|
+
|
21
|
+
The following environment variables can be set:
|
22
|
+
|
23
|
+
- `ICINGA2_MASTER` (default: `nil`)
|
24
|
+
- `ICINGA2_API_PORT` (default: `5665`)
|
25
|
+
- `ICINGA2_API_USER` (default: `root`)
|
26
|
+
- `ICINGA2_API_PASSWORD` (default: `icinga`)
|
27
|
+
- `REST_SERVICE_PORT` (default: `8080`)
|
28
|
+
- `REST_SERVICE_BIND` (default: `0.0.0.0`)
|
29
|
+
- `BASIC_AUTH_USER` (default: `admin`)
|
30
|
+
- `BASIC_AUTH_PASS` (default: `admin`)
|
31
|
+
|
32
|
+
The REST-service uses an basic-authentication for the first security step.
|
33
|
+
The second Step is an configured API user into the Icinga2-Master.
|
34
|
+
The API user credentials must be set as HTTP-Header vars (see the examples below).
|
35
|
+
|
36
|
+
To overwrite the default configuration for the REST-Service, put a `icinga2-cert-service.yaml` into `/etc` :
|
37
|
+
|
38
|
+
```yaml
|
39
|
+
---
|
40
|
+
icinga:
|
41
|
+
server: master-server
|
42
|
+
api:
|
43
|
+
port: 5665
|
44
|
+
user: root
|
45
|
+
password: icinga
|
46
|
+
|
47
|
+
rest-service:
|
48
|
+
port: 8080
|
49
|
+
bind: 192.168.10.10
|
50
|
+
|
51
|
+
basic-auth:
|
52
|
+
user: ba-user
|
53
|
+
password: v2rys3cr3t
|
54
|
+
```
|
55
|
+
|
56
|
+
|
57
|
+
# Who to used it
|
58
|
+
|
59
|
+
## With Icinga2 Version 2.8, we can use the new PKI-Proxy Mode
|
60
|
+
|
61
|
+
You can use `expect` on a *satellite* or *agent* to create an certificate request with the *icinga2 node wizard*.
|
62
|
+
(A complete `expect` example can be found below)
|
63
|
+
|
64
|
+
```bash
|
65
|
+
expect /init/node-wizard.expect
|
66
|
+
```
|
67
|
+
|
68
|
+
After this, you can use the *cert-service* to sign this request:
|
69
|
+
|
70
|
+
```bash
|
71
|
+
curl \
|
72
|
+
--user ${CERTIFICATE_SERVICE_BA_USER}:${CERTIFICATE_SERVICE_BA_PASSWORD} \
|
73
|
+
--silent \
|
74
|
+
--request GET \
|
75
|
+
--header "X-API-USER: ${CERTIFICATE_SERVICE_API_USER}" \
|
76
|
+
--header "X-API-PASSWORD: ${CERTIFICATE_SERVICE_API_PASSWORD}" \
|
77
|
+
--write-out "%{http_code}\n" \
|
78
|
+
--output /tmp/sign_${HOSTNAME}.json \
|
79
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/sign/${HOSTNAME}
|
80
|
+
```
|
81
|
+
|
82
|
+
## Otherwise, the pre 2.8 Mode works well
|
83
|
+
|
84
|
+
To create a certificate:
|
85
|
+
|
86
|
+
```bash
|
87
|
+
curl \
|
88
|
+
--request GET \
|
89
|
+
--user ${CERTIFICATE_SERVICE_BA_USER}:${CERTIFICATE_SERVICE_BA_PASSWORD} \
|
90
|
+
--silent \
|
91
|
+
--header "X-API-USER: ${CERTIFICATE_SERVICE_API_USER}" \
|
92
|
+
--header "X-API-KEY: ${CERTIFICATE_SERVICE_API_PASSWORD}" \
|
93
|
+
--output /tmp/request_${HOSTNAME}.json \
|
94
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/request/${HOSTNAME}
|
95
|
+
```
|
96
|
+
|
97
|
+
this creates an output file, that we use to download the certificate.
|
98
|
+
|
99
|
+
## Download the created certificate:
|
100
|
+
|
101
|
+
```bash
|
102
|
+
checksum=$(jq --raw-output .checksum /tmp/request_${HOSTNAME}.json)
|
103
|
+
master_name=$(jq --raw-output .master_name /tmp/request_${HOSTNAME}.json)
|
104
|
+
|
105
|
+
curl \
|
106
|
+
--request GET \
|
107
|
+
--user ${CERTIFICATE_SERVICE_BA_USER}:${CERTIFICATE_SERVICE_BA_PASSWORD} \
|
108
|
+
--silent \
|
109
|
+
--header "X-API-USER: ${CERTIFICATE_SERVICE_API_USER}" \
|
110
|
+
--header "X-API-KEY: ${CERTIFICATE_SERVICE_API_PASSWORD}" \
|
111
|
+
--header "X-CHECKSUM: ${checksum}" \
|
112
|
+
--output ${WORK_DIR}/pki/${HOSTNAME}/${HOSTNAME}.tgz \
|
113
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/cert/${HOSTNAME}
|
114
|
+
```
|
115
|
+
|
116
|
+
## Create the Satellite `Endpoint`
|
117
|
+
|
118
|
+
```bash
|
119
|
+
cat << EOF > /etc/icinga2/zones.conf
|
120
|
+
|
121
|
+
/* the following line specifies that the client connects to the master and not vice versa */
|
122
|
+
object Endpoint "${master_name}" { host = "${ICINGA_MASTER}"; port = "5665" }
|
123
|
+
object Zone "master" { endpoints = [ "${master_name}" ] }
|
124
|
+
|
125
|
+
object Endpoint NodeName {}
|
126
|
+
object Zone ZoneName { endpoints = [ NodeName ] ; parent = "master" }
|
127
|
+
|
128
|
+
object Zone "global-templates" { global = true }
|
129
|
+
object Zone "director-global" { global = true }
|
130
|
+
|
131
|
+
EOF
|
132
|
+
```
|
133
|
+
|
134
|
+
## NOTE
|
135
|
+
The generated certificate has an timeout from 10 minutes between beginning of creation and download.
|
136
|
+
|
137
|
+
|
138
|
+
# API
|
139
|
+
|
140
|
+
following API Calls are implemented:
|
141
|
+
|
142
|
+
## Health Check
|
143
|
+
|
144
|
+
The Health Check is important to determine whether the certificate service has started.
|
145
|
+
|
146
|
+
```bash
|
147
|
+
curl \
|
148
|
+
--request GET \
|
149
|
+
--silent \
|
150
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/health-check
|
151
|
+
```
|
152
|
+
|
153
|
+
The health check returns only a string with `healthy` as content.
|
154
|
+
|
155
|
+
## Icinga Version
|
156
|
+
|
157
|
+
Returns the Icinga Version
|
158
|
+
|
159
|
+
```bash
|
160
|
+
curl \
|
161
|
+
--request GET \
|
162
|
+
--silent \
|
163
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/icinga-version
|
164
|
+
```
|
165
|
+
|
166
|
+
The icinga version call returns only a string with the shortend version as content: `2.8`
|
167
|
+
|
168
|
+
## create a certificate request
|
169
|
+
|
170
|
+
Create an Certificate request
|
171
|
+
|
172
|
+
```bash
|
173
|
+
curl \
|
174
|
+
--user ${CERTIFICATE_SERVICE_BA_USER}:${CERTIFICATE_SERVICE_BA_PASSWORD} \
|
175
|
+
--request GET \
|
176
|
+
--header "X-API-USER: cert-service" \
|
177
|
+
--header "X-API-KEY: knockknock" \
|
178
|
+
--output /tmp/request_${HOSTNAME}.json \
|
179
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/request/${HOSTNAME}
|
180
|
+
```
|
181
|
+
|
182
|
+
## download an certificate
|
183
|
+
|
184
|
+
After an certificate request, you can download the created certificate:
|
185
|
+
|
186
|
+
```bash
|
187
|
+
checksum=$(jq --raw-output .checksum /tmp/request_${HOSTNAME}.json)
|
188
|
+
|
189
|
+
curl \
|
190
|
+
--user ${CERTIFICATE_SERVICE_BA_USER}:${CERTIFICATE_SERVICE_BA_PASSWORD} \
|
191
|
+
--request GET \
|
192
|
+
--header "X-API-USER: cert-service" \
|
193
|
+
--header "X-API-KEY: knockknock" \
|
194
|
+
--header "X-CHECKSUM: ${checksum}" \
|
195
|
+
--output /tmp/cert_${HOSTNAME}.tgz \
|
196
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/cert/${HOSTNAME}
|
197
|
+
```
|
198
|
+
|
199
|
+
## validate the satellite CA
|
200
|
+
|
201
|
+
If the CA has been renewed on the master, all satellites or agents will no longer be able to connect to the master.
|
202
|
+
To be able to detect this possibility, you can create a checksum of the `ca.crt` file and have it checked by the certificats service.
|
203
|
+
|
204
|
+
The following algorithms are supported to create a checksum:
|
205
|
+
- `md5`
|
206
|
+
- `sha256`
|
207
|
+
- `sha384`
|
208
|
+
- `sha512`
|
209
|
+
|
210
|
+
```bash
|
211
|
+
checksum=$(sha256sum ${ICINGA_CERT_DIR}/ca.crt | cut -f 1 -d ' ')
|
212
|
+
|
213
|
+
curl \
|
214
|
+
--user ${CERTIFICATE_SERVICE_BA_USER}:${CERTIFICATE_SERVICE_BA_PASSWORD} \
|
215
|
+
--request GET \
|
216
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/validate/${checksum}
|
217
|
+
```
|
218
|
+
|
219
|
+
## sign a certificate request
|
220
|
+
|
221
|
+
Version 2.8 of Icinga2 came with a CA proxy.
|
222
|
+
Here you can use the well-known `node wizard` to create a certificate request on a satellite or agent.
|
223
|
+
This certificate only has to be confirmed at the Icinga2 Master.
|
224
|
+
|
225
|
+
The certificate files are then replicated to the respective applicant.
|
226
|
+
|
227
|
+
With the following API call you can confirm the certificate without being logged on to the master.
|
228
|
+
|
229
|
+
```bash
|
230
|
+
curl \
|
231
|
+
--user ${CERTIFICATE_SERVICE_BA_USER}:${CERTIFICATE_SERVICE_BA_PASSWORD} \
|
232
|
+
--request POST \
|
233
|
+
--header "X-API-USER: cert-service" \
|
234
|
+
--header "X-API-KEY: knockknock" \
|
235
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/sign/${HOSTNAME}
|
236
|
+
```
|
237
|
+
|
238
|
+
## download an generic script for combine the latest 3 steps
|
239
|
+
|
240
|
+
For an own service, you can download an generic script, thats compine the lates 3 steps.
|
241
|
+
|
242
|
+
|
243
|
+
|
244
|
+
|
245
|
+
```bash
|
246
|
+
curl \
|
247
|
+
--user ${CERTIFICATE_SERVICE_BA_USER}:${CERTIFICATE_SERVICE_BA_PASSWORD} \
|
248
|
+
http://${CERTIFICATE_SERVICE_SERVER}:${CERTIFICATE_SERVICE_PORT}/v2/download/icinga2_certificates.sh
|
249
|
+
```
|
250
|
+
|
251
|
+
```bash
|
252
|
+
./icinga2_certificates.sh --help
|
253
|
+
|
254
|
+
Download a script to handle icinga2 certificates
|
255
|
+
|
256
|
+
Version 0.8.0 (05.02.2018)
|
257
|
+
|
258
|
+
Usage: icinga2_certificates [-h] [-v] ...
|
259
|
+
-h : Show this help
|
260
|
+
-v : Prints out the Version
|
261
|
+
--ba-user : Basic Auth User for the certificate Service. Also set as ENVIRONMENT variable BA_USER
|
262
|
+
--ba-password : Basic AUth Password for the certificate Service. Also set as ENVIRONMENT variable BA_PASSWORD
|
263
|
+
--api-user : Icinga2 API User. Also set as ENVIRONMENT variable API_USER
|
264
|
+
--api-password : Icinga2 API Password. Also set as ENVIRONMENT variable API_PASSWORD
|
265
|
+
-I|--icinga2-master : the Icinga2 Master himself. Also set as ENVIRONMENT variable ICINGA2_MASTER
|
266
|
+
-P|--icinga2-port : the Icinga2 API Port (default: 5665). Also set as ENVIRONMENT variable ICINGA2_API_PORT
|
267
|
+
-c|--certificate-server : the certificate server. Also set as ENVIRONMENT variable CERTIFICATE_SERVER
|
268
|
+
-p|--certifiacte-port : the port for the certificate service (default: 8080). Also set as ENVIRONMENT variable CERTIFICATE_PORT
|
269
|
+
-a|--certifiacte-path : the url path for the certifiacte service (default: /). Also set as ENVIRONMENT variable CERTIFICATE_PATH
|
270
|
+
-d|--destination : the local destination directory for storing certificate files (default: .) Also set as ENVIRONMENT variable DESTINATION_DIR
|
271
|
+
-r|--retry : how often are the backendservices attempted to reach you. Also set as ENVIRONMENT variable RETRY
|
272
|
+
-s|--sleep-for-restart : seconds before the Icinga2 Master is restarted. Also set as ENVIRONMENT variable SLEEP_FOR_RESTART
|
273
|
+
this is needed to activate the certificate and the generated configuration
|
274
|
+
|
275
|
+
Examples
|
276
|
+
icinga2_certificates.sh --icinga2-master localhost --api-user root --api-password icinga --certificate-server localhost
|
277
|
+
```
|
278
|
+
|
279
|
+
---
|
280
|
+
|
281
|
+
|
282
|
+
The `node wizard` can also be automated (via `expect`):
|
283
|
+
|
284
|
+
```
|
285
|
+
cat << EOF >> ~/node-wizard.expect
|
286
|
+
|
287
|
+
#!/usr/bin/expect
|
288
|
+
|
289
|
+
# exp_internal 1
|
290
|
+
|
291
|
+
log_user 1
|
292
|
+
set timeout 3
|
293
|
+
|
294
|
+
spawn icinga2 node wizard
|
295
|
+
|
296
|
+
expect -re "Please specify if this is a satellite/client setup" {
|
297
|
+
send -- "y\r"
|
298
|
+
}
|
299
|
+
expect -re "Please specify the common name " {
|
300
|
+
send -- "[exec hostname -f]\r"
|
301
|
+
}
|
302
|
+
expect -re "Master/Satellite Common Name" {
|
303
|
+
send -- "$env(ICINGA_MASTER)\r"
|
304
|
+
}
|
305
|
+
expect -re "Do you want to establish a connection to the parent node" {
|
306
|
+
send -- "y\r"
|
307
|
+
}
|
308
|
+
expect -re "endpoint host" {
|
309
|
+
send -- "$env(ICINGA_MASTER)\r"
|
310
|
+
}
|
311
|
+
expect -re "endpoint port" {
|
312
|
+
send -- "5665\r"
|
313
|
+
}
|
314
|
+
expect -re "Add more master/satellite endpoints" {
|
315
|
+
send -- "n\r"
|
316
|
+
}
|
317
|
+
expect -re "Is this information correct" {
|
318
|
+
send -- "y\r"
|
319
|
+
}
|
320
|
+
expect -re "Please specify the request ticket generated on your Icinga 2 master" {
|
321
|
+
send -- "\r"
|
322
|
+
}
|
323
|
+
expect -re "Bind Host" {
|
324
|
+
send -- "\r"
|
325
|
+
}
|
326
|
+
expect -re "Bind Port" {
|
327
|
+
send -- "\r"
|
328
|
+
}
|
329
|
+
expect -re "config from parent node" {
|
330
|
+
send -- "y\r"
|
331
|
+
}
|
332
|
+
expect -re "commands from parent node" {
|
333
|
+
send -- "y\r"
|
334
|
+
}
|
335
|
+
|
336
|
+
interact
|
337
|
+
|
338
|
+
EOF
|
339
|
+
|
340
|
+
|
341
|
+
expect ~/node-wizard.expect 1> /dev/null
|
342
|
+
```
|
343
|
+
|
344
|
+
|
@@ -0,0 +1,278 @@
|
|
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
|
+
module Sinatra
|
23
|
+
class CertServiceRest < Base
|
24
|
+
register Sinatra::BasicAuth
|
25
|
+
|
26
|
+
include Logging
|
27
|
+
|
28
|
+
@icinga_master = ENV.fetch('ICINGA2_MASTER' , nil)
|
29
|
+
@icinga_api_port = ENV.fetch('ICINGA2_API_PORT' , 5665 )
|
30
|
+
@icinga_api_user = ENV.fetch('ICINGA2_API_USER' , 'root' )
|
31
|
+
@icinga_api_password = ENV.fetch('ICINGA2_API_PASSWORD', 'icinga' )
|
32
|
+
@rest_service_port = ENV.fetch('REST_SERVICE_PORT' , 8080 )
|
33
|
+
@rest_service_bind = ENV.fetch('REST_SERVICE_BIND' , '0.0.0.0' )
|
34
|
+
@basic_auth_user = ENV.fetch('BASIC_AUTH_USER' , 'admin')
|
35
|
+
@basic_auth_pass = ENV.fetch('BASIC_AUTH_PASS' , 'admin')
|
36
|
+
|
37
|
+
config_file = ENV.fetch('CONFIG_FILE' , '/etc/icinga2-cert-service.yaml')
|
38
|
+
|
39
|
+
configure do
|
40
|
+
set :environment, :production
|
41
|
+
|
42
|
+
# default configuration
|
43
|
+
@rest_service_port = 8080
|
44
|
+
@rest_service_bind = '0.0.0.0'
|
45
|
+
|
46
|
+
if( File.exist?('/etc/rest-service.yaml') )
|
47
|
+
puts ' WARNING: The configuration file \'/etc/rest-service.yaml\' is OBSOLETE'
|
48
|
+
puts ' Please use \'/etc/icinga2-cert-service.yaml\' or use the environment variable \'CONFIG_FILE\''
|
49
|
+
end
|
50
|
+
|
51
|
+
if(File.exist?(config_file) && File.exist?('/etc/rest-service.yaml'))
|
52
|
+
puts ' WARNING: The configuration file \'/etc/rest-service.yaml\' is OBSOLETE'
|
53
|
+
puts ' for compatibility reasons this is used anyway.'
|
54
|
+
|
55
|
+
config_file = '/etc/rest-service.yaml'
|
56
|
+
end
|
57
|
+
|
58
|
+
if( File.exist?(config_file) )
|
59
|
+
|
60
|
+
begin
|
61
|
+
config = YAML.load_file(config_file)
|
62
|
+
|
63
|
+
@icinga_master = config.dig('icinga', 'server')
|
64
|
+
@icinga_api_port = config.dig('icinga', 'api', 'port') || 5665
|
65
|
+
@icinga_api_user = config.dig('icinga', 'api', 'user') || 5665
|
66
|
+
@icinga_api_password = config.dig('icinga', 'api', 'password') || 5665
|
67
|
+
@rest_service_port = config.dig('rest-service', 'port') || 8080
|
68
|
+
@rest_service_bind = config.dig('rest-service', 'bind') || '0.0.0.0'
|
69
|
+
@basic_auth_user = config.dig('basic-auth', 'user') || 'admin'
|
70
|
+
@basic_auth_pass = config.dig('basic-auth', 'password') || 'admin'
|
71
|
+
rescue => error
|
72
|
+
puts error
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
set :logging, true
|
78
|
+
set :app_file, caller_files.first || $PROGRAM_NAME
|
79
|
+
set :run, proc { $PROGRAM_NAME == app_file }
|
80
|
+
set :dump_errors, true
|
81
|
+
set :show_exceptions, true
|
82
|
+
set :public_folder, '/var/www/'
|
83
|
+
|
84
|
+
set :bind, @rest_service_bind
|
85
|
+
set :port, @rest_service_port.to_i
|
86
|
+
|
87
|
+
# -----------------------------------------------------------------------------
|
88
|
+
|
89
|
+
error do
|
90
|
+
msg = "ERROR\n\nThe cert-rest-service has nasty error - " + env['sinatra.error']
|
91
|
+
|
92
|
+
msg.message
|
93
|
+
end
|
94
|
+
|
95
|
+
# -----------------------------------------------------------------------------
|
96
|
+
|
97
|
+
before do
|
98
|
+
content_type :json
|
99
|
+
end
|
100
|
+
|
101
|
+
before '/v2/*/:host' do
|
102
|
+
request.body.rewind
|
103
|
+
@request_paylod = request.body.read
|
104
|
+
end
|
105
|
+
|
106
|
+
# -----------------------------------------------------------------------------
|
107
|
+
|
108
|
+
# configure Basic Auth
|
109
|
+
authorize 'API' do |username, password|
|
110
|
+
username == @basic_auth_user && password == @basic_auth_pass
|
111
|
+
end
|
112
|
+
|
113
|
+
# -----------------------------------------------------------------------------
|
114
|
+
|
115
|
+
config = {
|
116
|
+
icinga: {
|
117
|
+
server: @icinga_master,
|
118
|
+
api: {
|
119
|
+
port: @icinga_api_port,
|
120
|
+
user: @icinga_api_user,
|
121
|
+
password: @icinga_api_password,
|
122
|
+
pki_path: @icinga_api_pki_path,
|
123
|
+
node_name: @icinga_api_node_name
|
124
|
+
}
|
125
|
+
}
|
126
|
+
}
|
127
|
+
|
128
|
+
ics = IcingaCertService::Client.new(config)
|
129
|
+
|
130
|
+
# Health Check
|
131
|
+
#
|
132
|
+
get '/v2/health-check' do
|
133
|
+
status 200
|
134
|
+
'healthy'
|
135
|
+
end
|
136
|
+
|
137
|
+
# return the icinga2 version
|
138
|
+
#
|
139
|
+
get '/v2/icinga-version' do
|
140
|
+
status 200
|
141
|
+
result = ics.icinga_version
|
142
|
+
result + "\n"
|
143
|
+
end
|
144
|
+
|
145
|
+
|
146
|
+
# create a certificate request
|
147
|
+
#
|
148
|
+
protect 'API' do
|
149
|
+
get '/v2/request/:host' do
|
150
|
+
result = ics.create_certificate(host: params[:host], request: request.env)
|
151
|
+
result_status = result.dig(:status).to_i
|
152
|
+
|
153
|
+
status result_status
|
154
|
+
|
155
|
+
JSON.pretty_generate(result) + "\n"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# validate the satellite CA
|
160
|
+
#
|
161
|
+
protect 'API' do
|
162
|
+
get '/v2/validate/:checksum' do
|
163
|
+
result = ics.validate_certificate(checksum: params[:checksum])
|
164
|
+
result_status = result.dig(:status).to_i
|
165
|
+
|
166
|
+
status result_status
|
167
|
+
content_type :json
|
168
|
+
JSON.pretty_generate(result) + "\n"
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
|
173
|
+
# download an certificate
|
174
|
+
#
|
175
|
+
protect 'API' do
|
176
|
+
get '/v2/cert/:host' do
|
177
|
+
result = ics.check_certificate( host: params[:host], request: request.env )
|
178
|
+
|
179
|
+
logger.debug(result)
|
180
|
+
|
181
|
+
result_status = result.dig(:status).to_i
|
182
|
+
|
183
|
+
if result_status == 200
|
184
|
+
|
185
|
+
path = result.dig(:path)
|
186
|
+
file_name = result.dig(:file_name)
|
187
|
+
|
188
|
+
status result_status
|
189
|
+
|
190
|
+
send_file(format('%s/%s', path, file_name), filename: file_name, type: 'Application/octet-stream')
|
191
|
+
else
|
192
|
+
|
193
|
+
status result_status
|
194
|
+
|
195
|
+
JSON.pretty_generate(result) + "\n"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
# download the master ca.crt
|
201
|
+
#
|
202
|
+
protect 'API' do
|
203
|
+
get '/v2/master-ca' do
|
204
|
+
|
205
|
+
path = '/var/lib/icinga2/certs'
|
206
|
+
file_name = 'ca.crt'
|
207
|
+
if( File.exist?(format('%s/%s', path, file_name) ) )
|
208
|
+
status 200
|
209
|
+
send_file(format('%s/%s', path, file_name), filename: file_name, type: 'Application/octet-stream')
|
210
|
+
else
|
211
|
+
|
212
|
+
status 404
|
213
|
+
|
214
|
+
JSON.pretty_generate('no ca file found') + "\n"
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# sign a certificate request
|
220
|
+
#
|
221
|
+
protect 'API' do
|
222
|
+
get '/v2/sign/:host' do
|
223
|
+
status 200
|
224
|
+
|
225
|
+
result = ics.sign_certificate(host: params[:host], request: request.env)
|
226
|
+
|
227
|
+
JSON.pretty_generate(result) + "\n"
|
228
|
+
end
|
229
|
+
end
|
230
|
+
|
231
|
+
# download a generic scrip to handle simple certificate requests against
|
232
|
+
# ths service
|
233
|
+
# this script is needed by dashing and other clients
|
234
|
+
#
|
235
|
+
protect 'API' do
|
236
|
+
get '/v2/download/:file_name' do
|
237
|
+
status 200
|
238
|
+
|
239
|
+
result = ics.download(file_name: params[:file_name], request: request.env)
|
240
|
+
|
241
|
+
logger.debug(result)
|
242
|
+
|
243
|
+
result_status = result.dig(:status).to_i
|
244
|
+
|
245
|
+
if result_status == 200
|
246
|
+
|
247
|
+
path = result.dig(:path)
|
248
|
+
file_name = result.dig(:file_name)
|
249
|
+
|
250
|
+
status result_status
|
251
|
+
|
252
|
+
send_file(format('%s/%s', path, file_name), filename: file_name, type: 'Application/octet-stream')
|
253
|
+
else
|
254
|
+
|
255
|
+
status result_status
|
256
|
+
|
257
|
+
JSON.pretty_generate(result) + "\n"
|
258
|
+
end
|
259
|
+
end
|
260
|
+
end
|
261
|
+
|
262
|
+
|
263
|
+
not_found do
|
264
|
+
jj = {
|
265
|
+
'meta' => {
|
266
|
+
'code' => 404,
|
267
|
+
'message' => 'Request not found.'
|
268
|
+
}
|
269
|
+
}
|
270
|
+
content_type :json
|
271
|
+
JSON.pretty_generate(jj)
|
272
|
+
end
|
273
|
+
|
274
|
+
# -----------------------------------------------------------------------------
|
275
|
+
run! if app_file == $PROGRAM_NAME
|
276
|
+
# -----------------------------------------------------------------------------
|
277
|
+
end
|
278
|
+
end
|