manageiq-appliance_console 5.5.0 → 7.0.1

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.
Files changed (34) hide show
  1. checksums.yaml +4 -4
  2. data/.codeclimate.yml +24 -25
  3. data/.rspec_ci +2 -0
  4. data/.rubocop.yml +3 -3
  5. data/.rubocop_cc.yml +3 -4
  6. data/.rubocop_local.yml +1 -1
  7. data/.travis.yml +4 -3
  8. data/Gemfile +1 -3
  9. data/README.md +1 -2
  10. data/Rakefile +20 -1
  11. data/bin/appliance_console +30 -6
  12. data/lib/manageiq/appliance_console/certificate_authority.rb +1 -1
  13. data/lib/manageiq/appliance_console/cli.rb +166 -70
  14. data/lib/manageiq/appliance_console/database_admin.rb +35 -206
  15. data/lib/manageiq/appliance_console/database_configuration.rb +10 -2
  16. data/lib/manageiq/appliance_console/database_replication.rb +1 -1
  17. data/lib/manageiq/appliance_console/database_replication_standby.rb +1 -1
  18. data/lib/manageiq/appliance_console/external_auth_options.rb +3 -13
  19. data/lib/manageiq/appliance_console/internal_database_configuration.rb +4 -12
  20. data/lib/manageiq/appliance_console/key_configuration.rb +8 -1
  21. data/lib/manageiq/appliance_console/logfile_configuration.rb +2 -2
  22. data/lib/manageiq/appliance_console/manageiq_user_mixin.rb +15 -0
  23. data/lib/manageiq/appliance_console/message_configuration.rb +205 -0
  24. data/lib/manageiq/appliance_console/message_configuration_client.rb +98 -0
  25. data/lib/manageiq/appliance_console/message_configuration_server.rb +321 -0
  26. data/lib/manageiq/appliance_console/oidc_authentication.rb +27 -1
  27. data/lib/manageiq/appliance_console/postgres_admin.rb +412 -0
  28. data/lib/manageiq/appliance_console/utilities.rb +61 -2
  29. data/lib/manageiq/appliance_console/version.rb +1 -1
  30. data/lib/manageiq-appliance_console.rb +2 -6
  31. data/locales/appliance/en.yml +0 -16
  32. data/manageiq-appliance_console.gemspec +4 -3
  33. metadata +54 -24
  34. data/lib/manageiq/appliance_console/messaging_configuration.rb +0 -92
@@ -4,6 +4,8 @@ require 'net/scp'
4
4
  require 'active_support/all'
5
5
  require 'manageiq-password'
6
6
 
7
+ require_relative './manageiq_user_mixin'
8
+
7
9
  module ManageIQ
8
10
  module ApplianceConsole
9
11
  CERT_DIR = ENV['KEY_ROOT'] || ManageIQ::ApplianceConsole::RAILS_ROOT.join("certs")
@@ -11,6 +13,8 @@ module ApplianceConsole
11
13
  NEW_KEY_FILE = "#{KEY_FILE}.tmp".freeze
12
14
 
13
15
  class KeyConfiguration
16
+ include ManageIQ::ApplianceConsole::ManageiqUserMixin
17
+
14
18
  attr_accessor :host, :login, :password, :key_path, :action, :force
15
19
 
16
20
  def initialize(options = {})
@@ -89,7 +93,9 @@ module ApplianceConsole
89
93
  end
90
94
 
91
95
  def create_key
92
- ManageIQ::Password.generate_symmetric(NEW_KEY_FILE) && true
96
+ return unless !!ManageIQ::Password.generate_symmetric(NEW_KEY_FILE)
97
+
98
+ File.chown(manageiq_uid, manageiq_gid, NEW_KEY_FILE)
93
99
  end
94
100
 
95
101
  def fetch_key
@@ -97,6 +103,7 @@ module ApplianceConsole
97
103
  Net::SCP.start(host, login, :password => password) do |scp|
98
104
  scp.download!(key_path, NEW_KEY_FILE)
99
105
  end
106
+ File.chown(manageiq_uid, manageiq_gid, NEW_KEY_FILE)
100
107
  File.exist?(NEW_KEY_FILE)
101
108
  rescue => e
102
109
  say("Failed to fetch key: #{e.message}")
@@ -1,7 +1,7 @@
1
+ require "manageiq/appliance_console/utilities"
1
2
  require 'linux_admin'
2
3
  require 'pathname'
3
4
  require 'fileutils'
4
- require 'util/miq-system.rb'
5
5
 
6
6
  module ManageIQ
7
7
  module ApplianceConsole
@@ -18,7 +18,7 @@ module ApplianceConsole
18
18
  self.disk = config[:disk]
19
19
  self.new_logrotate_count = nil
20
20
 
21
- self.size = MiqSystem.disk_usage(LOGFILE_DIRECTORY)[0][:total_bytes]
21
+ self.size = Utilities.disk_usage(LOGFILE_DIRECTORY)[0][:total_bytes]
22
22
  self.current_logrotate_count = /rotate\s+(\d+)/.match(File.read(MIQ_LOGS_CONF))[1]
23
23
  self.evm_was_running = LinuxAdmin::Service.new("evmserverd").running?
24
24
  end
@@ -0,0 +1,15 @@
1
+ module ManageIQ
2
+ module ApplianceConsole
3
+ module ManageiqUserMixin
4
+ extend ActiveSupport::Concern
5
+
6
+ def manageiq_uid
7
+ @manageiq_uid ||= Process::UID.from_name("manageiq")
8
+ end
9
+
10
+ def manageiq_gid
11
+ @manageiq_gid ||= Process::GID.from_name("manageiq")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,205 @@
1
+ require 'active_support/core_ext/module/delegation'
2
+ require 'pathname'
3
+
4
+ require_relative './manageiq_user_mixin'
5
+
6
+ module ManageIQ
7
+ module ApplianceConsole
8
+ class MessageConfiguration
9
+ include ManageIQ::ApplianceConsole::ManageiqUserMixin
10
+
11
+ attr_reader :message_keystore_username, :message_keystore_password,
12
+ :message_server_host, :message_server_port,
13
+ :miq_config_dir_path, :config_dir_path, :sample_config_dir_path,
14
+ :client_properties_path,
15
+ :keystore_dir_path, :truststore_path, :keystore_path,
16
+ :messaging_yaml_sample_path, :messaging_yaml_path,
17
+ :ca_cert_path
18
+
19
+ BASE_DIR = "/opt/kafka".freeze
20
+ LOGS_DIR = "#{BASE_DIR}/logs".freeze
21
+ CONFIG_DIR = "#{BASE_DIR}/config".freeze
22
+ SAMPLE_CONFIG_DIR = "#{BASE_DIR}/config-sample".freeze
23
+ MIQ_CONFIG_DIR = ManageIQ::ApplianceConsole::RAILS_ROOT.join("config").freeze
24
+
25
+ def initialize(options = {})
26
+ @message_server_port = options[:message_server_port] || 9093
27
+ @message_keystore_username = options[:message_keystore_username] || "admin"
28
+ @message_keystore_password = options[:message_keystore_password]
29
+
30
+ @miq_config_dir_path = Pathname.new(MIQ_CONFIG_DIR)
31
+ @config_dir_path = Pathname.new(CONFIG_DIR)
32
+ @sample_config_dir_path = Pathname.new(SAMPLE_CONFIG_DIR)
33
+
34
+ @client_properties_path = config_dir_path.join("client.properties")
35
+ @keystore_dir_path = config_dir_path.join("keystore")
36
+ @truststore_path = keystore_dir_path.join("truststore.jks")
37
+ @keystore_path = keystore_dir_path.join("keystore.jks")
38
+
39
+ @messaging_yaml_sample_path = miq_config_dir_path.join("messaging.kafka.yml")
40
+ @messaging_yaml_path = miq_config_dir_path.join("messaging.yml")
41
+ @ca_cert_path = keystore_dir_path.join("ca-cert")
42
+ end
43
+
44
+ def already_configured?
45
+ installed_file_found = false
46
+ installed_files.each do |f|
47
+ if File.exist?(f)
48
+ installed_file_found = true
49
+ say("Installed file #{f} found.")
50
+ end
51
+ end
52
+ installed_file_found
53
+ end
54
+
55
+ def ask_questions
56
+ return false unless valid_environment?
57
+
58
+ ask_for_parameters
59
+ show_parameters
60
+ return false unless agree("\nProceed? (Y/N): ")
61
+
62
+ return false unless host_reachable?(message_server_host, "Message Server Host:")
63
+
64
+ true
65
+ end
66
+
67
+ def create_client_properties
68
+ say(__method__.to_s.tr("_", " ").titleize)
69
+
70
+ return if file_found?(client_properties_path)
71
+
72
+ algorithm = message_server_host.ipaddress? ? "" : "HTTPS"
73
+ protocol = secure? ? "SASL_SSL" : "PLAINTEXT"
74
+ content = secure? ? secure_client_properties_content(algorithm, protocol) : unsecure_client_properties_content(algorithm, protocol)
75
+
76
+ File.write(client_properties_path, content)
77
+ end
78
+
79
+ def secure_client_properties_content(algorithm, protocol)
80
+ secure_content = <<~CLIENT_PROPERTIES
81
+ ssl.truststore.location=#{truststore_path}
82
+ ssl.truststore.password=#{message_keystore_password}
83
+ CLIENT_PROPERTIES
84
+
85
+ unsecure_client_properties_content(algorithm, protocol) + secure_content
86
+ end
87
+
88
+ def unsecure_client_properties_content(algorithm, protocol)
89
+ <<~CLIENT_PROPERTIES
90
+ ssl.endpoint.identification.algorithm=#{algorithm}
91
+
92
+ sasl.mechanism=PLAIN
93
+ security.protocol=#{protocol}
94
+ sasl.jaas.config=org.apache.kafka.common.security.plain.PlainLoginModule required \\
95
+ username=#{message_keystore_username} \\
96
+ password=#{message_keystore_password} ;
97
+ CLIENT_PROPERTIES
98
+ end
99
+
100
+ def configure_messaging_yaml
101
+ say(__method__.to_s.tr("_", " ").titleize)
102
+
103
+ return if file_found?(messaging_yaml_path)
104
+
105
+ messaging_yaml = YAML.load_file(messaging_yaml_sample_path)
106
+
107
+ messaging_yaml["production"].delete("username")
108
+ messaging_yaml["production"].delete("password")
109
+
110
+ messaging_yaml["production"]["hostname"] = message_server_host
111
+ messaging_yaml["production"]["port"] = message_server_port
112
+ messaging_yaml["production"]["sasl.mechanism"] = "PLAIN"
113
+ messaging_yaml["production"]["sasl.username"] = message_keystore_username
114
+ messaging_yaml["production"]["sasl.password"] = ManageIQ::Password.try_encrypt(message_keystore_password)
115
+
116
+ if secure?
117
+ messaging_yaml["production"]["security.protocol"] = "SASL_SSL"
118
+ messaging_yaml["production"]["ssl.ca.location"] = ca_cert_path.to_path
119
+ else
120
+ messaging_yaml["production"]["security.protocol"] = "PLAINTEXT"
121
+ end
122
+
123
+ File.open(messaging_yaml_path, "w") do |f|
124
+ f.write(messaging_yaml.to_yaml)
125
+ f.chown(manageiq_uid, manageiq_gid)
126
+ end
127
+ end
128
+
129
+ def remove_installed_files
130
+ say(__method__.to_s.tr("_", " ").titleize)
131
+
132
+ installed_files.each { |f| FileUtils.rm_rf(f) }
133
+ end
134
+
135
+ def valid_environment?
136
+ if already_configured?
137
+ unconfigure if agree("\nAlready configured on this Appliance, Un-Configure first? (Y/N): ")
138
+ return false unless agree("\nProceed with Configuration? (Y/N): ")
139
+ end
140
+ true
141
+ end
142
+
143
+ def file_found?(path)
144
+ return false unless File.exist?(path)
145
+
146
+ say("\tWARNING: #{path} already exists. Taking no action.")
147
+ true
148
+ end
149
+
150
+ def files_found?(path_list)
151
+ return false unless path_list.all? { |path| File.exist?(path) }
152
+
153
+ path_list.each { |path| file_found?(path) }
154
+ true
155
+ end
156
+
157
+ def file_contains?(path, content)
158
+ return false unless File.exist?(path)
159
+
160
+ content.split("\n").each do |l|
161
+ l.gsub!("/", "\\/")
162
+ l.gsub!(/password=.*$/, "password=") # Remove the password as it can have special characters that grep can not match.
163
+ return false unless File.foreach(path).grep(/#{l}/).any?
164
+ end
165
+
166
+ say("Content already exists in #{path}. Taking no action.")
167
+ true
168
+ end
169
+
170
+ def host_reachable?(host, what)
171
+ require 'net/ping'
172
+ say("Checking connectivity to #{host} ... ")
173
+ unless Net::Ping::External.new(host).ping
174
+ say("Failed.\nCould not connect to #{host},")
175
+ say("the #{what} must be reachable by name.")
176
+ return false
177
+ end
178
+ say("Succeeded.")
179
+ true
180
+ end
181
+
182
+ def configure_messaging_type(value)
183
+ say(__method__.to_s.tr("_", " ").titleize)
184
+
185
+ ManageIQ::ApplianceConsole::Utilities.rake_run!("evm:settings:set", ["/prototype/messaging_type=#{value}"])
186
+ end
187
+
188
+ def restart_evmserverd
189
+ say("Restart evmserverd if it is running...")
190
+ evmserverd_service = LinuxAdmin::Service.new("evmserverd")
191
+ evmserverd_service.restart if evmserverd_service.running?
192
+ end
193
+
194
+ def unconfigure
195
+ configure_messaging_type("miq_queue") # Settings.prototype.messaging_type = 'miq_queue'
196
+ restart_evmserverd
197
+ remove_installed_files
198
+ end
199
+
200
+ def secure?
201
+ message_server_port == 9_093
202
+ end
203
+ end
204
+ end
205
+ end
@@ -0,0 +1,98 @@
1
+ require "awesome_spawn"
2
+ require "fileutils"
3
+ require "linux_admin"
4
+ require 'net/scp'
5
+ require "manageiq/appliance_console/message_configuration"
6
+
7
+ module ManageIQ
8
+ module ApplianceConsole
9
+ class MessageClientConfiguration < MessageConfiguration
10
+ attr_reader :message_server_password, :message_server_username, :installed_files,
11
+ :message_truststore_path_src, :message_ca_cert_path_src
12
+
13
+ def initialize(options = {})
14
+ super(options)
15
+
16
+ @message_server_host = options[:message_server_host]
17
+ @message_server_username = options[:message_server_usernamed] || "root"
18
+ @message_server_password = options[:message_server_password]
19
+
20
+ @message_truststore_path_src = options[:message_truststore_path_src] || truststore_path
21
+ @message_ca_cert_path_src = options[:message_ca_cert_path_src] || ca_cert_path
22
+
23
+ @installed_files = [client_properties_path, messaging_yaml_path, truststore_path]
24
+ end
25
+
26
+ def configure
27
+ begin
28
+ MessageServerConfiguration.new.unconfigure if MessageServerConfiguration.configured?
29
+ configure_messaging_yaml # Set up the local message client in case EVM is actually running on this, Message Server
30
+ create_client_properties # Create the client.properties configuration fle
31
+ fetch_truststore_from_server # Fetch the Java Keystore from the Kafka Server
32
+ configure_messaging_type("kafka") # Settings.prototype.messaging_type = 'kafka'
33
+ restart_evmserverd
34
+ rescue AwesomeSpawn::CommandResultError => e
35
+ say(e.result.output)
36
+ say(e.result.error)
37
+ say("")
38
+ say("Failed to Configure the Message Client- #{e}")
39
+ return false
40
+ rescue => e
41
+ say("Failed to Configure the Message Client- #{e}")
42
+ return false
43
+ end
44
+ true
45
+ end
46
+
47
+ def ask_for_parameters
48
+ say("\nMessage Client Parameters:\n\n")
49
+
50
+ @message_server_host = ask_for_string("Message Server Hostname or IP address")
51
+ @message_server_port = ask_for_integer("Message Server Port number", (1..65_535), 9_093).to_i
52
+ @message_server_username = ask_for_string("Message Server Username", message_server_username)
53
+ @message_server_password = ask_for_password("Message Server Password")
54
+ @message_truststore_path_src = ask_for_string("Message Server Truststore Path", truststore_path)
55
+ @message_ca_cert_path_src = ask_for_string("Message Server CA Cert Path", ca_cert_path)
56
+ @message_keystore_username = ask_for_string("Message Keystore Username", message_keystore_username) if secure?
57
+ @message_keystore_password = ask_for_password("Message Keystore Password") if secure?
58
+ end
59
+
60
+ def show_parameters
61
+ say("\nMessage Client Configuration:\n")
62
+ say("Message Client Details:\n")
63
+ say(" Message Server Hostname: #{message_server_host}\n")
64
+ say(" Message Server Username: #{message_server_username}\n")
65
+ say(" Message Keystore Username: #{message_keystore_username}\n")
66
+ end
67
+
68
+ def fetch_truststore_from_server
69
+ say(__method__.to_s.tr("_", " ").titleize)
70
+
71
+ fetch_from_server(message_truststore_path_src, truststore_path)
72
+ end
73
+
74
+ def fetch_ca_cert_from_server
75
+ say(__method__.to_s.tr("_", " ").titleize)
76
+
77
+ fetch_from_server(message_ca_cert_path_src, ca_cert_path)
78
+ end
79
+
80
+ private
81
+
82
+ def fetch_from_server(src_file, dst_file)
83
+ return if file_found?(dst_file)
84
+
85
+ FileUtils.mkdir_p(dst_file.dirname) unless dst_file.dirname.directory?
86
+
87
+ Net::SCP.start(message_server_host, message_server_username, :password => message_server_password) do |scp|
88
+ scp.download!(src_file, dst_file)
89
+ end
90
+
91
+ File.exist?(dst_file)
92
+ rescue => e
93
+ say("Failed to fetch #{src_file} from server: #{e.message}")
94
+ false
95
+ end
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,321 @@
1
+ require "awesome_spawn"
2
+ require "fileutils"
3
+ require "linux_admin"
4
+ require "manageiq/appliance_console/message_configuration"
5
+
6
+ module ManageIQ
7
+ module ApplianceConsole
8
+ class MessageServerConfiguration < MessageConfiguration
9
+ attr_reader :jaas_config_path,
10
+ :server_properties_path, :server_properties_sample_path,
11
+ :ca_cert_srl_path, :ca_key_path, :cert_file_path, :cert_signed_path,
12
+ :keystore_files, :installed_files, :message_persistent_disk
13
+
14
+ PERSISTENT_DIRECTORY = Pathname.new("/var/lib/kafka/persistent_data").freeze
15
+ PERSISTENT_NAME = "kafka_messages".freeze
16
+
17
+ def initialize(options = {})
18
+ super(options)
19
+
20
+ @message_server_host = options[:message_server_use_ipaddr] == true ? my_ipaddr : options[:message_server_host] || my_hostname
21
+ @message_persistent_disk = LinuxAdmin::Disk.new(:path => options[:message_persistent_disk]) unless options[:message_persistent_disk].nil?
22
+
23
+ @jaas_config_path = config_dir_path.join("kafka_server_jaas.conf")
24
+ @server_properties_path = config_dir_path.join("server.properties")
25
+ @server_properties_sample_path = sample_config_dir_path.join("server.properties")
26
+
27
+ @ca_cert_srl_path = keystore_dir_path.join("ca-cert.srl")
28
+ @ca_key_path = keystore_dir_path.join("ca-key")
29
+ @cert_file_path = keystore_dir_path.join("cert-file")
30
+ @cert_signed_path = keystore_dir_path.join("cert-signed")
31
+
32
+ @keystore_files = [ca_cert_path, ca_cert_srl_path, ca_key_path, cert_file_path, cert_signed_path, truststore_path, keystore_path]
33
+ @installed_files = [jaas_config_path, client_properties_path, server_properties_path, messaging_yaml_path, LOGS_DIR] + keystore_files
34
+ end
35
+
36
+ def configure
37
+ begin
38
+ configure_persistent_disk # Configure the persistent message store on a different disk
39
+ create_jaas_config # Create the message server jaas config file
40
+ create_client_properties # Create the client.properties config
41
+ create_logs_directory # Create the logs directory:
42
+ configure_firewall # Open the firewall for message port 9093
43
+ configure_keystore # Populate the Java Keystore
44
+ create_server_properties # Update the /opt/message/config/server.properties
45
+ configure_messaging_yaml # Set up the local message client in case EVM is actually running on this, Message Server
46
+ configure_messaging_type("kafka") # Settings.prototype.messaging_type = 'kafka'
47
+ restart_services
48
+ rescue AwesomeSpawn::CommandResultError => e
49
+ say(e.result.output)
50
+ say(e.result.error)
51
+ say("")
52
+ say("Failed to Configure the Message Server- #{e}")
53
+ return false
54
+ rescue => e
55
+ say("Failed to Configure the Message Server- #{e}")
56
+ return false
57
+ end
58
+ true
59
+ end
60
+
61
+ def restart_services
62
+ say("Starting zookeeper and configure it to start on reboots ...")
63
+ LinuxAdmin::Service.new("zookeeper").start.enable
64
+
65
+ say("Starting kafka and configure it to start on reboots ...")
66
+ LinuxAdmin::Service.new("kafka").start.enable
67
+
68
+ restart_evmserverd
69
+ end
70
+
71
+ def ask_for_parameters
72
+ say("\nMessage Server Parameters:\n\n")
73
+
74
+ @message_server_host = ask_for_string("Message Server Hostname or IP address", message_server_host)
75
+ @message_keystore_username = ask_for_string("Message Keystore Username", message_keystore_username)
76
+ @message_keystore_password = ask_for_password("Message Keystore Password")
77
+ @message_persistent_disk = ask_for_persistent_disk
78
+ end
79
+
80
+ def ask_for_persistent_disk
81
+ choose_disk if use_new_disk
82
+ end
83
+
84
+ def use_new_disk
85
+ agree("Configure a new persistent disk volume? (Y/N): ")
86
+ end
87
+
88
+ def choose_disk
89
+ ask_for_disk("Persistent disk")
90
+ end
91
+
92
+ def show_parameters
93
+ say("\nMessage Server Configuration:\n")
94
+ say("Message Server Details:\n")
95
+ say(" Message Server Hostname: #{message_server_host}\n")
96
+ say(" Message Keystore Username: #{message_keystore_username}\n")
97
+ say(" Persistent message disk: #{message_persistent_disk.path}\n") if message_persistent_disk
98
+ end
99
+
100
+ def unconfigure
101
+ super
102
+
103
+ unconfigure_firewall
104
+ deactivate_services
105
+ end
106
+
107
+ def self.configured?
108
+ LinuxAdmin::Service.new("kafka").running? ||
109
+ LinuxAdmin::Service.new("zookeeper").running?
110
+ end
111
+
112
+ private
113
+
114
+ def my_ipaddr
115
+ LinuxAdmin::IpAddress.new.address
116
+ end
117
+
118
+ def my_hostname
119
+ LinuxAdmin::Hosts.new.hostname
120
+ end
121
+
122
+ def configure_persistent_disk
123
+ return true unless message_persistent_disk
124
+
125
+ say(__method__.to_s.tr("_", " ").titleize)
126
+
127
+ deactivate_services # Just in case they are running.
128
+
129
+ FileUtils.mkdir_p(PERSISTENT_DIRECTORY)
130
+ LogicalVolumeManagement.new(:disk => message_persistent_disk, :mount_point => PERSISTENT_DIRECTORY, :name => PERSISTENT_NAME).setup
131
+ FileUtils.chmod(0o755, PERSISTENT_DIRECTORY)
132
+ FileUtils.chown("kafka", "kafka", PERSISTENT_DIRECTORY)
133
+
134
+ true
135
+ end
136
+
137
+ def activate_new_persistent_disk
138
+ return true unless message_persistent_disk
139
+
140
+ say(__method__.to_s.tr("_", " ").titleize)
141
+
142
+ data = File.read(server_properties_path)
143
+ data.gsub!(/^log.dirs=.*$/, "log.dirs=#{PERSISTENT_DIRECTORY}")
144
+ File.write(server_properties_path, data)
145
+
146
+ true
147
+ end
148
+
149
+ def create_jaas_config
150
+ say(__method__.to_s.tr("_", " ").titleize)
151
+
152
+ content = <<~JAAS
153
+ KafkaServer {
154
+ org.apache.kafka.common.security.plain.PlainLoginModule required
155
+ username=#{message_keystore_username}
156
+ password=#{message_keystore_password}
157
+ user_admin=#{message_keystore_password} ;
158
+ };
159
+ JAAS
160
+
161
+ File.write(jaas_config_path, content) unless file_found?(jaas_config_path)
162
+ end
163
+
164
+ def create_logs_directory
165
+ say(__method__.to_s.tr("_", " ").titleize)
166
+
167
+ return if file_found?(LOGS_DIR)
168
+
169
+ FileUtils.mkdir_p(LOGS_DIR)
170
+ FileUtils.chmod(0o755, LOGS_DIR)
171
+ FileUtils.chown("kafka", "kafka", LOGS_DIR)
172
+ end
173
+
174
+ def configure_firewall
175
+ say(__method__.to_s.tr("_", " ").titleize)
176
+
177
+ modify_firewall(:add_port)
178
+ end
179
+
180
+ def configure_keystore
181
+ say(__method__.to_s.tr("_", " ").titleize)
182
+
183
+ return if files_found?(keystore_files)
184
+
185
+ keystore_params = assemble_keystore_params
186
+
187
+ FileUtils.mkdir_p(keystore_dir_path) unless keystore_dir_path.directory?
188
+
189
+ # Generte a Java keystore and key pair, creating keystore.jks
190
+ # :stdin_data provides the -storepass twice to confirm and an extra CR to accept the same password for -keypass
191
+ AwesomeSpawn.run!("keytool", :params => keystore_params, :stdin_data => "#{message_keystore_password}\n#{message_keystore_password}\n\n")
192
+
193
+ # Use openssl to create a new CA cert, creating ca-cert and ca-key
194
+ AwesomeSpawn.run!("openssl", :env => {"PASSWORD" => message_keystore_password},
195
+ :params => ["req", "-new", "-x509", {"-keyout" => ca_key_path,
196
+ "-out" => ca_cert_path,
197
+ "-days" => 10_000,
198
+ "-passout" => "env:PASSWORD",
199
+ "-subj" => '/CN=something'}])
200
+
201
+ # Import the CA cert into the trust store, creating truststore.jks
202
+ # :stdin_data provides the -storepass argument and yes to confirm
203
+ AwesomeSpawn.run!("keytool", :params => {"-keystore" => truststore_path,
204
+ "-alias" => "CARoot",
205
+ "-import" => nil,
206
+ "-file" => ca_cert_path},
207
+ :stdin_data => "#{message_keystore_password}\n#{message_keystore_password}\nyes\n")
208
+
209
+ # Generate a certificate signing request (CSR) for an existing Java keystore, creating cert-file
210
+ # :stdin_data provides the -storepass argument
211
+ AwesomeSpawn.run!("keytool", :params => {"-keystore" => keystore_path,
212
+ "-alias" => keystore_params["-alias"],
213
+ "-certreq" => nil,
214
+ "-file" => cert_file_path},
215
+ :stdin_data => "#{message_keystore_password}\n")
216
+
217
+ # Use openssl to sign the certificate with the "CA" certificate, creating ca-cert.srl and cert-signed
218
+ AwesomeSpawn.run!("openssl", :env => {"PASSWORD" => message_keystore_password},
219
+ :params => ["x509", "-req", {"-CA" => ca_cert_path,
220
+ "-CAkey" => ca_key_path,
221
+ "-in" => cert_file_path,
222
+ "-out" => cert_signed_path,
223
+ "-days" => 10_000,
224
+ "-CAcreateserial" => nil,
225
+ "-passin" => "env:PASSWORD"}])
226
+
227
+ # Import a root or intermediate CA certificate to an existing Java keystore, updating keystore.jks
228
+ # :stdin_data provides the -storepass argument and yes to confirm
229
+ AwesomeSpawn.run!("keytool", :params => {"-keystore" => keystore_path,
230
+ "-alias" => "CARoot",
231
+ "-import" => nil,
232
+ "-file" => ca_cert_path},
233
+ :stdin_data => "#{message_keystore_password}\nyes\n")
234
+
235
+ # Import a signed primary certificate to an existing Java keystore, updating keystore.jks
236
+ # :stdin_data provides the -storepass argument
237
+ AwesomeSpawn.run!("keytool", :params => {"-keystore" => keystore_path,
238
+ "-alias" => keystore_params["-alias"],
239
+ "-import" => nil,
240
+ "-file" => cert_signed_path},
241
+ :stdin_data => "#{message_keystore_password}\n")
242
+ end
243
+
244
+ def create_server_properties
245
+ say(__method__.to_s.tr("_", " ").titleize)
246
+
247
+ if message_server_host.ipaddress?
248
+ ident_algorithm = ""
249
+ client_auth = "none"
250
+ else
251
+ ident_algorithm = "HTTPS"
252
+ client_auth = "required"
253
+ end
254
+
255
+ content = <<~SERVER_PROPERTIES
256
+
257
+ listeners=SASL_SSL://:#{message_server_port}
258
+
259
+ ssl.endpoint.identification.algorithm=#{ident_algorithm}
260
+ ssl.keystore.location=#{keystore_path}
261
+ ssl.keystore.password=#{message_keystore_password}
262
+ ssl.key.password=#{message_keystore_password}
263
+
264
+ ssl.truststore.location=#{truststore_path}
265
+ ssl.truststore.password=#{message_keystore_password}
266
+
267
+ ssl.client.auth=#{client_auth}
268
+
269
+ sasl.enabled.mechanisms=PLAIN
270
+ sasl.mechanism.inter.broker.protocol=PLAIN
271
+
272
+ security.inter.broker.protocol=SASL_SSL
273
+ SERVER_PROPERTIES
274
+
275
+ return if file_contains?(server_properties_path, content)
276
+
277
+ FileUtils.cp(server_properties_sample_path, server_properties_path)
278
+ File.write(server_properties_path, content, :mode => "a")
279
+
280
+ activate_new_persistent_disk
281
+ end
282
+
283
+ def unconfigure_firewall
284
+ say(__method__.to_s.tr("_", " ").titleize)
285
+
286
+ modify_firewall(:remove_port)
287
+ end
288
+
289
+ def deactivate_services
290
+ say(__method__.to_s.tr("_", " ").titleize)
291
+
292
+ LinuxAdmin::Service.new("zookeeper").stop
293
+ LinuxAdmin::Service.new("kafka").stop
294
+ end
295
+
296
+ def assemble_keystore_params
297
+ keystore_params = {"-keystore" => keystore_path,
298
+ "-validity" => 10_000,
299
+ "-genkey" => nil,
300
+ "-keyalg" => "RSA"}
301
+
302
+ if message_server_host.ipaddress?
303
+ keystore_params["-alias"] = "localhost"
304
+ keystore_params["-ext"] = "san=ip:#{message_server_host}"
305
+ else
306
+ keystore_params["-alias"] = message_server_host
307
+ keystore_params["-ext"] = "san=dns:#{message_server_host}"
308
+ end
309
+
310
+ keystore_params["-dname"] = "cn=#{keystore_params["-alias"]}"
311
+
312
+ keystore_params
313
+ end
314
+
315
+ def modify_firewall(action)
316
+ AwesomeSpawn.run!("firewall-cmd", :params => {action => "#{message_server_port}/tcp", :permanent => nil})
317
+ AwesomeSpawn.run!("firewall-cmd --reload")
318
+ end
319
+ end
320
+ end
321
+ end