Skip to content

Commit

Permalink
Merge pull request SteveLTN#267 from MarcelWaldvogel/live-update
Browse files Browse the repository at this point in the history
Allow on-the-fly reconfiguration
  • Loading branch information
SteveLTN authored Feb 13, 2021
2 parents c663b2d + dd2f3e0 commit d18e448
Show file tree
Hide file tree
Showing 10 changed files with 93 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ RUN rm /var/log/nginx/access.log && \
WORKDIR /root

RUN apt-get update && \
apt-get install -y python ruby cron iproute2 apache2-utils logrotate wget && \
apt-get install -y python ruby cron iproute2 apache2-utils logrotate wget inotify-tools && \
apt-get clean && \
rm -rf /var/lib/apt/lists/*

Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ Docker Hub page:
- [Other configurations](#other-configurations)
- [Advanced Usage](#advanced-usage)
- [Configure Nginx through Environment Variables](#configure-nginx-through-environment-variables)
- [Change Configuration Dynamically](#change-configuration-dynamically)
- [Override Nginx Configuration Files](#override-nginx-configuration-files)
- [How It Works](#how-it-works)
- [About Rate Limits of Let's Encrypt](#about-rate-limits-of-lets-encrypt)
Expand Down Expand Up @@ -573,6 +574,14 @@ server {
}
```
### Change Configuration Dynamically
Environment variables may be dynamically overridden by modifying files
`/var/lib/https-portal/dynamic-env`. The file's name and contents will create
an environment variable with that name and contents, respectively. About 1s
after the last modification, the configuration will be updated to reflect the
new configuration. This allows modifying the configuration without downtime.
### Override Nginx Configuration Files
You can override default nginx settings by providing a config segment of
Expand Down
4 changes: 4 additions & 0 deletions fs_overlay/bin/setup
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/ruby
require '/opt/certs_manager/certs_manager'

CertsManager.new.setup
6 changes: 2 additions & 4 deletions fs_overlay/etc/cont-init.d/20-setup
Original file line number Diff line number Diff line change
@@ -1,4 +1,2 @@
#!/usr/bin/with-contenv ruby
require '/opt/certs_manager/certs_manager'

CertsManager.new.setup
#!/usr/bin/with-contenv /usr/bin/execlineb
/usr/bin/justc-envdir -I /var/lib/https-portal/dynamic-env /bin/setup
4 changes: 3 additions & 1 deletion fs_overlay/etc/services.d/10-docker-gen/run
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#!/usr/bin/with-contenv sh

docker-gen -watch -only-exposed -notify-output -notify reconfig /etc/docker-gen/domains.tmpl /var/run/domains
docker-gen -watch -only-exposed -notify-output -notify \
"justc-envdir -I /var/lib/https-portal/dynamic-env /bin/reconfig" \
/etc/docker-gen/domains.tmpl /var/run/domains
3 changes: 3 additions & 0 deletions fs_overlay/etc/services.d/30-dynamic-env/finish
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
#!/usr/bin/execlineb -S0

s6-svscanctl -t /var/run/s6/services
38 changes: 38 additions & 0 deletions fs_overlay/etc/services.d/30-dynamic-env/run
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
#!/bin/sh
env=/var/lib/https-portal/dynamic-env
dom=/var/lib/https-portal/dynamic-domains
mkdir -p $env $dom

# Remember when we started (tiny race condition window)
touch $env.stamp # Outside of watched dirs!

# Wait for a modification to an all-uppercase file (ignore temp/swp files)
inotifywait -qm -e close_write,move,delete $env $dom | while read line
do
echo "[dynamic-env] read $line"
if echo "$line" | egrep -q '( [_A-Z]*|\.dom)$'
then
echo "[dynamic-env] All-uppercase or .dom file modified"

echo "[dynamic-env] Wait for activity to cease..."
while inotifywait -qq -t 1 -e close_write,move,delete $env $dom
do
:
done

# Apply new configuration; but only, if anything actually
# changed (top-level "while" loop will continue getting
# potentially old events)
if find $env $dom -newer $env.stamp | grep -q .
then
# Remember when we started (tiny race condition window)
touch $env.stamp # Outside of watched dirs!

echo "[dynamic-env] File changed since last run: Applying new configuration..."
/usr/bin/justc-envdir $env /bin/reconfig
echo "[dynamic-env] Configuration applied" >&2
else
echo "[dynamic-env] No file changed since last run -- suppressing"
fi
fi
done
35 changes: 23 additions & 12 deletions fs_overlay/opt/certs_manager/certs_manager.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,16 @@ class CertsManager
attr_accessor :lock

def setup
setup_config(true)
end

def reconfig
setup_config(false)
end

def setup_config(initial)
with_lock do
add_dockerhost_to_hosts
ensure_dockerhost_in_hosts
ensure_crontab

NAConfig.domains.each do |domain|
Expand All @@ -29,13 +37,23 @@ def setup
config_domains(NAConfig.domains)
Nginx.setup

Nginx.start
if initial
Nginx.start
else
Nginx.reload
end

ensure_signed(NAConfig.domains, true)

Nginx.stop
if initial
Nginx.stop
else
Nginx.reload
end
end
if initial
sleep 1 # Give Nginx some time to shutdown
end
sleep 1 # Give Nginx some time to shutdown
end

def renew
Expand All @@ -60,17 +78,10 @@ def renew
puts "Renewal done."
end

def reconfig
with_lock do
ensure_keys_and_certs_exist(NAConfig.auto_discovered_domains)
config_domains(NAConfig.auto_discovered_domains)
ensure_signed(NAConfig.auto_discovered_domains, false)
end
end

private

def config_domains(domains)
Dir['/etc/nginx/conf.d/*.conf'].each { |file| File.delete file }
domains.each do |domain|
Nginx.config_domain(domain)
end
Expand Down
10 changes: 6 additions & 4 deletions fs_overlay/opt/certs_manager/lib/commands.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,13 @@ def mkdir(domain)
system "mkdir -p #{domain.dir}"
end

def add_dockerhost_to_hosts
docker_host_ip = `/sbin/ip route|awk '/default/ { print $3 }'`.strip
def ensure_dockerhost_in_hosts
unless File.foreach("/etc/hosts").grep(/dockerhost/).any?
docker_host_ip = `/sbin/ip route|awk '/default/ { print $3 }'`.strip

File.open('/etc/hosts', 'a') do |f|
f.puts "#{docker_host_ip}\tdockerhost"
File.open('/etc/hosts', 'a') do |f|
f.puts "#{docker_host_ip}\tdockerhost"
end
end
end

Expand Down
8 changes: 4 additions & 4 deletions fs_overlay/opt/certs_manager/lib/na_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,11 @@ def self.env_domains
end

def self.auto_discovered_domains
if File.exist? '/var/run/domains'
parse File.read('/var/run/domains')
else
[]
doms = []
Dir['/var/run/domains', "#{NAConfig.portal_base_dir}/dynamic-domains/*.dom"].each do |file|
doms.concat(parse File.read(file))
end
doms
end

def self.debug_mode?
Expand Down

0 comments on commit d18e448

Please sign in to comment.