httpd_configmap_generator 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
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