class Heroku::Command::Certs

manage ssl endpoints for an app

Constants

SSL_DOCTOR

Public Instance Methods

add() click to toggle source

certs:add CRT KEY

Add an ssl endpoint to an app.

--bypass  # bypass the trust chain completion step
# File lib/heroku/command/certs.rb, line 71
def add
  crt, key = read_crt_and_key
  endpoint = action("Adding SSL Endpoint to #{app}") { heroku.ssl_endpoint_add(app, crt, key) }
  display_warnings(endpoint)
  display "#{app} now served by #{endpoint['cname']}"
  display "Certificate details:"
  display_certificate_info(endpoint)
rescue UsageError
  fail("Usage: pogo certs:add CRT KEY\nMust specify CRT and KEY to add cert.")
end
chain() click to toggle source

certs:chain CRT [CRT …]

Print the ordered and complete chain for the given certificate.

Optional intermediate certificates may be given too, and will be used during chain resolution.

# File lib/heroku/command/certs.rb, line 45
def chain
  puts read_crt_through_ssl_doctor
rescue UsageError
  fail("Usage: pogo certs:chain CRT [CRT ...]\nMust specify at least one certificate file.")
end
index() click to toggle source

certs

List ssl endpoints for an app.

# File lib/heroku/command/certs.rb, line 15
def index
  endpoints = heroku.ssl_endpoint_list(app)

  if endpoints.empty?
    display "#{app} has no SSL Endpoints."
    display "Use `pogo certs:add CRT KEY` to add one."
  else
    endpoints.map! do |endpoint|
      {
        'cname'       => endpoint['cname'],
        'domains'     => endpoint['ssl_cert']['cert_domains'].join(', '),
        'expires_at'  => format_date(endpoint['ssl_cert']['expires_at']),
        'ca_signed?'  => endpoint['ssl_cert']['ca_signed?'].to_s.capitalize
      }
    end
    display_table(
      endpoints,
      %w( cname domains expires_at ca_signed? ),
      [ "Endpoint", "Common Name(s)", "Expires", "Trusted" ]
    )
  end
end
info() click to toggle source

certs:info

Show certificate information for an ssl endpoint.

# File lib/heroku/command/certs.rb, line 103
def info
  cname = options[:endpoint] || current_endpoint
  endpoint = action("Fetching SSL Endpoint #{cname} info for #{app}") do
    heroku.ssl_endpoint_info(app, cname)
  end

  display "Certificate details:"
  display_certificate_info(endpoint)
end
key() click to toggle source

certs:key CRT KEY [KEY …]

Print the correct key for the given certificate.

You must pass one single certificate, and one or more keys. The first key that signs the certificate will be printed back.

# File lib/heroku/command/certs.rb, line 58
def key
  crt, key = read_crt_and_key_through_ssl_doctor("Testing for signing key")
  puts key
rescue UsageError
  fail("Usage: pogo certs:key CRT KEY [KEY ...]\nMust specify one certificate file and at least one key file.")
end
remove() click to toggle source

certs:remove

Remove an SSL Endpoint from an app.

# File lib/heroku/command/certs.rb, line 117
def remove
  cname = options[:endpoint] || current_endpoint
  action("Removing SSL Endpoint #{cname} from #{app}") do
    heroku.ssl_endpoint_remove(app, cname)
  end
  display "NOTE: Billing is still active. Remove SSL Endpoint add-on to stop billing."
end
rollback() click to toggle source

certs:rollback

Rollback an SSL Endpoint for an app.

# File lib/heroku/command/certs.rb, line 129
def rollback
  cname = options[:endpoint] || current_endpoint

  endpoint = action("Rolling back SSL Endpoint #{cname} for #{app}") do
    heroku.ssl_endpoint_rollback(app, cname)
  end

  display "New active certificate details:"
  display_certificate_info(endpoint)
end
update() click to toggle source

certs:update CRT KEY

Update an SSL Endpoint on an app.

--bypass  # bypass the trust chain completion step
# File lib/heroku/command/certs.rb, line 88
def update
  crt, key = read_crt_and_key
  cname    = options[:endpoint] || current_endpoint
  endpoint = action("Updating SSL Endpoint #{cname} for #{app}") { heroku.ssl_endpoint_update(app, cname, crt, key) }
  display_warnings(endpoint)
  display "Updated certificate details:"
  display_certificate_info(endpoint)
rescue UsageError
  fail("Usage: pogo certs:update CRT KEY\nMust specify CRT and KEY to update cert.")
end

Private Instance Methods

current_endpoint() click to toggle source
# File lib/heroku/command/certs.rb, line 142
def current_endpoint
  endpoint = heroku.ssl_endpoint_list(app).first || error("#{app} has no SSL Endpoints.")
  endpoint["cname"]
end
display(msg = "", new_line = true) click to toggle source
Calls superclass method Heroku::Helpers#display
# File lib/heroku/command/certs.rb, line 174
def display(msg = "", new_line = true)
  super if $stdout.tty?
end
display_certificate_info(endpoint) click to toggle source
# File lib/heroku/command/certs.rb, line 147
def display_certificate_info(endpoint)
  data = {
    'Common Name(s)'  => endpoint['ssl_cert']['cert_domains'],
    'Expires At'      => format_date(endpoint['ssl_cert']['expires_at']),
    'Issuer'          => endpoint['ssl_cert']['issuer'],
    'Starts At'       => format_date(endpoint['ssl_cert']['starts_at']),
    'Subject'         => endpoint['ssl_cert']['subject']
  }
  styled_hash(data)

  if endpoint["ssl_cert"]["ca_signed?"]
    display "SSL certificate is verified by a root authority."
  elsif endpoint["issuer"] == endpoint["subject"]
    display "SSL certificate is self signed."
  else
    display "SSL certificate is not trusted."
  end
end
display_warnings(endpoint) click to toggle source
# File lib/heroku/command/certs.rb, line 166
def display_warnings(endpoint)
  if endpoint["warnings"]
    endpoint["warnings"].each do |field, warning|
      display "WARNING: #{field} #{warning}"
    end
  end
end
post_to_ssl_doctor(path, action_text = nil) click to toggle source
# File lib/heroku/command/certs.rb, line 178
def post_to_ssl_doctor(path, action_text = nil)
  raise UsageError if args.size < 1
  action_text ||= "Resolving trust chain"
  action(action_text) do
    input = args.map { |arg| File.read(arg) rescue error("Unable to read #{args[0]} file") }.join("\n")
    SSL_DOCTOR.post(:path => path, :body => input, :headers => {'Content-Type' => 'application/octet-stream'}, :expects => 200).body
  end
rescue Excon::Errors::BadRequest, Excon::Errors::UnprocessableEntity => e
  error(e.response.body)
end
read_crt_and_key() click to toggle source
# File lib/heroku/command/certs.rb, line 206
def read_crt_and_key
  options[:bypass] ? read_crt_and_key_bypassing_ssl_doctor : read_crt_and_key_through_ssl_doctor
end
read_crt_and_key_bypassing_ssl_doctor() click to toggle source
# File lib/heroku/command/certs.rb, line 199
def read_crt_and_key_bypassing_ssl_doctor
  raise UsageError if args.size != 2
  crt = File.read(args[0]) rescue error("Unable to read #{args[0]} CRT")
  key = File.read(args[1]) rescue error("Unable to read #{args[1]} KEY")
  [crt, key]
end
read_crt_and_key_through_ssl_doctor(action_text = nil) click to toggle source
# File lib/heroku/command/certs.rb, line 189
def read_crt_and_key_through_ssl_doctor(action_text = nil)
  crt_and_key = post_to_ssl_doctor("resolve-chain-and-key", action_text)
  #Heroku::OkJson.decode(crt_and_key).values_at("pem", "key")
  read_crt_and_key_bypassing_ssl_doctor
end
read_crt_through_ssl_doctor(action_text = nil) click to toggle source
# File lib/heroku/command/certs.rb, line 195
def read_crt_through_ssl_doctor(action_text = nil)
  post_to_ssl_doctor("resolve-chain", action_text)
end