httpd_configmap_generator 0.1.1 → 0.3.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.
Files changed (35) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.travis.yml +3 -2
  4. data/.yamllint +11 -0
  5. data/Dockerfile +3 -2
  6. data/Gemfile +4 -0
  7. data/README-active-directory.md +11 -15
  8. data/README-ipa.md +7 -12
  9. data/README-ldap.md +62 -0
  10. data/README-oidc.md +39 -0
  11. data/README-saml.md +9 -14
  12. data/README.md +37 -49
  13. data/bin/httpd_configmap_generator +36 -50
  14. data/httpd_configmap_generator.gemspec +5 -3
  15. data/lib/httpd_configmap_generator.rb +2 -0
  16. data/lib/httpd_configmap_generator/active_directory.rb +2 -2
  17. data/lib/httpd_configmap_generator/base.rb +10 -6
  18. data/lib/httpd_configmap_generator/base/command.rb +19 -17
  19. data/lib/httpd_configmap_generator/base/config_helper.rb +15 -0
  20. data/lib/httpd_configmap_generator/base/config_map.rb +43 -26
  21. data/lib/httpd_configmap_generator/base/file_helper.rb +67 -0
  22. data/lib/httpd_configmap_generator/base/kerberos.rb +10 -8
  23. data/lib/httpd_configmap_generator/base/network.rb +27 -25
  24. data/lib/httpd_configmap_generator/base/pam.rb +6 -4
  25. data/lib/httpd_configmap_generator/base/sssd.rb +1 -1
  26. data/lib/httpd_configmap_generator/ipa.rb +12 -1
  27. data/lib/httpd_configmap_generator/ldap.rb +186 -0
  28. data/lib/httpd_configmap_generator/oidc.rb +48 -0
  29. data/lib/httpd_configmap_generator/saml.rb +16 -14
  30. data/lib/httpd_configmap_generator/version.rb +1 -1
  31. data/templates/httpd-scc-sysadmin.yaml +38 -0
  32. metadata +18 -14
  33. data/lib/httpd_configmap_generator/base/config.rb +0 -13
  34. data/lib/httpd_configmap_generator/base/file.rb +0 -65
  35. data/lib/httpd_configmap_generator/options.rb +0 -13
@@ -7,8 +7,8 @@
7
7
  # -o filename: for the generated auth-config map.
8
8
  #
9
9
 
10
- require "bundler/setup"
11
- require "trollop"
10
+ Dir.chdir(__dir__) { require "bundler/setup" }
11
+ require "optimist"
12
12
  require "httpd_configmap_generator"
13
13
 
14
14
  CMD = File.basename($PROGRAM_NAME)
@@ -20,62 +20,63 @@ end
20
20
 
21
21
  module HttpdConfigmapGenerator
22
22
  class Cli
23
- def parse_arguments(auth_config)
24
- opts = Trollop.options do
23
+ SUB_COMMANDS = [HttpdConfigmapGenerator.supported_auth_types] | %w(update export)
24
+
25
+ def run
26
+ Optimist.options do
25
27
  version("#{CMD} #{HttpdConfigmapGenerator::VERSION} - External Authentication Configuration script")
26
28
  banner <<-EOS
27
29
  #{version}
28
30
 
29
31
  Usage: #{CMD} auth_type | update | export [--help | options]
30
32
 
33
+ supported auth_type: #{HttpdConfigmapGenerator.supported_auth_types.sort.join(', ')}
34
+
31
35
  #{CMD} options are:
32
36
  EOS
33
37
  opt :version, "Version of the #{CMD} command",
34
38
  :default => false, :short => "-V"
35
- auth_config.required_options.each do |key, key_options|
36
- opt key, key_options[:description], HttpdConfigmapGenerator::Cli.options_for(key_options, true)
37
- end
38
- auth_config.optional_options.each do |key, key_options|
39
- opt key, key_options[:description], HttpdConfigmapGenerator::Cli.options_for(key_options)
40
- end
41
- end
42
- opts
43
- end
44
-
45
- def run_configure(auth_type)
46
- begin
47
- auth_config = HttpdConfigmapGenerator.new_config(auth_type)
48
- rescue => err
49
- error_msg(err.to_s)
39
+ stop_on(SUB_COMMANDS)
50
40
  end
51
41
 
52
- auth_config.run_configure(parse_arguments(auth_config))
53
- end
42
+ auth_type = ARGV.shift
43
+ Optimist.die "Must specify an authentication type" if auth_type.nil?
54
44
 
55
- def run_update
56
45
  begin
57
- auth_config = HttpdConfigmapGenerator::Update.new
46
+ auth_config =
47
+ case auth_type
48
+ when "update" then HttpdConfigmapGenerator::Update.new
49
+ when "export" then HttpdConfigmapGenerator::Export.new
50
+ else HttpdConfigmapGenerator.new_config(auth_type)
51
+ end
58
52
  rescue => err
59
53
  error_msg(err.to_s)
60
54
  end
61
55
 
62
- auth_config.update(parse_arguments(auth_config))
63
- end
64
-
65
- def run_export
66
- begin
67
- auth_config = HttpdConfigmapGenerator::Export.new
68
- rescue => err
69
- error_msg(err.to_s)
56
+ params = Optimist.options do
57
+ auth_config.required_options.each do |key, key_options|
58
+ opt key, key_options[:description], HttpdConfigmapGenerator::Cli.options_for(key_options, true)
59
+ end
60
+ auth_config.optional_options.each do |key, key_options|
61
+ opt key, key_options[:description], HttpdConfigmapGenerator::Cli.options_for(key_options)
62
+ end
70
63
  end
71
64
 
72
- auth_config.export(parse_arguments(auth_config))
65
+ case auth_type
66
+ when "update" then auth_config.update(params)
67
+ when "export" then auth_config.export(params)
68
+ else auth_config.run_configure(params)
69
+ end
73
70
  end
74
71
 
75
72
  def self.options_for(key_options, required = false)
76
73
  options = {}
77
- options[:default] = key_options.key?(:default) ? key_options[:default] : ""
78
- options[:required] = true if required
74
+ if key_options[:default].nil?
75
+ options[:type] = key_options[:type] || :string
76
+ else
77
+ options[:default] = key_options[:default]
78
+ end
79
+ options[:required] = required
79
80
  options[:short] = key_options[:short] if key_options[:short]
80
81
  options[:multi] = key_options[:multi] if key_options[:multi]
81
82
  options
@@ -83,19 +84,4 @@ Usage: #{CMD} auth_type | update | export [--help | options]
83
84
  end
84
85
  end
85
86
 
86
- if ARGV.empty?
87
- error_msg("
88
- Usage: #{CMD} auth_type | update | export [--help | options]
89
- Supported auth_type: #{HttpdConfigmapGenerator.supported_auth_types.join(', ')}
90
- ")
91
- else
92
- auth_type = ARGV.shift
93
- case auth_type
94
- when "update"
95
- HttpdConfigmapGenerator::Cli.new.run_update
96
- when "export"
97
- HttpdConfigmapGenerator::Cli.new.run_export
98
- else
99
- HttpdConfigmapGenerator::Cli.new.run_configure(auth_type)
100
- end
101
- end
87
+ HttpdConfigmapGenerator::Cli.new.run
@@ -14,8 +14,10 @@ Gem::Specification.new do |s|
14
14
  s.description = "The Httpd Configmap Generator"
15
15
  s.licenses = ["Apache-2.0"]
16
16
 
17
- s.files = `git ls-files -z`.split("\x0").reject do |f|
18
- f.match(%r{^(test|spec|features)/})
17
+ if Dir.exist?(File.join(__dir__, ".git"))
18
+ s.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(%r{^(test|spec|features)/})
20
+ end
19
21
  end
20
22
  s.bindir = "bin"
21
23
  s.executables = s.files.grep(%r{^bin/}) { |f| File.basename(f) } - %w(console setup)
@@ -30,5 +32,5 @@ Gem::Specification.new do |s|
30
32
  s.add_dependency "awesome_spawn", "~> 1.4"
31
33
  s.add_dependency "iniparse", "~> 1.4"
32
34
  s.add_dependency "more_core_extensions", "~> 3.4"
33
- s.add_dependency "trollop", "~> 2.1"
35
+ s.add_dependency "optimist", "~> 3.0"
34
36
  end
@@ -2,7 +2,9 @@ require "httpd_configmap_generator/version"
2
2
  require "httpd_configmap_generator/base"
3
3
  require "httpd_configmap_generator/active_directory"
4
4
  require "httpd_configmap_generator/ipa"
5
+ require "httpd_configmap_generator/ldap"
5
6
  require "httpd_configmap_generator/saml"
7
+ require "httpd_configmap_generator/oidc"
6
8
  require "httpd_configmap_generator/update"
7
9
  require "httpd_configmap_generator/export"
8
10
  require "more_core_extensions/core_ext/hash"
@@ -9,6 +9,8 @@ module HttpdConfigmapGenerator
9
9
 
10
10
  def required_options
11
11
  super.merge(
12
+ :host => { :description => "Application Domain",
13
+ :short => "-h" },
12
14
  :ad_domain => { :description => "Active Directory Domain" },
13
15
  :ad_user => { :description => "Active Directory User" },
14
16
  :ad_password => { :description => "Active Directory Password" }
@@ -34,10 +36,8 @@ module HttpdConfigmapGenerator
34
36
  /etc/pam.d/postlogin-ac
35
37
  /etc/pam.d/smartcard-auth-ac
36
38
  /etc/pam.d/system-auth-ac
37
- /etc/resolv.conf
38
39
  /etc/sssd/sssd.conf
39
40
  /etc/sysconfig/authconfig
40
- /etc/sysconfig/network
41
41
  )
42
42
  end
43
43
 
@@ -1,8 +1,8 @@
1
1
  require "pathname"
2
2
  require "httpd_configmap_generator/base/command"
3
- require "httpd_configmap_generator/base/config"
3
+ require "httpd_configmap_generator/base/config_helper"
4
4
  require "httpd_configmap_generator/base/config_map"
5
- require "httpd_configmap_generator/base/file"
5
+ require "httpd_configmap_generator/base/file_helper"
6
6
  require "httpd_configmap_generator/base/kerberos"
7
7
  require "httpd_configmap_generator/base/network"
8
8
  require "httpd_configmap_generator/base/pam"
@@ -11,6 +11,13 @@ require "httpd_configmap_generator/base/sssd"
11
11
 
12
12
  module HttpdConfigmapGenerator
13
13
  class Base
14
+ include Command
15
+ include ConfigHelper
16
+ include FileHelper
17
+ include Kerberos
18
+ include Network
19
+ include Pam
20
+
14
21
  APACHE_USER = "apache".freeze
15
22
  HTTP_KEYTAB = "/etc/http.keytab".freeze
16
23
  IPA_COMMAND = "/usr/bin/ipa".freeze
@@ -47,10 +54,7 @@ module HttpdConfigmapGenerator
47
54
 
48
55
  def required_options
49
56
  {
50
- :host => { :description => "Application Domain",
51
- :short => "-h" },
52
- :output => { :description => "Configuration map file to create",
53
- :short => "-o" }
57
+ :output => { :description => "Configuration map file to create", :short => "-o" }
54
58
  }
55
59
  end
56
60
 
@@ -2,27 +2,29 @@ require "awesome_spawn"
2
2
 
3
3
  module HttpdConfigmapGenerator
4
4
  class Base
5
- def command_run(executable, options = {})
6
- if opts && opts[:debug]
7
- debug_msg("Running Command: #{AwesomeSpawn.build_command_line(executable, options)}")
5
+ module Command
6
+ def command_run(executable, options = {})
7
+ if opts && opts[:debug]
8
+ debug_msg("Running Command: #{AwesomeSpawn.build_command_line(executable, options)}")
9
+ end
10
+ AwesomeSpawn.run(executable, options)
8
11
  end
9
- AwesomeSpawn.run(executable, options)
10
- end
11
12
 
12
- def command_run!(executable, options = {})
13
- if opts && opts[:debug]
14
- debug_msg("Running Command: #{AwesomeSpawn.build_command_line(executable, options)}")
13
+ def command_run!(executable, options = {})
14
+ if opts && opts[:debug]
15
+ debug_msg("Running Command: #{AwesomeSpawn.build_command_line(executable, options)}")
16
+ end
17
+ AwesomeSpawn.run!(executable, options)
15
18
  end
16
- AwesomeSpawn.run!(executable, options)
17
- end
18
19
 
19
- def log_command_error(err)
20
- err_msg("Command Error: #{err}")
21
- if err.kind_of?(AwesomeSpawn::CommandResultError)
22
- err_msg("stdout: #{err.result.output}")
23
- err_msg("stderr: #{err.result.error}")
24
- else
25
- err_msg(err.backtrace)
20
+ def log_command_error(err)
21
+ err_msg("Command Error: #{err}")
22
+ if err.kind_of?(AwesomeSpawn::CommandResultError)
23
+ err_msg("stdout: #{err.result.output}")
24
+ err_msg("stderr: #{err.result.error}")
25
+ else
26
+ err_msg(err.backtrace)
27
+ end
26
28
  end
27
29
  end
28
30
  end
@@ -0,0 +1,15 @@
1
+ require "active_support"
2
+ require "active_support/core_ext" # for Time.current
3
+
4
+ module HttpdConfigmapGenerator
5
+ class Base
6
+ module ConfigHelper
7
+ def config_file_backup(path)
8
+ if File.exist?(path)
9
+ timestamp = Time.current.strftime(TIMESTAMP_FORMAT)
10
+ FileUtils.copy(path, "#{path}.#{timestamp}")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -16,11 +16,11 @@ module HttpdConfigmapGenerator
16
16
  @config_map = template
17
17
  end
18
18
 
19
- def generate(auth_type, realm, file_list)
19
+ def generate(auth_type, realm = "undefined", file_list = nil, metadata = {})
20
20
  info_msg("Generating Auth Config-Map for #{auth_type}")
21
21
  @config_map = template(auth_type, realm)
22
22
  file_specs = gen_filespecs(file_list)
23
- define_configuration(file_specs)
23
+ define_configuration(file_specs, metadata)
24
24
  include_files(file_specs)
25
25
  end
26
26
 
@@ -71,7 +71,7 @@ module HttpdConfigmapGenerator
71
71
  file_specs = []
72
72
  file_list.each do |file|
73
73
  file_specs << file_entry_spec(file.strip)
74
- end
74
+ end unless file_list.nil?
75
75
  file_specs.sort_by { |file_spec| file_spec[:basename] }
76
76
  end
77
77
 
@@ -84,30 +84,42 @@ module HttpdConfigmapGenerator
84
84
  file_specs = []
85
85
  file_list.each do |file_to_add|
86
86
  file_spec = file_to_add.split(",").map(&:strip)
87
- case file_spec.length
88
- when 1
89
- file = file_spec.first
90
- file_entry = file_entry_spec(file)
91
- when 2
92
- source_file, target_file = file_spec
93
- raise "Must specify a mode for URL file sources" if source_file =~ URI.regexp(%w(http https))
94
- file_entry = file_entry_spec(source_file, target_file)
95
- when 3
96
- source_file, target_file, mode = file_spec
97
- if source_file =~ URI.regexp(%w(http https))
98
- fetch_network_file(source_file, target_file)
99
- file_entry = file_entry_spec(target_file, target_file, mode)
87
+ file_entry =
88
+ case file_spec.length
89
+ when 1
90
+ file_entry_spec(file_spec.first)
91
+ when 2
92
+ source_file, target_file = file_spec
93
+ file_entry_for_source_target(source_file, target_file)
94
+ when 3
95
+ source_file, target_file, mode = file_spec
96
+ file_entry_for_source_target_mode(source_file, target_file, mode)
100
97
  else
101
- file_entry = file_entry_spec(source_file, target_file, mode)
98
+ raise "Invalid file specification #{file_to_add}"
102
99
  end
103
- else
104
- raise "Invalid file specification #{file_to_add}"
105
- end
106
100
  file_specs << file_entry
107
101
  end
108
102
  file_specs.sort_by { |file_spec| file_spec[:basename] }
109
103
  end
110
104
 
105
+ def file_entry_for_source_target(source_file, target_file)
106
+ raise "Must specify a mode for URL file sources" if source_file =~ URI.regexp(%w(http https))
107
+ file_entry = file_entry_spec(source_file, target_file)
108
+ file_entry[:source_file] = source_file
109
+ file_entry
110
+ end
111
+
112
+ def file_entry_for_source_target_mode(source_file, target_file, mode)
113
+ if source_file =~ URI.regexp(%w(http https))
114
+ fetch_network_file(source_file, target_file)
115
+ file_entry = file_entry_spec(target_file, target_file, mode)
116
+ else
117
+ file_entry = file_entry_spec(source_file, target_file, mode)
118
+ file_entry[:source_file] = source_file
119
+ end
120
+ file_entry
121
+ end
122
+
111
123
  def file_entry_spec(source_file, target_file = nil, mode = nil)
112
124
  target_file = source_file.dup unless target_file
113
125
  unless mode
@@ -123,7 +135,7 @@ module HttpdConfigmapGenerator
123
135
  }
124
136
  end
125
137
 
126
- def update_configuration(file_specs)
138
+ def update_configuration(file_specs, metadata={})
127
139
  auth_configuration = fetch_auth_configuration
128
140
  return define_configuration(file_specs) unless auth_configuration
129
141
  # first, remove any file_specs references in the file list, we don't want duplication here.
@@ -134,7 +146,7 @@ module HttpdConfigmapGenerator
134
146
  end
135
147
  auth_configuration = auth_configuration.join("\n") + "\n"
136
148
  # now, append any of the new file_specs at the end of the list.
137
- append_configuration(auth_configuration, file_specs)
149
+ append_configuration(auth_configuration, file_specs, metadata)
138
150
  end
139
151
 
140
152
  def search_file_entry(target_file)
@@ -145,14 +157,14 @@ module HttpdConfigmapGenerator
145
157
  entry ? entry.first.split('=')[1].strip.split(' ') : nil
146
158
  end
147
159
 
148
- def define_configuration(file_specs)
160
+ def define_configuration(file_specs, metadata={})
149
161
  auth_configuration = "# External Authentication Configuration File\n#\n"
150
- append_configuration(auth_configuration, file_specs)
162
+ append_configuration(auth_configuration, file_specs, metadata)
151
163
  end
152
164
 
153
165
  def include_files(file_specs)
154
166
  file_specs.each do |file_spec|
155
- content = File.read(file_spec[:target])
167
+ content = File.read(file_spec[:source_file] || file_spec[:target])
156
168
  content = Base64.encode64(content) if file_spec[:binary]
157
169
  # encode(:universal_newline => true) will convert \r\n to \n, necessary for to_yaml to render properly.
158
170
  config_map[DATA_SECTION].merge!(file_basename(file_spec) => content.encode(:universal_newline => true))
@@ -163,12 +175,17 @@ module HttpdConfigmapGenerator
163
175
  file_spec[:binary] ? "#{file_spec[:basename]}.base64" : file_spec[:basename]
164
176
  end
165
177
 
166
- def append_configuration(auth_configuration, file_specs)
178
+ def append_configuration(auth_configuration, file_specs, metadata)
167
179
  file_specs.each do |file_spec|
168
180
  debug_msg("Adding file #{file_spec[:target]} ...")
169
181
  auth_configuration += "file = #{file_basename(file_spec)} #{file_spec[:target]} #{file_spec[:mode]}\n"
170
182
  end
171
183
  config_map[DATA_SECTION] ||= {}
184
+
185
+ metadata.each do |key, value|
186
+ config_map[DATA_SECTION].merge!(key => value)
187
+ end
188
+
172
189
  config_map[DATA_SECTION].merge!(AUTH_CONFIGURATION => auth_configuration)
173
190
  end
174
191
 
@@ -0,0 +1,67 @@
1
+ require "pathname"
2
+
3
+ module HttpdConfigmapGenerator
4
+ class Base
5
+ module FileHelper
6
+ def template_directory
7
+ @template_directory ||=
8
+ Pathname.new(Gem::Specification.find_by_name("httpd_configmap_generator").full_gem_path).join("templates")
9
+ end
10
+
11
+ def cp_template(file, src_dir, dest_dir = "/")
12
+ src_path = path_join(src_dir, file)
13
+ dest_path = path_join(dest_dir, file.gsub(".erb", ""))
14
+ if src_path.to_s.include?(".erb")
15
+ File.write(dest_path, ERB.new(File.read(src_path), nil, '-').result(binding))
16
+ else
17
+ FileUtils.cp(src_path, dest_path)
18
+ end
19
+ end
20
+
21
+ def delete_target_file(file_path)
22
+ if File.exist?(file_path)
23
+ if opts[:force]
24
+ info_msg("File #{file_path} exists, forcing a delete")
25
+ File.delete(file_path)
26
+ else
27
+ raise "File #{file_path} already exist"
28
+ end
29
+ end
30
+ end
31
+
32
+ def create_target_directory(file_path)
33
+ dirname = File.dirname(file_path)
34
+ return if File.exist?(dirname)
35
+ debug_msg("Creating directory #{dirname} ...")
36
+ FileUtils.mkdir_p(dirname)
37
+ end
38
+
39
+ def rm_file(file, dir = "/")
40
+ path = path_join(dir, file)
41
+ File.delete(path) if File.exist?(path)
42
+ end
43
+
44
+ def path_join(*args)
45
+ path = Pathname.new(args.shift)
46
+ args.each { |path_seg| path = path.join("./#{path_seg}") }
47
+ path
48
+ end
49
+
50
+ def file_binary?(file)
51
+ data = File.read(file)
52
+ ascii = control = binary = total = 0
53
+ data[0..512].each_byte do |c|
54
+ total += 1
55
+ if c < 32
56
+ control += 1
57
+ elsif c >= 32 && c <= 128
58
+ ascii += 1
59
+ else
60
+ binary += 1
61
+ end
62
+ end
63
+ control.to_f / ascii > 0.1 || binary.to_f / ascii > 0.05
64
+ end
65
+ end
66
+ end
67
+ end