fluent-plugin-ssl-check 2.1.0 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.rubocop.yml +0 -3
- data/Gemfile.lock +1 -1
- data/README.md +51 -0
- data/fluent-plugin-ssl-check.gemspec +1 -1
- data/lib/fluent/plugin/in_ssl_check.rb +48 -11
- metadata +2 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: ab7b3def2e942f92cf1b8a1fa46f4c7bafe50fbee692334736e19574849e9911
         | 
| 4 | 
            +
              data.tar.gz: 5083966b749ba1407cdce60b33f8952dce4b300c5bd7e33496e1b1e02ae0814a
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: fe0d1bd834d8831a510df5754cb488d2e543fbca465bf3b344312b92f236278fbd75cf972efee0d0ffc45838f4550d09f81dba34fbcbf0445810a831b6fda365
         | 
| 7 | 
            +
              data.tar.gz: 8cef76bf6831eae2d1053a99699fc07f31077939bd3bc5ab28a402be4d709f9f4eec53ec9c14e954a01cefd5866a399e4d2ceeb373bd0e94d205023bf0375b3a
         | 
    
        data/.rubocop.yml
    CHANGED
    
    
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | @@ -2,6 +2,7 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            [Fluentd](https://fluentd.org/) input plugin to check ssl service.
         | 
| 4 4 |  | 
| 5 | 
            +
             | 
| 5 6 | 
             
            ## plugins
         | 
| 6 7 |  | 
| 7 8 | 
             
            ### in - ssl_check
         | 
| @@ -30,6 +31,10 @@ Options are: | |
| 30 31 | 
             
            * interval: check every X seconds
         | 
| 31 32 | 
             
            * ca_path: directory that contains CA files
         | 
| 32 33 | 
             
            * ca_file: specify a CA file directly
         | 
| 34 | 
            +
            * sni: want the sni support (true)
         | 
| 35 | 
            +
            * verify_mode: none or peer
         | 
| 36 | 
            +
            * cert: client cert for ssl connection
         | 
| 37 | 
            +
            * key: client key associated to client cert for ssl connection
         | 
| 33 38 | 
             
            * timeout: timeout for ssl check execution (5sec)
         | 
| 34 39 | 
             
            * log_events: emit log format (true)
         | 
| 35 40 | 
             
            * metric_events: emit metric format (false)
         | 
| @@ -38,6 +43,52 @@ Options are: | |
| 38 43 |  | 
| 39 44 | 
             
            If no port is specified with host, default port is 443.
         | 
| 40 45 |  | 
| 46 | 
            +
             | 
| 47 | 
            +
            ## output examples
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            ### log output example
         | 
| 50 | 
            +
             | 
| 51 | 
            +
            ``` json
         | 
| 52 | 
            +
            {
         | 
| 53 | 
            +
                "timestamp": "2023-10-03T09:59:41.580+02:00",
         | 
| 54 | 
            +
                "status": 1,
         | 
| 55 | 
            +
                "host": "www.google.fr",
         | 
| 56 | 
            +
                "port": 443,
         | 
| 57 | 
            +
                "ssl_version": "TLSv1.2",
         | 
| 58 | 
            +
                "ssl_dn": "/CN=*.google.fr",
         | 
| 59 | 
            +
                "ssl_not_after": "2023-11-27T08:25:08.000Z",
         | 
| 60 | 
            +
                "expire_in_days": 55,
         | 
| 61 | 
            +
                "serial": "4e79dbb13c6b57b309780da2d1edbda4"
         | 
| 62 | 
            +
            }
         | 
| 63 | 
            +
            ```
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            ### metric output example
         | 
| 66 | 
            +
             | 
| 67 | 
            +
            ``` json
         | 
| 68 | 
            +
            {
         | 
| 69 | 
            +
                "timestamp": "2023-10-03T10:06:21.417+02:00",
         | 
| 70 | 
            +
                "metric_name": "ssl_status",
         | 
| 71 | 
            +
                "metric_value": 1,
         | 
| 72 | 
            +
                "host": "www.google.fr",
         | 
| 73 | 
            +
                "port": 443,
         | 
| 74 | 
            +
                "ssl_dn": "/CN=*.google.fr",
         | 
| 75 | 
            +
                "ssl_version": "TLSv1.2",
         | 
| 76 | 
            +
                "ssl_not_after": "2023-11-27T08:25:08.000Z",
         | 
| 77 | 
            +
                "serial": "4e79dbb13c6b57b309780da2d1edbda4"
         | 
| 78 | 
            +
            }
         | 
| 79 | 
            +
             | 
| 80 | 
            +
            {
         | 
| 81 | 
            +
                "timestamp": "2023-10-03T10:06:21.417+02:00",
         | 
| 82 | 
            +
                "metric_name": "ssl_expirency",
         | 
| 83 | 
            +
                "metric_value": 55,
         | 
| 84 | 
            +
                "host": "www.google.fr",
         | 
| 85 | 
            +
                "port": 443,
         | 
| 86 | 
            +
                "ssl_dn": "/CN=*.google.fr",
         | 
| 87 | 
            +
                "serial": "4e79dbb13c6b57b309780da2d1edbda4"
         | 
| 88 | 
            +
            }
         | 
| 89 | 
            +
            ```
         | 
| 90 | 
            +
             | 
| 91 | 
            +
             | 
| 41 92 | 
             
            ## Installation
         | 
| 42 93 |  | 
| 43 94 | 
             
            Manual install, by executing:
         | 
| @@ -33,10 +33,10 @@ module Fluent | |
| 33 33 | 
             
                  Fluent::Plugin.register_input(NAME, self)
         | 
| 34 34 |  | 
| 35 35 | 
             
                  DEFAULT_TAG = NAME
         | 
| 36 | 
            -
                  DEFAULT_HOST = 'localhost'
         | 
| 37 36 | 
             
                  DEFAULT_PORT = 443
         | 
| 38 37 | 
             
                  DEFAULT_INTERVAL = 600
         | 
| 39 38 | 
             
                  DEFAULT_SNI = true
         | 
| 39 | 
            +
                  DEFAULT_VERIFY_MODE = :peer
         | 
| 40 40 | 
             
                  DEFAULT_TIMEOUT = 5
         | 
| 41 41 | 
             
                  DEFAULT_LOG_EVENTS = true
         | 
| 42 42 | 
             
                  DEFAULT_METRIC_EVENTS = false
         | 
| @@ -55,6 +55,12 @@ module Fluent | |
| 55 55 | 
             
                  config_param :ca_file, :string, default: nil
         | 
| 56 56 | 
             
                  desc 'SNI support'
         | 
| 57 57 | 
             
                  config_param :sni, :bool, default: DEFAULT_SNI
         | 
| 58 | 
            +
                  desc 'Verify mode'
         | 
| 59 | 
            +
                  config_param :verify_mode, :enum, list: %i[none peer], default: DEFAULT_VERIFY_MODE
         | 
| 60 | 
            +
                  desc 'Client Cert'
         | 
| 61 | 
            +
                  config_param :cert, :string, default: nil
         | 
| 62 | 
            +
                  desc 'Client Key'
         | 
| 63 | 
            +
                  config_param :key, :string, default: nil
         | 
| 58 64 |  | 
| 59 65 | 
             
                  desc 'Timeout for check'
         | 
| 60 66 | 
             
                  config_param :timeout, :integer, default: DEFAULT_TIMEOUT
         | 
| @@ -70,17 +76,22 @@ module Fluent | |
| 70 76 |  | 
| 71 77 | 
             
                  helpers :timer
         | 
| 72 78 |  | 
| 73 | 
            -
                  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 79 | 
            +
                  # rubocop:disable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Style/DoubleNegation
         | 
| 74 80 | 
             
                  def configure(conf)
         | 
| 75 81 | 
             
                    super
         | 
| 76 82 |  | 
| 77 83 | 
             
                    raise Fluent::ConfigError, 'tag can not be empty.' if !tag || tag.empty?
         | 
| 78 | 
            -
                    raise Fluent::ConfigError, 'hosts can not be empty.'  | 
| 84 | 
            +
                    raise Fluent::ConfigError, 'hosts can not be empty.' unless hosts
         | 
| 79 85 | 
             
                    raise Fluent::ConfigError, 'interval can not be < 1.' if !interval || interval < 1
         | 
| 80 86 | 
             
                    raise Fluent::ConfigError, 'ca_path should be a dir.' if ca_path && !File.directory?(ca_path)
         | 
| 81 87 | 
             
                    raise Fluent::ConfigError, 'ca_file should be a file.' if ca_file && !File.file?(ca_file)
         | 
| 88 | 
            +
                    raise Fluent::ConfigError, 'cert should be a file.' if cert && !File.file?(cert)
         | 
| 89 | 
            +
                    raise Fluent::ConfigError, 'key should be a file.' if key && !File.file?(key)
         | 
| 90 | 
            +
                    raise Fluent::ConfigError, 'cert and key should be specified.' if !!cert ^ !!key
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    log.warn("#{NAME}: hosts is empty, nothing to process") if hosts.empty?
         | 
| 82 93 | 
             
                  end
         | 
| 83 | 
            -
                  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
         | 
| 94 | 
            +
                  # rubocop:enable Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity, Metrics/AbcSize, Style/DoubleNegation
         | 
| 84 95 |  | 
| 85 96 | 
             
                  def start
         | 
| 86 97 | 
             
                    super
         | 
| @@ -107,7 +118,9 @@ module Fluent | |
| 107 118 | 
             
                    ssl_client = SslClient.new(
         | 
| 108 119 | 
             
                      host: host, port: port,
         | 
| 109 120 | 
             
                      ca_path: ca_path, ca_file: ca_file,
         | 
| 110 | 
            -
                      sni: sni,  | 
| 121 | 
            +
                      sni: sni, verify_mode: ssl_verify_mode,
         | 
| 122 | 
            +
                      cert: cert, key: key,
         | 
| 123 | 
            +
                      timeout: timeout
         | 
| 111 124 | 
             
                    )
         | 
| 112 125 | 
             
                    ssl_client.ssl_info
         | 
| 113 126 | 
             
                  end
         | 
| @@ -121,7 +134,8 @@ module Fluent | |
| 121 134 | 
             
                      'ssl_version' => ssl_info.ssl_version,
         | 
| 122 135 | 
             
                      'ssl_dn' => ssl_info.subject_s,
         | 
| 123 136 | 
             
                      'ssl_not_after' => ssl_info.not_after,
         | 
| 124 | 
            -
                      'expire_in_days' => ssl_info.expire_in_days
         | 
| 137 | 
            +
                      'expire_in_days' => ssl_info.expire_in_days,
         | 
| 138 | 
            +
                      'serial' => ssl_info.serial
         | 
| 125 139 | 
             
                    }
         | 
| 126 140 | 
             
                    record.update('error_class' => ssl_info.error_class) if ssl_info.error_class
         | 
| 127 141 | 
             
                    router.emit(tag, Fluent::EventTime.from_time(ssl_info.time), record)
         | 
| @@ -141,7 +155,8 @@ module Fluent | |
| 141 155 | 
             
                      "#{event_prefix}port" => ssl_info.port,
         | 
| 142 156 | 
             
                      "#{event_prefix}ssl_dn" => ssl_info.subject_s,
         | 
| 143 157 | 
             
                      "#{event_prefix}ssl_version" => ssl_info.ssl_version,
         | 
| 144 | 
            -
                      "#{event_prefix}ssl_not_after" => ssl_info.not_after
         | 
| 158 | 
            +
                      "#{event_prefix}ssl_not_after" => ssl_info.not_after,
         | 
| 159 | 
            +
                      "#{event_prefix}serial" => ssl_info.serial
         | 
| 145 160 | 
             
                    }
         | 
| 146 161 | 
             
                    router.emit(tag, Fluent::EventTime.from_time(ssl_info.time), record)
         | 
| 147 162 | 
             
                  end
         | 
| @@ -155,11 +170,20 @@ module Fluent | |
| 155 170 | 
             
                      'metric_value' => ssl_info.expire_in_days,
         | 
| 156 171 | 
             
                      "#{event_prefix}host" => ssl_info.host,
         | 
| 157 172 | 
             
                      "#{event_prefix}port" => ssl_info.port,
         | 
| 158 | 
            -
                      "#{event_prefix}ssl_dn" => ssl_info.subject_s
         | 
| 173 | 
            +
                      "#{event_prefix}ssl_dn" => ssl_info.subject_s,
         | 
| 174 | 
            +
                      "#{event_prefix}serial" => ssl_info.serial
         | 
| 159 175 | 
             
                    }
         | 
| 160 176 | 
             
                    router.emit(tag, Fluent::EventTime.from_time(ssl_info.time), record)
         | 
| 161 177 | 
             
                  end
         | 
| 162 178 |  | 
| 179 | 
            +
                  private
         | 
| 180 | 
            +
             | 
| 181 | 
            +
                  def ssl_verify_mode
         | 
| 182 | 
            +
                    return OpenSSL::SSL::VERIFY_PEER if verify_mode == :peer
         | 
| 183 | 
            +
             | 
| 184 | 
            +
                    OpenSSL::SSL::VERIFY_NONE
         | 
| 185 | 
            +
                  end
         | 
| 186 | 
            +
             | 
| 163 187 | 
             
                  # ssl info
         | 
| 164 188 | 
             
                  #  to encapsulate extracted ssl information
         | 
| 165 189 | 
             
                  class SslInfo
         | 
| @@ -198,6 +222,10 @@ module Fluent | |
| 198 222 | 
             
                      cert.not_after.iso8601(3)
         | 
| 199 223 | 
             
                    end
         | 
| 200 224 |  | 
| 225 | 
            +
                    def serial
         | 
| 226 | 
            +
                      cert&.serial&.to_s(16)&.downcase
         | 
| 227 | 
            +
                    end
         | 
| 228 | 
            +
             | 
| 201 229 | 
             
                    def status
         | 
| 202 230 | 
             
                      return KO if error
         | 
| 203 231 |  | 
| @@ -214,16 +242,23 @@ module Fluent | |
| 214 242 | 
             
                  # ssl client
         | 
| 215 243 | 
             
                  #  to check ssl status
         | 
| 216 244 | 
             
                  class SslClient
         | 
| 217 | 
            -
                    attr_reader :host, :port, :ca_path, :ca_file, :sni, :timeout
         | 
| 245 | 
            +
                    attr_reader :host, :port, :ca_path, :ca_file, :sni, :verify_mode, :cert, :key, :timeout
         | 
| 218 246 |  | 
| 219 | 
            -
                     | 
| 247 | 
            +
                    # rubocop:disable Metrics/ParameterLists
         | 
| 248 | 
            +
                    def initialize(host:, port:, ca_path: nil, ca_file: nil, sni: true, verify_mode: OpenSSL::SSL::VERIFY_PEER,
         | 
| 249 | 
            +
                                   cert: nil, key: nil,
         | 
| 250 | 
            +
                                   timeout: 5)
         | 
| 220 251 | 
             
                      @host = host
         | 
| 221 252 | 
             
                      @port = port
         | 
| 222 253 | 
             
                      @ca_path = ca_path
         | 
| 223 254 | 
             
                      @ca_file = ca_file
         | 
| 224 255 | 
             
                      @sni = sni
         | 
| 256 | 
            +
                      @verify_mode = verify_mode
         | 
| 257 | 
            +
                      @cert = cert
         | 
| 258 | 
            +
                      @key = key
         | 
| 225 259 | 
             
                      @timeout = timeout
         | 
| 226 260 | 
             
                    end
         | 
| 261 | 
            +
                    # rubocop:enable Metrics/ParameterLists
         | 
| 227 262 |  | 
| 228 263 | 
             
                    def ssl_info
         | 
| 229 264 | 
             
                      info = SslInfo.new(host: host, port: port)
         | 
| @@ -257,10 +292,12 @@ module Fluent | |
| 257 292 |  | 
| 258 293 | 
             
                    def ssl_context
         | 
| 259 294 | 
             
                      OpenSSL::SSL::SSLContext.new.tap do |ssl_context|
         | 
| 260 | 
            -
                        ssl_context.verify_mode =  | 
| 295 | 
            +
                        ssl_context.verify_mode = verify_mode
         | 
| 261 296 | 
             
                        ssl_context.cert_store = store
         | 
| 262 297 | 
             
                        ssl_context.min_version = nil
         | 
| 263 298 | 
             
                        ssl_context.max_version = OpenSSL::SSL::TLS1_2_VERSION
         | 
| 299 | 
            +
                        ssl_context.cert = OpenSSL::X509::Certificate.new(File.open(cert)) if cert
         | 
| 300 | 
            +
                        ssl_context.key = OpenSSL::PKey::RSA.new(File.open(key)) if key
         | 
| 264 301 | 
             
                      end
         | 
| 265 302 | 
             
                    end
         | 
| 266 303 | 
             
                  end
         | 
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: fluent-plugin-ssl-check
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 2. | 
| 4 | 
            +
              version: 2.3.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Thomas Tych
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2023- | 
| 11 | 
            +
            date: 2023-10-03 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: bump
         |