diff --git a/app/controllers/admin/site_panel_controller.rb b/app/controllers/admin/site_panel_controller.rb index 63c2ff0..ec43533 100644 --- a/app/controllers/admin/site_panel_controller.rb +++ b/app/controllers/admin/site_panel_controller.rb @@ -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 \ No newline at end of file diff --git a/app/models/site_cert.rb b/app/models/site_cert.rb index 5cbb407..e2e25d0 100644 --- a/app/models/site_cert.rb +++ b/app/models/site_cert.rb @@ -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 \ No newline at end of file diff --git a/app/models/site_construct.rb b/app/models/site_construct.rb index 36c17f7..58e20ad 100644 --- a/app/models/site_construct.rb +++ b/app/models/site_construct.rb @@ -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 \ No newline at end of file diff --git a/app/models/site_server.rb b/app/models/site_server.rb index 0fb31d1..16a43c9 100644 --- a/app/models/site_server.rb +++ b/app/models/site_server.rb @@ -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 \ No newline at end of file diff --git a/app/views/admin/site_panel/_server_manager_index.html.erb b/app/views/admin/site_panel/_server_manager_index.html.erb index 6fc86ff..e41ed75 100644 --- a/app/views/admin/site_panel/_server_manager_index.html.erb +++ b/app/views/admin/site_panel/_server_manager_index.html.erb @@ -40,6 +40,9 @@ <% if site_server.has_certbot%> <%= t("client_management.alreay_install") %> + <% if site_server.active %> + " data-id="<%=site_server.id.to_s%>"><%= t("client_management.install_certbot_certificates_in_batches") %> + <% end %> <% else %> <%= t("client_management.not_install") %> <% if site_server.active %> @@ -48,6 +51,9 @@ <% end %> + <% if site_server.need_update_site_ids.count != 0 %> + <%=t("client_management.update_nginx_settings")%> + <% end %> "><%=t(:edit)%> ';}"><%=t(:remove)%> "><%= t('client_management.see_sites') %> @@ -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"); + }); + }) }) \ No newline at end of file diff --git a/app/views/admin/site_panel/_sites_list_table.html.erb b/app/views/admin/site_panel/_sites_list_table.html.erb index 71946aa..4f9b814 100644 --- a/app/views/admin/site_panel/_sites_list_table.html.erb +++ b/app/views/admin/site_panel/_sites_list_table.html.erb @@ -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); }); } diff --git a/app/views/admin/site_panel/edit_server_info.html.erb b/app/views/admin/site_panel/edit_server_info.html.erb index e2606ba..4911ef9 100644 --- a/app/views/admin/site_panel/edit_server_info.html.erb +++ b/app/views/admin/site_panel/edit_server_info.html.erb @@ -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 = ')" title="*.example.com (<%=t("client_management.domain_hint")%>)">'; + tp2.append('
'); + tp2.append(tp3); + tp2.append('<%= delete_domain_button %>') + } function delete_domain(ele){ $(ele).prev('input').remove() $(ele).prev('br').remove() @@ -51,9 +59,9 @@ <% unless @site_server.new_record? %>
- +
- <%= f.check_box :active %> + <%= f.check_box :active,:id=>"active_server" %>
<% end %> @@ -87,6 +95,29 @@ %> +
+ +
+ <%= f.check_box :enable_redirect_default_domain,:id=>"enable_redirect_default_domain" %> +
+
+
+ +
+
+ <% + @site_server.default_domain_names.each_with_index do |domain_name,i| %> + <%= '
'.html_safe if i !=0 %> + <%= text_field_tag "site_server[default_domain_names][]",domain_name,{:id=>nil,:class=>"domain_name"} %> + <%= delete_domain_button %> + <% end %> +
+ <%= f.button "#{t('add')} domain", class: 'btn btn-info', + :onclick=> "add_default_domain(this)", + :type => 'button' + %> +
+
diff --git a/app/views/admin/site_panel/see_detail_for_created_site.html.erb b/app/views/admin/site_panel/see_detail_for_created_site.html.erb index eba7828..6b0edbe 100644 --- a/app/views/admin/site_panel/see_detail_for_created_site.html.erb +++ b/app/views/admin/site_panel/see_detail_for_created_site.html.erb @@ -6,11 +6,20 @@
<%=@site.server_type%>
+
+ +
+ <% %> + <%= 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" %> +
+
+ <%= hidden_field_tag "site_construct[default_domain_idx]", "-1" %>
<% @site.domain_name.split(' ').each_with_index do |domain_name,i| %>
+ <%= 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 %>
-
<%=@site.path%>
+
<%=@site.full_site_path%>
@@ -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("") $('.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(""); diff --git a/config/locales/en.yml b/config/locales/en.yml index 782538d..8c4f7b3 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -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 diff --git a/config/locales/zh_tw.yml b/config/locales/zh_tw.yml index 92a4290..db2db3d 100644 --- a/config/locales/zh_tw.yml +++ b/config/locales/zh_tw.yml @@ -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: 啟用 diff --git a/config/routes.rb b/config/routes.rb index b3bc57f..a96a8dc 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -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' diff --git a/lib/tasks/change_site_cert.rake b/lib/tasks/change_site_cert.rake index e719179..0dafa4c 100644 --- a/lib/tasks/change_site_cert.rake +++ b/lib/tasks/change_site_cert.rake @@ -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 \ No newline at end of file diff --git a/lib/tasks/change_site_server_name.rake b/lib/tasks/change_site_server_name.rake index dcf25d8..e1a92b9 100644 --- a/lib/tasks/change_site_server_name.rake +++ b/lib/tasks/change_site_server_name.rake @@ -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]+/,'') diff --git a/lib/tasks/detect_sites.rake b/lib/tasks/detect_sites.rake index b3b60bf..4b8062c 100644 --- a/lib/tasks/detect_sites.rake +++ b/lib/tasks/detect_sites.rake @@ -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 diff --git a/lib/tasks/update_multiple_nginx_setting.rake b/lib/tasks/update_multiple_nginx_setting.rake new file mode 100644 index 0000000..6bac19b --- /dev/null +++ b/lib/tasks/update_multiple_nginx_setting.rake @@ -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(""+@site_server.server_name+"") + @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("Changing "+site_construct.site_name+" nginx...") + 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 \ No newline at end of file