Class: Potluck::Nginx::SSL

Inherits:
Object
  • Object
show all
Defined in:
lib/potluck/nginx/ssl.rb

Overview

SSL-specific configuration for Nginx. Provides self-signed certificate generation for use in developemnt.

Constant Summary collapse

DEFAULT_CONFIG =
{
  'ssl_ciphers' => 'ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM'\
    '-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:D'\
    'HE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384',
  'ssl_prefer_server_ciphers' => 'off',
  'ssl_protocols' => 'TLSv1.2 TLSv1.3',
  'ssl_session_cache' => 'shared:SSL:10m',
  'ssl_session_tickets' => 'off',
  'ssl_session_timeout' => '1d',
  'add_header' => {
    repeat: true,
    'Strict-Transport-Security' => '\'max-age=31536000; includeSubDomains\' always',
  }.freeze,
}.freeze
CERT_DAYS =
365
CERT_RENEW_DAYS =
14

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(nginx, dir, host, crt_file: nil, key_file: nil, dhparam_file: nil, config: {}) ⇒ SSL

Creates a new instance. Providing no SSL files will cue generation of a self-signed certificate.

  • nginx - Nginx instance.

  • dir - Directory where SSL files are located or should be written to.

  • host - Name of the host for determining file names and generating a self-signed certificate.

  • crt_file - Path to the CRT file (optional).

  • key_file - Path to the KEY file (optional).

  • dhparam_file - Path to the DH parameters file (optional).

  • config - Nginx configuration hash (optional).



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/potluck/nginx/ssl.rb', line 44

def initialize(nginx, dir, host, crt_file: nil, key_file: nil, dhparam_file: nil, config: {})
  @nginx = nginx
  @dir = dir
  @host = host

  @auto_generated = !crt_file && !key_file && !dhparam_file

  if !@auto_generated && (!crt_file || !key_file || !dhparam_file)
    raise(ArgumentError, 'Must supply values for all three or none: crt_file, key_file, dhparam_file')
  end

  @csr_file = File.join(@dir, "#{@host}.csr").freeze
  @crt_file = crt_file || File.join(@dir, "#{@host}.crt").freeze
  @key_file = key_file || File.join(@dir, "#{@host}.key").freeze
  @dhparam_file = dhparam_file || File.join(@dir, 'dhparam.pem').freeze

  @config = Util.deep_merge({
    'ssl_certificate' => @crt_file,
    'ssl_certificate_key' => @key_file,
    'ssl_dhparam' => @dhparam_file,
    'ssl_stapling' => ('on' unless @auto_generated),
    'ssl_stapling_verify' => ('on' unless @auto_generated),
  }, DEFAULT_CONFIG, config)
end

Instance Attribute Details

#configObject (readonly)

Returns the value of attribute config.



31
32
33
# File 'lib/potluck/nginx/ssl.rb', line 31

def config
  @config
end

#crt_fileObject (readonly)

Returns the value of attribute crt_file.



31
32
33
# File 'lib/potluck/nginx/ssl.rb', line 31

def crt_file
  @crt_file
end

#csr_fileObject (readonly)

Returns the value of attribute csr_file.



31
32
33
# File 'lib/potluck/nginx/ssl.rb', line 31

def csr_file
  @csr_file
end

#dhparam_fileObject (readonly)

Returns the value of attribute dhparam_file.



31
32
33
# File 'lib/potluck/nginx/ssl.rb', line 31

def dhparam_file
  @dhparam_file
end

#key_fileObject (readonly)

Returns the value of attribute key_file.



31
32
33
# File 'lib/potluck/nginx/ssl.rb', line 31

def key_file
  @key_file
end

Instance Method Details

#ensure_filesObject

If SSL files were passed to SSL.new, does nothing. Otherwise checks if auto-generated SSL files exist and generates them if not. If they do exist, the expiration for the certificate is checked and the certificate regenerated if the expiration date is soon or in the past.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/potluck/nginx/ssl.rb', line 74

def ensure_files
  return if !@auto_generated || (
    File.exist?(@csr_file) &&
    File.exist?(@key_file) &&
    File.exist?(@crt_file) &&
    File.exist?(@dhparam_file) &&
    (Time.parse(
      @nginx.run("openssl x509 -enddate -noout -in #{@crt_file}").sub('notAfter=', '')
    ) - Time.now) >= CERT_RENEW_DAYS * 24 * 60 * 60
  )

  @nginx.log('Generating SSL files...')

  @nginx.run("openssl genrsa -out #{@key_file} 4096", capture_stderr: false)
  @nginx.run("openssl req -out #{@csr_file} -key #{@key_file} -new -sha256 -config /dev/stdin <<< "\
    "'#{openssl_config}'", capture_stderr: false)
  @nginx.run("openssl x509 -in #{@csr_file} -out #{@crt_file} -signkey #{@key_file} -days "\
    "#{CERT_DAYS} -req -sha256 -extensions req_ext -extfile /dev/stdin <<< '#{openssl_config}'",
    capture_stderr: false)
  @nginx.run("openssl dhparam -out #{@dhparam_file} 2048", capture_stderr: false)

  if IS_MACOS
    @nginx.log('Adding cert to keychain...')

    @nginx.run(
      "sudo security delete-certificate -t -c #{@host} 2>&1 || "\
      "sudo security delete-certificate -c #{@host} 2>&1 || :"
    )

    @nginx.run("sudo security add-trusted-cert -d -r trustRoot -k "\
      "/Library/Keychains/System.keychain #{@crt_file}")
  end
end