Add batch install certbot certs feature.

Add redirect to default domain name feature.
This commit is contained in:
BoHung Chiu 2021-10-08 20:04:38 +08:00
parent ce266fcd78
commit 23d659a6da
15 changed files with 437 additions and 125 deletions

View File

@ -134,6 +134,16 @@ class Admin::SitePanelController < OrbitAdminController
@site_server = SiteServer.find(params[:id])
end
end
def update_nginx_settings
extra_text = ""
if params[:key].present?
extra_text = "[#{params[:key].gsub(/[\(\)\[\]]/){|ff| "\\"+ff }}]"
end
Thread.new do
system("bundle exec rake create_site:update_multiple_nginx_setting#{extra_text}")
end
render :json => {"success"=>true}
end
def install_certbot
extra_text = ""
if params[:server_name].present?
@ -224,6 +234,7 @@ class Admin::SitePanelController < OrbitAdminController
render 'see_detail_for_created_site' and return
elsif params[:type] == 'change_server_name'
site_construct = SiteConstruct.find(params[:id])
site_construct.update_attributes(update_site_params)
site_construct.update(:domain_name=>params[:site_construct][:domain_name])
cmd = "bundle exec rake create_site:change_site_server_name[#{params[:id]},'#{params[:site_construct][:domain_name]}','#{params[:site_construct][:port].to_a.join('////') }']"
site_construct.update(:status=>"execing",:infos=>["Execing change domain name task..."])
@ -235,15 +246,19 @@ class Admin::SitePanelController < OrbitAdminController
SiteConstruct.find(params[:id]).destroy
redirect_to :back and return
elsif params[:type] == 'select_cert'
@site_construct = SiteConstruct.find(params[:id])
@site_construct.update(:redirect_to_https=>params[:redirect_to_https])
is_certbot = true
if params[:site_cert_id] != "certbot"
is_certbot = false
@site_construct.update(:site_cert_id=>BSON::ObjectId(params[:site_cert_id]))
if !params[:is_server]
@site_construct = SiteConstruct.find(params[:id])
@site_construct.update(:redirect_to_https=>params[:redirect_to_https])
is_certbot = true
if params[:site_cert_id] != "certbot"
is_certbot = false
@site_construct.update(:site_cert_id=>BSON::ObjectId(params[:site_cert_id]))
end
else
is_certbot = true
end
Thread.new do
system("bundle exec rake create_site:change_site_cert[#{params[:id]},#{is_certbot}]")
system("bundle exec rake create_site:change_site_cert[#{params[:id]},#{is_certbot},#{params[:is_server]}]")
end
else
Thread.new do
@ -294,7 +309,11 @@ class Admin::SitePanelController < OrbitAdminController
site_construct_params = params.require(:site_construct).permit! rescue {}
end
def site_server_params
params.require(:site_server).permit! rescue {}
server_params = params.require(:site_server).permit! rescue {}
if server_params[:default_domain_names].nil?
server_params[:default_domain_names] = []
end
server_params
end
def check_server_ability(site_num_add=0)
store_token = current_site.store_token
@ -313,4 +332,8 @@ class Admin::SitePanelController < OrbitAdminController
@server_ability.update_attributes(site_num: site_num)
end
end
def update_site_params
site_params = params.require(:site_construct).permit!
site_params.except(:domain_name,:port)
end
end

View File

@ -30,21 +30,27 @@ class SiteCert
self.end_date.strftime("%Y-%m-%d") rescue ""
end
def change_data
cert_file_md5 = `openssl x509 -noout -modulus -in #{self.cert_file.file.file} | openssl md5`
private_key_md5 = `openssl rsa -noout -modulus -in #{self.private_key.file.file} | openssl md5`
is_valid = (cert_file_md5 == private_key_md5)
domain_names = `openssl x509 -text < #{self.cert_file.file.file} | grep 'DNS:' | sed 's/\s*DNS:\([a-z0-9.\-]*\)[,\s]\?/\1 /g'`.split('DNS:').map{|s| s.strip}.select{|s| s.present?} rescue []
start_date_text = `openssl x509 -text < #{self.cert_file.file.file} -startdate -noout`.split('=').last.strip
end_date_text = `openssl x509 -text < #{self.cert_file.file.file} -enddate -noout`.split('=').last.strip
self.start_date = DateTime.parse(start_date_text) rescue nil
self.end_date = DateTime.parse(end_date_text) rescue nil
self.is_valid = is_valid
self.domain_names = domain_names
if !@skip_callback
@skip_callback = true
self.save(:validate=>false)
end
@skip_callback = false
false
if !@skip_callback
cert_file_md5 = `openssl x509 -noout -modulus -in #{self.cert_file.file.file} | openssl md5`
private_key_md5 = `openssl rsa -noout -modulus -in #{self.private_key.file.file} | openssl md5`
is_valid = (cert_file_md5 == private_key_md5)
domain_names = `openssl x509 -text < #{self.cert_file.file.file} | grep 'DNS:' | sed 's/\s*DNS:\([a-z0-9.\-]*\)[,\s]\?/\1 /g'`.split('DNS:').map{|s| s.strip}.select{|s| s.present?} rescue []
if domain_names.blank?
self.is_valid = false
@skip_callback = true
self.save(:validate=>false)
else
start_date_text = `openssl x509 -text < #{self.cert_file.file.file} -startdate -noout`.split('=').last.strip
end_date_text = `openssl x509 -text < #{self.cert_file.file.file} -enddate -noout`.split('=').last.strip
self.start_date = DateTime.parse(start_date_text) rescue nil
self.end_date = DateTime.parse(end_date_text) rescue nil
self.is_valid = is_valid
self.domain_names = domain_names
@skip_callback = true
self.save(:validate=>false)
end
@skip_callback = false
false
end
end
end

View File

@ -5,6 +5,8 @@ class SiteConstruct
SiteServer.all.map{|s| s.server_name}
end
SITE_TYPES = ["School","Gravity"]
field :enable_redirect_default_domain, type: Integer, default: 0 #0 => use default, 1 => disable, 2 => enable
field :default_domain_idx, type: Integer, default: -1 #-1 present use default
field :rails_env, type: String, :default => "development"
field :server_type
field :site_name
@ -104,6 +106,7 @@ class SiteConstruct
else
port = port.to_s
end
redirect_default_text = get_redirect_default_text(port)
port_text = port
if port.to_i == 443
if self.site_cert.nil?
@ -118,12 +121,18 @@ class SiteConstruct
new_server_block = new_server_block.gsub(/\s*ssl_certificate[^;]+;/,'')
level_2_block = parse_nginx_text_to_server_blocks(old_server_block,true,2)
get_redirect_block = level_2_block.select{|t| t.match(/\s*return\s+30[12]\s+https:\/\/\$host\$request_uri\s*;/)}
get_redirect_to_default_block = level_2_block.select{|t| t.match(/\s*return\s+30[12]\s+http(s|):\/\/[^\$]+\$request_uri\s*;/)}
location_app_block = level_2_block.select{|t| t.match(/location\s+@app/)}
if get_redirect_block.count > 0
get_redirect_block.each do |redirect_block|
new_server_block = new_server_block.gsub(redirect_block,'')
end
end
if get_redirect_to_default_block.count > 0
get_redirect_to_default_block.each do |redirect_block|
new_server_block = new_server_block.gsub(redirect_block,'')
end
end
if location_app_block.count > 0
location_app_block = location_app_block.map do |app_block|
new_app_block = app_block.gsub(/proxy_set_header\s+X-Forwarded-Proto\s+https\s*;/,"")
@ -144,6 +153,9 @@ class SiteConstruct
" }\n"}
end
end
if redirect_default_text.present?
new_server_block = new_server_block.sub(/\s*root/){|ff| redirect_default_text + ff}
end
new_server_block = new_server_block.gsub(/[ \t\s]+\n/,"\n\n").gsub(/\n{3,}/,'\n\n')
else
'server {\n'+
@ -153,6 +165,7 @@ class SiteConstruct
((self.redirect_to_https && !self.site_cert.nil?) ? " if ($host ~ (#{self.site_cert.domain_names.map{|s| '^'+s.gsub('.','\.').gsub('*','[^.]*').gsub(',','')}.join('|')}) ) {\n"+
" return 301 https://$host$request_uri;\n"+
"}\n" : '') : '')+
redirect_default_text +
' root '+self.full_site_path+'/public;\n\n'+
' server_name '+self.domain_name+';\n\n'+
' client_max_body_size 500m;\n\n'+
@ -213,4 +226,39 @@ class SiteConstruct
def site_server
SiteServer.where(server_name: self.server_type).first
end
def get_default_domain
custom_default_domain_name = ""
domain_names = domain_name.strip().split(" ")
if default_domain_idx == -1
custom_default_domain_name = site_server.default_domain_names.select do |default_domain_name|
default_domain_name = Regexp.new("\\A"+default_domain_name.gsub(".","\\.").gsub("*","[^.]*"))
domain_names.select{|n| n.match(default_domain_name) }.count != 0
end.first
else
custom_default_domain_name = domain_names[default_domain_idx]
end
custom_default_domain_name
end
def get_enable_redirect_default_domain
(self.enable_redirect_default_domain == 0 ? site_server.enable_redirect_default_domain : ((self.enable_redirect_default_domain - 1) == 1) rescue false)
end
def default_enable_redirect_default_domain
site_server.enable_redirect_default_domain rescue false
end
def get_redirect_default_text(port=80)
port = port.to_i
is_enable = get_enable_redirect_default_domain
scheme = (port == 443 ? "https" : "http")
port_text = ((port == 80 || port == 443 || port.blank?) ? "" : ":#{port}")
text = ""
if is_enable
default_domain = get_default_domain
if default_domain.present?
text = "\n\n if ($host !~* (^#{default_domain.gsub('.','\.')}$) ) {\n"+
" return 302 #{scheme}://#{default_domain}#{port_text}$request_uri;\n"+
" }\n\n"
end
end
text
end
end

View File

@ -5,6 +5,8 @@ class SiteServer
include OrbitTag::Taggable
include OrbitModel::Status
field :server_name , type: String ,default: ''
field :enable_redirect_default_domain, type: Boolean, default: false
field :default_domain_names, type: Array, default: []
field :domain_name , type: String ,default: ''
field :domain_names, type: Array, default: []
field :ip , type: String ,default: ''
@ -12,6 +14,7 @@ class SiteServer
field :password , type: String ,default: ''
field :active , type: Boolean ,default: true
field :has_certbot, type: Boolean , default: false
field :need_update_site_ids, type: Array, default: []
def domain_names
if self.domain_name != ''
[self.domain_name]
@ -19,9 +22,38 @@ class SiteServer
super
end
end
def site_constructs
SiteConstruct.where(server_type: self.server_name)
end
before_save do
if self.domain_name != '' && self.domain_names.length !=0
self.domain_name = ''
end
tmp_site_constructs = site_constructs
need_change = false
if self.enable_redirect_default_domain_changed?
tmp_site_constructs = tmp_site_constructs.where(:enable_redirect_default_domain.in=>[nil,0])
need_change = true
end
if (need_change || (!need_change && self.enable_redirect_default_domain))
if self.default_domain_names_changed?
check_domains = []
removed_default_domains = self.default_domain_names_was - self.default_domain_names
add_default_domains = self.default_domain_names - self.default_domain_names_was
check_domains += removed_default_domains
check_domains += add_default_domains
check_domains = check_domains.map{|domain| Regexp.new("(\\A|\\s)"+domain.gsub(".","\\.").gsub("*","[^.]*") + "($|\\s)")}
tmp_site_constructs = tmp_site_constructs.where(:domain_name.in=>check_domains)
need_change = true
elsif need_change
check_domains = self.default_domain_names
check_domains = check_domains.map{|domain| Regexp.new("(\\A|\\s)"+domain.gsub(".","\\.").gsub("*","[^.]*") + "($|\\s)")}
tmp_site_constructs = tmp_site_constructs.where(:domain_name.in=>check_domains)
end
end
if need_change
self.need_update_site_ids += tmp_site_constructs.pluck(:id)
self.need_update_site_ids = self.need_update_site_ids.uniq
end
end
end

View File

@ -40,6 +40,9 @@
<td>
<% if site_server.has_certbot%>
<span class="green_text"><%= t("client_management.alreay_install") %></span>
<% if site_server.active %>
<span><a class="btn btn-success install_certbot_certs" title="<%= t("client_management.install_certbot_certificates_in_batches") %>" data-id="<%=site_server.id.to_s%>"><%= t("client_management.install_certbot_certificates_in_batches") %></a></span>
<% end %>
<% else %>
<span class="red_text"><%= t("client_management.not_install") %></span>
<% if site_server.active %>
@ -48,6 +51,9 @@
<% end %>
</td>
<td>
<% if site_server.need_update_site_ids.count != 0 %>
<a class="btn btn-primary update_nginx_settings" href="#" data-key="<%=site_server.server_name%>" style="background: blueviolet;"><%=t("client_management.update_nginx_settings")%></a>
<% end %>
<a class="btn btn-primary" href = "<%= admin_site_panel_edit_server_info_path+"?id=#{site_server.id.to_s}" %>"><%=t(:edit)%></a>
<a class="btn btn-primary" href = "#" onclick="if(window.confirm('Do you really want to delete <%=site_server.server_name%>?')) { window.location.href = '<%= admin_site_panel_edit_server_info_path+"?id=#{site_server.id.to_s}&type=delete" %>';}"><%=t(:remove)%></a>
<a class="btn btn-primary" href = "<%= admin_site_panel_sites_list_path+"?server_name=#{site_server.server_name}" %>"><%= t('client_management.see_sites') %></a>
@ -170,6 +176,14 @@
show_infos_dialog("detect_sites");
})
});
$(".update_nginx_settings").off('click').on('click',function(){
var key = $(this).data("key");
if( key == undefined)
key = "";
$.post("<%=admin_site_panel_update_nginx_settings_path%>",{"key":key}).done(function(){
show_infos_dialog("update_multiple_nginx_setting");
})
});
$("#exec_commands").off('click').on('click',function(){
//show_exec_commands_block(server_names)
$.post("<%=admin_site_panel_edit_server_info_path%>",{"type":"get_server_names"}).done(function(server_names){
@ -193,5 +207,11 @@
show_infos_dialog("install_certbot");
})
})
$(".install_certbot_certs").off("click").on("click",function(){
var id = $(this).data("id");
$.post("<%=admin_site_panel_edit_site_path%>",{'id': id,'type':'select_cert', 'is_server': true}).done(function(response){
show_infos_dialog("install_certbot_certs");
});
})
})
</script>

View File

@ -261,9 +261,7 @@
var redirect_to_https = ($('#https_setting_area [name="redirect_to_https"]:checked').length != 0);
var site_cert_id = $('#https_setting_area [name="site_cert"]:checked').eq(0).val();
if(site_cert_id != undefined){
console.log(site_cert_id);
$.post("<%=admin_site_panel_edit_site_path%>",{'id': id,'type':'select_cert','site_cert_id': site_cert_id,'redirect_to_https': redirect_to_https}).done(function(response){
console.log(response);
show_infos_dialog(item);
});
}

View File

@ -15,6 +15,14 @@
tp2.append(tp3);
tp2.append('<%= delete_domain_button %>')
}
function add_default_domain(ele){
var tp=$(ele);
var tp2=tp.parents('.controls').eq(0).find('.group');
var tp3 = '<input class="domain_name" name="site_server[default_domain_names][]" type="text" placeholder="*.example.com (<%=t("client_management.domain_hint")%>)" title="*.example.com (<%=t("client_management.domain_hint")%>)">';
tp2.append('<br>');
tp2.append(tp3);
tp2.append('<%= delete_domain_button %>')
}
function delete_domain(ele){
$(ele).prev('input').remove()
$(ele).prev('br').remove()
@ -51,9 +59,9 @@
</div>
<% unless @site_server.new_record? %>
<div class="control-group">
<label class="control-label muted"><%= t("client_management.active") %></label>
<label class="control-label muted" for="active_server"><%= t("client_management.active") %></label>
<div class="controls">
<%= f.check_box :active %>
<%= f.check_box :active,:id=>"active_server" %>
</div>
</div>
<% end %>
@ -87,6 +95,29 @@
%>
</div>
</div>
<div class="control-group">
<label class="control-label muted" for="enable_redirect_default_domain"><%= t("client_management.redirect_to_default_domain_name") %></label>
<div class="controls">
<%= f.check_box :enable_redirect_default_domain,:id=>"enable_redirect_default_domain" %>
</div>
</div>
<div class="control-group">
<label class="control-label muted" for="default_domain_names"><%=t("client_management.default_domain_names")%>:</label>
<div class="controls">
<div class="group">
<%
@site_server.default_domain_names.each_with_index do |domain_name,i| %>
<%= '<br>'.html_safe if i !=0 %>
<%= text_field_tag "site_server[default_domain_names][]",domain_name,{:id=>nil,:class=>"domain_name"} %>
<%= delete_domain_button %>
<% end %>
</div>
<%= f.button "#{t('add')} domain", class: 'btn btn-info',
:onclick=> "add_default_domain(this)",
:type => 'button'
%>
</div>
</div>
<div class="control-group">
<label class="control-label muted" for="ip">IP:</label>
<div class="controls">

View File

@ -6,11 +6,20 @@
<label class="control-label">Server</label>
<div class="controls"><%=@site.server_type%></div>
</div>
<div class="control-group">
<label class="control-label" for="enable_redirect_default_domain"><%= t("client_management.redirect_to_default_domain_name") %></label>
<div class="controls">
<% %>
<%= select_tag "site_construct[enable_redirect_default_domain]", options_for_select((0..2).to_a.collect{ |i| [I18n.t("client_management.option.#{i}") + (i == 0 ? ": #{@site.default_enable_redirect_default_domain ? I18n.t("client_management.option.2") : I18n.t("client_management.option.1")}" : ""), i.to_s] }, @site.enable_redirect_default_domain) ,:id=>"enable_redirect_default_domain" %>
</div>
</div>
<%= hidden_field_tag "site_construct[default_domain_idx]", "-1" %>
<div class="control-group">
<label class="control-label">Domain name</label>
<div class="controls">
<% @site.domain_name.split(' ').each_with_index do |domain_name,i| %>
<div class="domain_group">
<%= check_box_tag "site_construct[default_domain_idx]", i, (@site.default_domain_idx == i), :class=>"default_domain_idx", :title=>t("client_management.set_to_default_domain_name") %>
<%= text_field_tag nil,domain_name,:class=>'domain_name',onchange: 'domain_name_change()',:oninput=>'replace_domain_input(this)' %>
<% if i != 0 %>
<button class="btn btn-danger" onclick="delete_domain(this)">
@ -66,7 +75,7 @@
</div>
<div class="control-group">
<label class="control-label">Site path</label>
<div class="controls"><%=@site.path%></div>
<div class="controls"><%=@site.full_site_path%></div>
</div>
<div class="control-group">
<div class="controls">
@ -168,12 +177,16 @@
var first_domain_group = $('.domain_group').first()
var clone_domain_group = $(first_domain_group).clone()
clone_domain_group.find('input[type="text"]').val('')
clone_domain_group.find('.default_domain_idx').val($(".default_domain_idx").length);
clone_domain_group.append("<button class=\"btn btn-danger\" onclick=\"delete_domain(this)\">delete domain</button>")
$('.domain_group').last().after(clone_domain_group)
})
function delete_domain(ele){
$('.domain_group').eq($(ele).parents('.domain_group').index('.domain_group')).remove()
domain_name_change()
$(".default_domain_idx").each(function(i,v){
$(v).val(i);
})
}
function replace_domain_input(ele){
var correct_val = $(ele).val().match(/([0-9]||[a-z]||[-_.])*/gi).join("");

View File

@ -7,6 +7,18 @@ en:
upload_cert: Upload Cert
cert_management: Cert Management
client_management:
install_certbot_certificates_in_batches: Install certbot certificates in batches
update_nginx_settings: "Update nginx settings"
set_to_default_domain_name: "Set to default domain name"
domain_hint: "* only match one level url"
redirect_to_default_domain_name: Redirect to default domain_name
default_domain_names: Default domain names
disable: Disable
enable: Enable
option:
'0': Default
'1': Disable
'2': Enable
alreay_install: Alreay install
not_install: Not install
active: Active

View File

@ -7,6 +7,18 @@ zh_tw:
upload_cert: 上傳憑證
cert_management: 憑證管理
client_management:
install_certbot_certificates_in_batches: 批次安裝certbot憑證
update_nginx_settings: "修改nginx設定"
set_to_default_domain_name: "設定為預設的domain name"
domain_hint: "*僅能匹配一層的網址"
redirect_to_default_domain_name: 跳轉到預設的domain name
default_domain_names: 預設的domain names
disable: 關閉
enable: 開啟
option:
'0': 預設
'1': 關閉
'2': 開啟
alreay_install: 已安裝
not_install: 未安裝
active: 啟用

View File

@ -52,6 +52,7 @@ Rails.application.routes.draw do
post "site_panel/install_certbot" => "site_panel#install_certbot"
post "site_panel/update_cert_setting" => "site_panel#update_cert_setting"
post "site_panel/update_nginx_settings" => "site_panel#update_nginx_settings"
resources :site_panel do
delete 'destroy_cert'
get 'edit_cert'

View File

@ -3,98 +3,133 @@ require 'pathname'
require 'json'
namespace :create_site do
desc "Change Site Cert"
task :change_site_cert,[:id,:is_certbot] => :environment do |task,args|
task :change_site_cert,[:id,:is_certbot,:id_is_server] => :environment do |task,args|
begin
@site_construct = SiteConstruct.find(args.id)
@site_cert = @site_construct.site_cert
site_server = @site_construct.site_server
@site_construct.update(:infos=>[],:status=>"changing")
is_certbot = (args.is_certbot == "true") || (@site_cert.is_certbot rescue false)
if !site_server.nil? && (!@site_cert.nil? || is_certbot)
@password = site_server.password
Net::SSH.start(site_server.ip , site_server.account , password: site_server.password) do |ssh|
if is_certbot
domain_name = @site_construct.domain_name
if domain_name.present?
certbot_path = exec_ssh_command_by_sudo_and_see_output(ssh,"bash -l -c 'which certbot certbot-auto'",false,true).strip.split("\n")[0]
if certbot_path.present?
if @site_cert
update_infos("Using certbot to change cert setting...")
else
update_infos("Using certbot to generate cert for #{domain_name}...")
end
redirect_to_https = @site_construct.redirect_to_https
exec_ssh_command_by_sudo_and_see_output(ssh,"sudo -p 'sudo password:' #{certbot_path} --nginx -d #{domain_name} -n --#{redirect_to_https ? 'redirect' : 'no-redirect'}",true,false)
nginx_file = @site_construct.nginx_file
nginx_file_content = exec_ssh_command_by_sudo_and_see_output(ssh,"cat #{nginx_file}",false,true)
crt_file_path = nginx_file_content.match(/ssl_certificate\s+(.*)/)[1].split(';').first rescue ''
private_key_path = nginx_file_content.match(/ssl_certificate_key\s+(.*)/)[1].split(';').first rescue ''
site_cert = @site_construct.site_cert
if crt_file_path.present? && private_key_path.present?
if site_cert.nil?
site_cert = SiteCert.where(:source_paths=>[crt_file_path,private_key_path]).first
site_cert = SiteCert.new if site_cert.nil?
end
if true #site_cert.source_paths.count == 0
site_cert["cert_file"] = File.basename(crt_file_path)
cert_file_store_path = site_cert.cert_file.file.file
crt_file_content = exec_ssh_command_by_sudo_and_see_output(ssh,"cat #{crt_file_path}",false).select{|s| s.present?}.join("\n").strip.split(/(\r\n|\n)/).select{|s| s.present?}.join("\n")
FileUtils.mkdir_p(File.dirname(cert_file_store_path)) unless Dir.exist?(File.dirname(cert_file_store_path))
File.open(site_cert.cert_file.file.file,'w+'){|f| f.write(crt_file_content)}
site_cert["private_key"] = File.basename(private_key_path)
private_key_store_path = site_cert.private_key.file.file
private_key_content = exec_ssh_command_by_sudo_and_see_output(ssh,"cat #{private_key_path}",false).select{|s| s.present?}.join("\n").strip.split(/(\r\n|\n)/).select{|s| s.present?}.join("\n")
FileUtils.mkdir_p(File.dirname(private_key_store_path)) unless Dir.exist?(File.dirname(private_key_path))
File.open(site_cert.private_key.file.file,'w+'){|f| f.write(private_key_content)}
site_cert.source_paths = [crt_file_path,private_key_path]
site_cert.is_certbot = private_key_path.include?("letsencrypt")
site_cert.save
@site_construct.update(:site_cert=>site_cert)
end
all_ports = (@site_construct.port + ["443"]).uniq
@site_construct.update(:port=> all_ports )
update_infos("Finish installing cert with certbot!")
else
update_infos("Certbot generate cert failed!")
update_infos("Please check your domain dns setting(A record)!")
@site_construct.update(:status=>"error")
end
else
update_infos("Please install certbot first!")
end
else
update_infos("Please set domain name first!")
end
else
update_infos("Copying Cert to #{@site_construct.server_type}...")
cert_file_content = [(@site_cert.cert_file.file.read.strip rescue ""),(@site_cert.ca_bundle.file.read.strip rescue "")].join("\n").strip
private_key_content = @site_cert.private_key.file.read
cert_file_store_path = @site_construct.cert_file_remote_store_path
exec_ssh_command_by_sudo(ssh,"mkdir -p #{File.dirname(cert_file_store_path)}")
exec_command_by_user(ssh,"x='#{cert_file_content}'; echo '#{@password}' | sudo -S sh -c \"echo '$x' > #{cert_file_store_path}\"")
private_key_store_path = @site_construct.private_key_remote_store_path
exec_ssh_command_by_sudo(ssh,"mkdir -p #{File.dirname(private_key_store_path)}")
exec_command_by_user(ssh,"x='#{private_key_content}'; echo '#{@password}' | sudo -S sh -c \"echo '$x' > #{private_key_store_path}\"")
update_infos("Finish copy.")
update_infos("Setting Cert...")
nginx_file_content = exec_command_by_user(ssh,"cat #{@site_construct.nginx_file}")
all_ports = (@site_construct.port + ["443"]).uniq
@site_construct.update(:port=> all_ports )
nginx_file_content = @site_construct.generate_nginx_text(nginx_file_content)
cmd = "x='#{nginx_file_content}'; echo '#{@password}' | sudo -S sh -c \"echo '$x' > #{@site_construct.nginx_file}\""
exec_command_by_user(ssh,cmd)
end
exec_ssh_command_by_sudo(ssh,"service nginx restart")
update_infos("Finish!")
@site_construct.update(:status=>"finish")
site_server = nil
site_constructs = []
@is_multithread = false
@thread = nil
if args.id_is_server
site_server = SiteServer.find(args.id)
site_constructs = site_server.site_constructs.to_a
@is_multithread = true
Multithread.where(:key=>'install_certbot_certs').each{|thread| thread.destroy if (thread.status["status"] == "error" || thread.status["status"] == "finish")}
Multithread.where(:key=>'install_certbot_certs').destroy
@thread = Multithread.where(:key=>'install_certbot_certs').first
if @thread.nil?
@thread = Multithread.create(:key=>'install_certbot_certs',:status=>{"infos"=>[],"status"=>"execing"})
else
return false
end
else
update_infos("Cert not found!")
@site_construct = SiteConstruct.find(args.id)
site_server = @site_construct.site_server
@site_construct.update(:infos=>[],:status=>"changing")
site_constructs << @site_construct
end
tmp_is_certbot = (args.is_certbot == "true")
if !site_server.nil?
@password = site_server.password
Net::SSH.start(site_server.ip , site_server.account , password: site_server.password) do |ssh|
if tmp_is_certbot || (@site_cert.is_certbot rescue false)
domain_names = site_constructs.flat_map{|site_construct| site_construct.domain_name.strip.split(" ")}.select{|d| d.present? && d.match(/^[\d\.]+$/).nil?}
domain_names = domain_names.select{|d| `dig #{d} +short`.strip == site_server.ip}
if domain_names.count != 0
certbot_path = exec_ssh_command_by_sudo_and_see_output(ssh,"bash -l -c 'which certbot certbot-auto'",false,true).strip.split("\n")[0]
if certbot_path.present?
auto_update_infos("Using certbot to update cert setting...")
if site_constructs.count > 1
auto_update_infos("Generating single cert for multiple domain names...")
end
redirect_to_https = (@site_construct.redirect_to_https rescue false)
exec_ssh_command_by_sudo_and_see_output(ssh,"sudo -p 'sudo password:' #{certbot_path} --nginx #{domain_names.map{|d| "-d " + d}.join(" ")} -n --#{redirect_to_https ? 'redirect' : 'no-redirect'}",true,false)
site_constructs.each do |site_construct|
@site_construct = site_construct
auto_update_infos("Checking cert for #{site_construct.site_name}...")
nginx_file = site_construct.nginx_file
nginx_file_content = exec_ssh_command_by_sudo_and_see_output(ssh,"cat #{nginx_file}",false,true)
crt_file_path = nginx_file_content.match(/(?<=!#|^)\s*ssl_certificate\s+(.*)/)[1].split(';').first rescue ''
private_key_path = nginx_file_content.match(/(?<=!#|^)\s*ssl_certificate_key\s+(.*)/)[1].split(';').first rescue ''
site_cert = site_construct.site_cert
if crt_file_path.present? && private_key_path.present?
if site_cert.nil? || !site_cert.is_certbot
site_cert = SiteCert.where(:source_paths=>[crt_file_path,private_key_path]).first
site_cert = SiteCert.new if site_cert.nil?
end
if true #site_cert.source_paths.count == 0
site_cert["cert_file"] = File.basename(crt_file_path)
cert_file_store_path = site_cert.cert_file.file.file
crt_file_content = exec_ssh_command_by_sudo_and_see_output(ssh,"cat #{crt_file_path}",false).select{|s| s.present?}.join("\n").strip.split(/(\r\n|\n)/).select{|s| s.present?}.join("\n")
FileUtils.mkdir_p(File.dirname(cert_file_store_path)) unless Dir.exist?(File.dirname(cert_file_store_path))
File.open(site_cert.cert_file.file.file,'w+'){|f| f.write(crt_file_content)}
site_cert["private_key"] = File.basename(private_key_path)
private_key_store_path = site_cert.private_key.file.file
private_key_content = exec_ssh_command_by_sudo_and_see_output(ssh,"cat #{private_key_path}",false).select{|s| s.present?}.join("\n").strip.split(/(\r\n|\n)/).select{|s| s.present?}.join("\n")
FileUtils.mkdir_p(File.dirname(private_key_store_path)) unless Dir.exist?(File.dirname(private_key_path))
File.open(site_cert.private_key.file.file,'w+'){|f| f.write(private_key_content)}
site_cert.source_paths = [crt_file_path,private_key_path]
site_cert.is_certbot = private_key_path.include?("letsencrypt")
site_cert.save
site_construct.update(:site_cert=>site_cert)
end
all_ports = (site_construct.port + ["443"]).uniq
site_construct.update(:port=> all_ports )
auto_update_infos("Finish installing cert with certbot in #{site_construct.site_name} !")
else
auto_update_infos("Certbot generate cert failed!")
auto_update_infos("Please check your domain dns setting(A record)!")
site_construct.update(:status=>"error")
end
end
else
auto_update_infos("Please install certbot first!")
end
else
auto_update_infos("Please set domain name first!")
end
else
site_constructs.each do |site_construct|
@site_cert = @site_construct.site_cert
next if @site_cert.nil?
is_certbot = tmp_is_certbot || (@site_cert.is_certbot rescue false)
@site_construct = site_construct
@site_cert.change_data
if @site_cert.is_valid
auto_update_infos("Copying Cert to #{@site_construct.server_type}...")
cert_file_content = [(@site_cert.cert_file.file.read.strip rescue ""),(@site_cert.ca_bundle.file.read.strip rescue "")].join("\n").strip
private_key_content = @site_cert.private_key.file.read
cert_file_store_path = @site_construct.cert_file_remote_store_path
exec_ssh_command_by_sudo(ssh,"mkdir -p #{File.dirname(cert_file_store_path)}")
exec_command_by_user(ssh,"x='#{cert_file_content}'; echo '#{@password}' | sudo -S sh -c \"echo '$x' > #{cert_file_store_path}\"")
private_key_store_path = @site_construct.private_key_remote_store_path
exec_ssh_command_by_sudo(ssh,"mkdir -p #{File.dirname(private_key_store_path)}")
exec_command_by_user(ssh,"x='#{private_key_content}'; echo '#{@password}' | sudo -S sh -c \"echo '$x' > #{private_key_store_path}\"")
auto_update_infos("Finish copy.")
auto_update_infos("Setting Cert...")
nginx_file_content = exec_command_by_user(ssh,"cat #{@site_construct.nginx_file}")
all_ports = (@site_construct.port + ["443"]).uniq
@site_construct.update(:port=> all_ports )
nginx_file_content = @site_construct.generate_nginx_text(nginx_file_content)
cmd = "x='#{nginx_file_content}'; echo '#{@password}' | sudo -S sh -c \"echo '$x' > #{@site_construct.nginx_file}\""
exec_command_by_user(ssh,cmd)
end
end
end
exec_ssh_command_by_sudo(ssh,"service nginx restart")
auto_update_infos("Finish!")
if @is_multithread
@thread.update(:status=>@thread.status.merge({"status"=>"finish"}))
else
@site_construct.update(:status=>"finish")
end
end
else
auto_update_infos("Cert not found!")
@site_construct.update(:status=>"error")
end
rescue => e
puts [e,e.backtrace]
update_infos(e.to_s)
auto_update_infos(e.to_s)
@site_construct.update(:status=>"error")
end
end
@ -109,4 +144,14 @@ namespace :create_site do
end
return output
end
def auto_update_infos(info)
if @is_multithread
update_thread_infos(info)
if @site_construct
update_infos(info)
end
else
update_infos(info)
end
end
end

View File

@ -12,18 +12,11 @@ namespace :create_site do
update_infos("Starting change domain name and ports.")
Net::SSH.start(site_server.ip , site_server.account , password: site_server.password) do |ssh|
@site_construct.update(:domain_name=>args.server_name)
update_infos("Reading setting file.")
nginx_file_content = exec_command_by_user(ssh,"cat #{@site_construct.nginx_file}")
if args.port.present?
all_ports = args.port.split('////')
@site_construct.update(:port=> all_ports )
end
nginx_file_content = @site_construct.generate_nginx_text(nginx_file_content)
update_infos("Writing...")
cmd = "x='#{nginx_file_content}'; echo '#{@password}' | sudo -S sh -c \"echo '$x' > #{@site_construct.nginx_file}\""
exec_command_by_user(ssh,cmd)
exec_ssh_command_by_sudo(ssh,"service nginx restart")
update_infos("Finish!")
change_construct_nginx(ssh,@site_construct)
@site_construct.update(:status=>'finish')
end
end
@ -33,6 +26,17 @@ namespace :create_site do
puts [e,e.backtrace]
end
end
def change_construct_nginx(ssh,site_construct,update_multithread=false)
@is_multithread = update_multithread
auto_update_infos("Reading setting file...")
nginx_file_content = exec_command_by_user(ssh,"cat #{site_construct.nginx_file}")
nginx_file_content = site_construct.generate_nginx_text(nginx_file_content)
auto_update_infos("Writing...")
cmd = "x='#{nginx_file_content}'; echo '#{@password}' | sudo -S sh -c \"echo '$x' > #{site_construct.nginx_file}\""
exec_command_by_user(ssh,cmd)
exec_ssh_command_by_sudo(ssh,"service nginx restart")
auto_update_infos("Finish!")
end
def exec_command_by_user(session,command)
output = session.exec!(command)
return output[0...-1].gsub(/^\n[\n]+/,'')

View File

@ -4,7 +4,6 @@ require 'fileutils'
namespace :create_site do
desc "Detect sites"
task :detect_sites,[:detect_name] => :environment do |task,args|
#Multithread.where(:key=>'detect_sites').destroy
Multithread.where(:key=>'detect_sites').each{|thread| thread.destroy if (thread.status["status"] == "error" || thread.status["status"] == "finish")}
Multithread.where(:key=>'detect_sites').destroy
@thread = Multithread.where(:key=>'detect_sites').first
@ -122,8 +121,8 @@ namespace :create_site do
end
end
site_construct.update(:rails_env => rails_env)
crt_file_path = nginx_file_content.match(/ssl_certificate\s+(.*)/)[1].split(';').first rescue ''
private_key_path = nginx_file_content.match(/ssl_certificate_key\s+(.*)/)[1].split(';').first rescue ''
crt_file_path = nginx_file_content.match(/(?<=!#|^)\s*ssl_certificate\s+(.*)/)[1].split(';').first rescue ''
private_key_path = nginx_file_content.match(/(?<=!#|^)\s*ssl_certificate_key\s+(.*)/)[1].split(';').first rescue ''
site_cert = nil
if crt_file_path.present? || private_key_path.present?
site_cert = site_construct.site_cert

View File

@ -0,0 +1,68 @@
require 'net/ssh'
require 'pathname'
require 'fileutils'
namespace :create_site do
desc "Update multiple nginx setting"
task :update_multiple_nginx_setting,[:server_name] => :environment do |task,args|
Multithread.where(:key=>'update_multiple_nginx_setting').each{|thread| thread.destroy if (thread.status["status"] == "error" || thread.status["status"] == "finish")}
Multithread.where(:key=>'update_multiple_nginx_setting').destroy
@thread = Multithread.where(:key=>'update_multiple_nginx_setting').first
@type = "exec_all"
if @thread.nil?
begin
@thread = Multithread.create(:key=>'update_multiple_nginx_setting',:status=>{"infos"=>[],"status"=>"execing"})
if( args.server_name.nil? rescue true)
site_servers = SiteServer.all.where(:active=>true).to_a
else
site_servers = SiteServer.where(:server_name=>args.server_name).to_a
end
site_servers.each do |site_server|
next if (site_server.need_update_site_ids.count == 0)
@site_server = site_server
update_thread_infos("<span style='color: skyblue;'>"+@site_server.server_name+"</span>")
@password = @site_server.password
begin
begin
Net::SSH.start(@site_server.ip , @site_server.account , password: @site_server.password) do |ssh|
end
rescue Net::SSH::HostKeyMismatch
system("ssh-keygen -f \"$HOME/.ssh/known_hosts\" -R #{@site_server.ip}")
rescue Errno::ENOTTY
system("ssh-add \"$HOME/.ssh/id_rsa\"")
rescue => e
update_thread_infos(e.to_s)
next
end
Net::SSH.start(@site_server.ip , @site_server.account , password: @site_server.password) do |ssh|
need_update_sites = SiteConstruct.where(:id.in=>@site_server.need_update_site_ids).to_a
need_update_sites.each do |site_construct|
update_thread_infos("<span style='color: blueviolet;'>Changing "+site_construct.site_name+" nginx...</span>")
change_construct_nginx(ssh,site_construct,true)
end
@site_server.need_update_site_ids = []
@site_server.save
end
end
end
@thread.update(:status=>@thread.status.merge({"status"=>"finish"}))
rescue => e
puts [e,e.backtrace]
@thread.update(:status=>{"infos"=>@thread.status["infos"].push(e.to_s),"status"=>"error"})
end
end
end
def exec_ssh_command_by_sudo(session,command)
output = session.exec!("echo '#{@password}' | sudo -S #{command}")
# output = session.exec!("echo '#{@password}' | sudo -S -s #{command}")
if output.include?("sudo:") && output.include?("command not found")
output = session.exec!(command)
end
return output
end
def update_thread_infos(info)
puts info
@thread.status["infos"] = @thread.status["infos"].push(info)
@thread.save!
return @thread.status["infos"]
end
end