diff --git a/Gemfile-2.3 b/Gemfile-2.3 new file mode 100644 index 0000000..4951e91 --- /dev/null +++ b/Gemfile-2.3 @@ -0,0 +1,13 @@ +source 'https://rubygems.org' + +gem 'dotenv' + +gem 'sidekiq' +gem 'sidekiq-workflow', git: 'https://git.imirhil.fr/aeris/sidekiq-workflow.git', branch: :master + +gem 'simpleidn' +gem 'cryptcheck', '~> 2.0.0', path: '../engine' + +group :development do + gem 'amazing_print' +end diff --git a/Gemfile-2.3.lock b/Gemfile-2.3.lock new file mode 100644 index 0000000..e90bae5 --- /dev/null +++ b/Gemfile-2.3.lock @@ -0,0 +1,85 @@ +GIT + remote: https://git.imirhil.fr/aeris/sidekiq-workflow.git + revision: 9600075c17e83f2013f0b7d6e35698bea6b0ee40 + branch: master + specs: + sidekiq-workflow (0.0.0) + colorize (~> 0.8) + redis (~> 4.3) + redlock (~> 1.2) + sidekiq + terminal-table (~> 3.0) + +PATH + remote: ../engine + specs: + cryptcheck (2.0.0) + amazing_print + colorize + httparty + nokogiri + parallel + ruby-progressbar + thor + +GEM + remote: https://rubygems.org/ + specs: + amazing_print (1.2.1) + colorize (0.8.1) + connection_pool (2.2.5) + dotenv (2.7.6) + foreman (0.87.2) + http_accept_language (2.1.1) + httparty (0.20.0) + mime-types (~> 3.0) + multi_xml (>= 0.5.2) + mime-types (3.4.1) + mime-types-data (~> 3.2015) + mime-types-data (3.2022.0105) + mini_portile2 (2.4.0) + multi_xml (0.6.0) + nokogiri (1.10.10) + mini_portile2 (~> 2.4.0) + parallel (1.19.2) + pg (1.2.3) + rack (2.0.9) + rack-protection (2.2.0) + rack + recursive-open-struct (1.1.3) + redis (4.4.0) + redlock (1.2.2) + redis (>= 3.0.0, < 5.0) + ruby-progressbar (1.11.0) + sidekiq (5.2.8) + connection_pool (~> 2.2, >= 2.2.2) + rack (< 2.1.0) + rack-protection (>= 1.5.0) + redis (>= 3.3.5, < 5) + simpleidn (0.2.1) + unf (~> 0.1.4) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) + thor (1.2.1) + unf (0.1.4) + unf_ext + unf_ext (0.0.8.1) + unicode-display_width (2.1.0) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + amazing_print + cryptcheck (~> 2.0.0)! + dotenv + foreman + http_accept_language + pg + recursive-open-struct + sidekiq + sidekiq-workflow! + simpleidn + +BUNDLED WITH + 2.3.9 diff --git a/Procfile b/Procfile new file mode 100644 index 0000000..cd299bb --- /dev/null +++ b/Procfile @@ -0,0 +1,4 @@ +web: bundle exec guard -i +webpack: bundle exec webpack-dev-server +sidekiq: bundle exec sidekiq -q default +sidekiq_1_0: BUNDLE_GEMFILE=Gemfile-2.3 bin/sidekiq 1.0 -q tls_1_0 diff --git a/app/controllers/check_controller.rb b/app/controllers/check_controller.rb index ffb8412..2e890cb 100644 --- a/app/controllers/check_controller.rb +++ b/app/controllers/check_controller.rb @@ -1,59 +1,66 @@ class CheckController < ApplicationController - before_action :check_host, except: %i(index) - helper_method :tls_type, :type - - def show - enqueue_host unless @analysis - @host = SimpleIDN.to_unicode @host - respond_to do |format| - format.html do - return render :processing if @analysis.pending - @result = @analysis.result.collect { |r| RecursiveOpenStruct.new r, recurse_over_arrays: true } - end - format.json { render json: @analysis } - end - end - - def refresh - unless @analysis.pending - if Rails.env == 'production' - refresh_allowed = @analysis.updated_at + Rails.configuration.refresh_delay - if Time.now < refresh_allowed - flash[:warning] = "Merci d’attendre au moins #{l refresh_allowed} pour rafraîchir" - return redirect_to action: :show, id: @host - end - end - enqueue_host - end - redirect_to action: :show - end - - protected - def default_args - end - - def enqueue_host - @analysis = Analysis.pending! self.type, @host, @args - self.worker.perform_async @analysis.host, *@analysis.args - end - - def check_host - @id = params[:id] - - if @id.end_with? '.json' - @id = @id.sub /\.json$/, '' - request.format = :json - end - - @host, @args = @id.split ':' - @host = SimpleIDN.to_ascii @host.downcase - if /[^a-zA-Z0-9.-]/ =~ @host - flash[:danger] = "Hôte #{@host} invalide" - redirect_to action: :index - return false - end - @args ||= default_args - - @analysis = Analysis[self.type, @host, @args] - end + before_action :check_host, except: %i[index] + before_action :exclude_robots_indexing + helper_method :tls_type, :type + + def show + enqueue_host unless @analysis + @host = SimpleIDN.to_unicode @host + respond_to do |format| + format.html do + return render :processing if @analysis.pending + @result = @analysis.result.collect { |r| RecursiveOpenStruct.new r, recurse_over_arrays: true } + end + format.json { render json: @analysis } + end + end + + def refresh + unless @analysis.pending + if Rails.env == 'production' + refresh_allowed = @analysis.updated_at + Rails.configuration.refresh_delay + if Time.now < refresh_allowed + flash[:warning] = "Merci d’attendre au moins #{l refresh_allowed} pour rafraîchir" + return redirect_to action: :show, id: @host + end + end + enqueue_host + end + redirect_to action: :show + end + + protected + + def exclude_robots_indexing + response.set_header 'X-Robots-Tag', + %i[none noarchive nosnippet notranslate noimageindex].join(',') + end + + def default_args + end + + def enqueue_host + @analysis = Analysis.pending! self.type, @host, @args + CheckWorkflow.start! self.type, @analysis.host, *@analysis.args + end + + def check_host + @id = params[:id] + + if @id.end_with? '.json' + @id = @id.sub /\.json$/, '' + request.format = :json + end + + @host, @args = @id.split ':' + @host = SimpleIDN.to_ascii @host.downcase + if /[^a-zA-Z0-9.-]/ =~ @host + flash[:danger] = "Hôte #{@host} invalide" + redirect_to action: :index + return false + end + @args ||= default_args + + @analysis = Analysis[self.type, @host, @args] + end end diff --git a/app/controllers/https_controller.rb b/app/controllers/https_controller.rb index 994d580..1ebf6e8 100644 --- a/app/controllers/https_controller.rb +++ b/app/controllers/https_controller.rb @@ -4,10 +4,6 @@ class HttpsController < CheckController :https end - def worker - HTTPSWorker - end - def tls_type 'HTTPS' end diff --git a/app/controllers/smtp_controller.rb b/app/controllers/smtp_controller.rb index e36cafb..42ab94d 100644 --- a/app/controllers/smtp_controller.rb +++ b/app/controllers/smtp_controller.rb @@ -4,10 +4,6 @@ class SmtpController < CheckController :smtp end - def worker - SMTPWorker - end - def tls_type 'STARTTLS' end diff --git a/app/controllers/ssh_controller.rb b/app/controllers/ssh_controller.rb index ca4ac20..b9c13b0 100644 --- a/app/controllers/ssh_controller.rb +++ b/app/controllers/ssh_controller.rb @@ -4,10 +4,6 @@ class SshController < CheckController :ssh end - def worker - SSHWorker - end - def tls_type 'SSH' end diff --git a/app/controllers/tls_controller.rb b/app/controllers/tls_controller.rb index 3c07312..e98d98f 100644 --- a/app/controllers/tls_controller.rb +++ b/app/controllers/tls_controller.rb @@ -4,10 +4,6 @@ class TlsController < CheckController :tls end - def worker - TLSWorker - end - def tls_type 'TLS' end diff --git a/app/controllers/xmpp_controller.rb b/app/controllers/xmpp_controller.rb index 4e81cbe..547a7bf 100644 --- a/app/controllers/xmpp_controller.rb +++ b/app/controllers/xmpp_controller.rb @@ -5,10 +5,6 @@ class XmppController < CheckController :xmpp end - def worker - XMPPWorker - end - def tls_type 'STARTTLS' end diff --git a/app/jobs/check_job.rb b/app/jobs/check_job.rb new file mode 100644 index 0000000..01aa304 --- /dev/null +++ b/app/jobs/check_job.rb @@ -0,0 +1,30 @@ +require 'simpleidn' + +class CheckJob + include Sidekiq::Workflow::Worker + sidekiq_options queue: :tls_1_0, retry: false + + def perform(type, host, *args, **_) + host = SimpleIDN.to_ascii host.downcase + result = self.analyze type, host, *args + args = nil if args.empty? + set_payload({ type: type, host: host, args: args, result: result }) + end + + def analyze(type, *args) + case type.to_sym + when :https + CryptCheck::Tls::Https.analyze *args + when :smtp + CryptCheck::Tls::Smtp.analyze *args + when :xmpp + CryptCheck::Tls::Xmpp.analyze *args + when :tls + CryptCheck::Tls.analyze *args + when :ssh + CryptCheck::Ssh.analyze *args + else + raise "Unsupported analysis #{type}" + end + end +end diff --git a/app/jobs/persist_job.rb b/app/jobs/persist_job.rb new file mode 100644 index 0000000..822086b --- /dev/null +++ b/app/jobs/persist_job.rb @@ -0,0 +1,13 @@ +class PersistJob + include Sidekiq::Workflow::Worker + sidekiq_options retry: false + + def perform(*_, **_) + result = self.get_payload CheckJob, :json + type = result.fetch 'type' + host = result.fetch 'host' + args = result.fetch 'args' + result = result.fetch 'result' + Analysis.post! type, host, args, result + end +end diff --git a/app/workers/check_worker.rb b/app/workers/check_worker.rb deleted file mode 100644 index bb1c9a8..0000000 --- a/app/workers/check_worker.rb +++ /dev/null @@ -1,11 +0,0 @@ -class CheckWorker - include Sidekiq::Worker - sidekiq_options retry: false - - def perform(host, *args) - host = SimpleIDN.to_ascii host.downcase - result = self.analyze host, *args - args = nil if args.empty? - Analysis.post! self.type, host, args, result - end -end diff --git a/app/workers/https_worker.rb b/app/workers/https_worker.rb deleted file mode 100644 index e5047c0..0000000 --- a/app/workers/https_worker.rb +++ /dev/null @@ -1,12 +0,0 @@ -class HTTPSWorker < CheckWorker - sidekiq_options retry: false - - protected - def analyze(host, port) - CryptCheck::Tls::Https.analyze host, port - end - - def type - :https - end -end diff --git a/app/workers/smtp_worker.rb b/app/workers/smtp_worker.rb deleted file mode 100644 index 3befdc8..0000000 --- a/app/workers/smtp_worker.rb +++ /dev/null @@ -1,12 +0,0 @@ -class SMTPWorker < CheckWorker - sidekiq_options retry: false - - protected - def analyze(host) - CryptCheck::Tls::Smtp.analyze host - end - - def type - :smtp - end -end diff --git a/app/workers/ssh_worker.rb b/app/workers/ssh_worker.rb deleted file mode 100644 index 9579ca6..0000000 --- a/app/workers/ssh_worker.rb +++ /dev/null @@ -1,26 +0,0 @@ -class SSHWorker < CheckWorker - sidekiq_options retry: false - - protected - def analyze(host, port=22) - CryptCheck::Ssh.analyze host, port - end - - def type - :ssh - end - - def to_json(server) - { - kex: server.kex, - encryption: server.encryption, - hmac: server.hmac, - compression: server.compression, - key_: server.key - } - end - - def grade_to_json(grade) - nil - end -end diff --git a/app/workers/tls_worker.rb b/app/workers/tls_worker.rb deleted file mode 100644 index f3ebbf1..0000000 --- a/app/workers/tls_worker.rb +++ /dev/null @@ -1,12 +0,0 @@ -class TLSWorker < CheckWorker - sidekiq_options retry: false - - protected - def analyze(host, port) - CryptCheck::Tls.analyze host, port - end - - def type - :tls - end -end diff --git a/app/workers/xmpp_worker.rb b/app/workers/xmpp_worker.rb deleted file mode 100644 index 35477b2..0000000 --- a/app/workers/xmpp_worker.rb +++ /dev/null @@ -1,12 +0,0 @@ -class XMPPWorker < CheckWorker - sidekiq_options retry: false - - protected - def analyze(host, type) - CryptCheck::Tls::Xmpp.analyze host, type - end - - def type - :xmpp - end -end diff --git a/app/workflows/check_workflow.rb b/app/workflows/check_workflow.rb new file mode 100644 index 0000000..7d532d1 --- /dev/null +++ b/app/workflows/check_workflow.rb @@ -0,0 +1,6 @@ +class CheckWorkflow < Sidekiq::Workflow + def configure(*args, **kwargs) + check = job CheckJob, *args, **kwargs + job PersistJob, after: check + end +end diff --git a/bin/sidekiq b/bin/sidekiq index 948103d..af663c3 100755 --- a/bin/sidekiq +++ b/bin/sidekiq @@ -1,20 +1,8 @@ -#!/usr/bin/env ruby -$:.unshift File.expand_path File.join File.dirname(__FILE__), '../../cryptcheck/lib' -require 'rubygems' -require 'bundler/setup' - -$TESTING = false -$CELLULOID_DEBUG = false - -require 'sidekiq/cli' - -begin - cli = Sidekiq::CLI.instance - cli.parse - cli.run -rescue => e - raise e if $DEBUG - STDERR.puts e.message - STDERR.puts e.backtrace.join("\n") - exit 1 -end +#!/usr/bin/env bash +set -e +DIR="$(dirname "$(readlink -e "${BASH_SOURCE[0]}")")" +RAILS_DIR="$(readlink -f "$DIR/..")" +ENGINE_DIR="$(readlink -f "$RAILS_DIR/../engine")" +eval "$("$ENGINE_DIR/bin/load-rbenv" "$1")" +shift +bundle exec sidekiq -r "$RAILS_DIR/config/sidekiq.rb" $* diff --git a/config/initializers/redis.rb b/config/initializers/redis.rb new file mode 100644 index 0000000..0b19258 --- /dev/null +++ b/config/initializers/redis.rb @@ -0,0 +1,3 @@ +if redis_url = ENV['REDIS_URL'] + Sidekiq::Workflow.configure url: redis_url +end diff --git a/config/sidekiq.rb b/config/sidekiq.rb new file mode 100755 index 0000000..2c4ec30 --- /dev/null +++ b/config/sidekiq.rb @@ -0,0 +1,29 @@ +#!/usr/bin/env ruby +require 'rubygems' +require 'bundler/setup' +env = ENV.fetch 'RAILS_ENV', 'development' +Bundler.require env + +require 'dotenv' +Dotenv.load ".env.#{ENV['RAILS_ENV']}.local", ".env.#{ENV['RAILS_ENV']}", '.env.local', '.env' + +class Hash + def compact + select { |_, value| !value.nil? } + end +end + +require 'redis' +Redis.exists_returns_integer = true +redis_url = ENV.fetch 'REDIS_URL' + +require 'sidekiq' +Sidekiq.configure_server { |c| c.redis = { url: redis_url } } +Sidekiq.configure_client { |c| c.redis = { url: redis_url } } +require 'sidekiq/workflow' +redis_url = ENV['REDIS_URL'] +Sidekiq::Workflow.configure url: redis_url + +require 'cryptcheck' +load File.join __dir__, '../app/jobs/check_job.rb' +load File.join __dir__, '../app/jobs/persist_job.rb'