/
apache.rb
307 lines (263 loc) · 8.34 KB
/
apache.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
require 'symbiosis/config_file'
require 'symbiosis/domain/http'
require 'symbiosis/host'
require 'tempfile'
require 'diffy'
module Symbiosis
module ConfigFiles
class Apache < Symbiosis::ConfigFile
#
# Tests the file using Apache and a temporary file. Returns true if
# apache2 deems the snippet OK.
#
def ok?
return false unless File.executable?("/usr/sbin/apache2")
output = []
config = self.generate_config(self.template)
tempfile = Tempfile.new(File.basename(self.filename))
tempfile.puts(config)
tempfile.close(false)
IO.popen( "APACHE_RUN_DIR=/tmp APACHE_LOG_DIR=/tmp /usr/sbin/apache2 -C 'ErrorLog /dev/null' -C 'UseCanonicalName off' -C 'Include /etc/apache2/mods-enabled/*.load' -C 'Include /etc/apache2/mods-enabled/*.conf' -f #{tempfile.path} -t 2>&1 ") {|io| output = io.readlines }
if "Syntax OK" == output.last.chomp
warn output.collect{|o| "\t"+o}.join.chomp if $VERBOSE
tempfile.unlink
return true
else
warn output.collect{|o| "\t"+o}.join.chomp
File.rename(tempfile.path, tempfile.path+".conf")
warn "\tTemporary config snippet retained at #{tempfile.path}.conf"
return false
end
end
def diff
config = self.generate_config(self.template)
tempfile = Tempfile.new(File.basename(self.filename))
tempfile.puts(config)
tempfile.close(false)
fn = ( File.exists?(self.filename) ? self.filename : '/dev/null' )
Diffy::Diff.new(fn, tempfile.path, :source => 'files', :include_diff_info => true)
ensure
tmpfile.unlink
end
#
# This checks a site has its config file linked into the sites-enabled
# directory. If no filename has been specified, it defaults to
# self.filename with "sites-available" transformed to "sites-enabled".
#
# This function returns true if self.filename is symlinked to fn.
#
def enabled?(fn = nil)
fn = self.filename.sub("sites-available","sites-enabled") if fn.nil?
#
# Make sure the file exists, and that it is a symlink pointing to our
# config file
#
if File.symlink?(fn)
ln = File.readlink(fn)
unless ln =~ /^\//
ln = File.join(File.dirname(fn),ln)
end
return File.expand_path(ln) == self.filename
end
#
# FIXME: should probably check at this point to see if any files point
# back to the config, or if any file contains the configuration for
# this domain.
#
#
# Otherwise return false
#
false
end
#
# This enables a site by symlinking the self.filename to fn.
#
# If fn is not specified, then self.filename is used, with
# sites-available changed to sites-enabled.
#
# If the force flag is set to true, then any file in the way is removed
# first.
#
def enable(fn = nil, force = false)
#
# Take the filename and and replace available with enabled if no
# filename is given.
#
fn = self.filename.sub("sites-available","sites-enabled") if fn.nil?
#
# Do nothing if we're already enabled.
#
return if self.enabled?(fn)
#
# Clobber any files in the way, if the force flag is set.
#
if force and File.exist?(fn)
File.unlink(fn)
end
#
# If the file is still there after disabling, raise an error
#
raise Errno::EEXIST, fn if File.exist?(fn)
#
# Symlink away!
#
File.symlink(self.filename, fn)
nil
end
#
# This disables a site whose configuration is contained in fn. This
# function makes sure that the site is enabled, before disabling it.
#
#
#
def disable(fn = nil, force = false)
#
# Take the filename and and replace available with enabled if no
# filename is given.
#
fn = self.filename.sub("sites-available","sites-enabled") if fn.nil?
#
# Remove the file, only if it is a symlink to our filename, or if the
# force flag is set.
#
if self.enabled?(fn) or (File.exist?(fn) and force)
File.unlink(fn)
end
#
# If the file is still there after disabling, raise an error
#
raise Errno::EEXIST, fn if File.exist?(fn)
nil
end
#
# Returns an array of Symbiosis::IPAddr objects, one for each IP
# available for this domain, if defined, or the system's primary IPv4 and
# IPv6 addresses.
#
def available_ips
if defined? @domain and @domain.is_a?(Symbiosis::Domain)
@domain.ips
else
[Symbiosis::Host.primary_ipv4, Symbiosis::Host.primary_ipv6].compact
end
end
#
# Return all the IPs as apache-compatible strings for use in templates.
#
def ips
self.available_ips.collect do |ip|
if ip.ipv6?
"["+ip.to_s+"]"
else
ip.to_s
end
end
end
#
# Return just the first IP for use in templates.
#
def ip
ip = self.available_ips.first
warn "\tUsing one IP (#{ip}) where the domain has more than one configured!" if self.available_ips.length > 1 and $VERBOSE
if ip.ipv6?
"["+ip.to_s+"]"
else
ip.to_s
end
end
#
# Return the domain config directory.
#
# If no domain has been defined, nil is returned.
#
def domain_directory
if defined?(@domain) and @domain.is_a?(Symbiosis::Domain)
@domain.directory
else
nil
end
end
#
# Returns the domain's htdocs directory.
#
def htdocs_directory
if defined?(@domain) and @domain.is_a?(Symbiosis::Domain)
@domain.htdocs_dir
else
nil
end
end
#
# Returns the domain's cgi-bin directory.
#
def cgibin_directory
if defined?(@domain) and @domain.is_a?(Symbiosis::Domain)
@domain.cgibin_dir
else
nil
end
end
#
#
# Return a ServerAlias snippet for all server aliases for a domain.
#
def server_aliases
domain.aliases.collect{|a| "ServerAlias #{a}"}.join("\n ")
end
#
# Returns the certificate, key, and bundle configuration lines.
#
# This does not have an explicit validation step. That should be handled
# elsewhere.
#
def ssl_config
ans = []
if defined?(@domain) and @domain.is_a?(Symbiosis::Domain)
#
#
#
ans << "SSLCertificateFile #{@domain.ssl_certificate_file}"
#
# Add the separate key unless the key is in the certificate.
#
ans << "SSLCertificateKeyFile #{@domain.ssl_key_file}" unless @domain.ssl_certificate_file == @domain.ssl_key_file
#
# Add a bundle, if needed.
#
ans << "SSLCertificateChainFile #{@domain.ssl_bundle_file}" if @domain.ssl_bundle_file
elsif File.exist?("/etc/ssl/ssl.crt")
#
# TODO: this makes absolutely no checks for the certificate validity
# etc., unlike the @domain functions above.
#
ans << "SSLCertificateFile /etc/ssl/ssl.crt"
#
# Add the key and bundle, assuming they exist.
#
ans << "SSLCertificateKeyFile /etc/ssl/ssl.key" if File.exist?("/etc/ssl/ssl.key")
ans << "SSLCertificateChainFile /etc/ssl/ssl.bundle" if File.exist?("/etc/ssl/ssl.bundle")
end
ans.join("\n ")
end
#
# Checks to see if a domain has mandatory ssl.
#
# If no domain is set, then this returns false.
#
def mandatory_ssl?
if defined?(@domain) and @domain.is_a?(Symbiosis::Domain)
@domain.ssl_mandatory?
else
false
end
end
#
# This returns the FQDN
#
def hostname
return @hostname if defined? @hostname and @hostname.is_a?(String)
@hostname = Symbiosis::Host.fqdn
end
end
end
end