SSL, Django Development Server and Chrome

I am developing a Django site that uses Stripe. Even for testing, Stripe requires HTTPS. In the past, I used django-sslserver version 0.19 and ignored the complaining Chrome made about the certificate being self signed. Today (Sept 2017), none of that worked.

First thing I did was upgrade django-sslserver to 0.20. This crashed with an error related to:

ssl.PROTOCOL_TLSv1_2

It turns out ssl is built into Python and that constant is not defined in version 2.7.6. Reverting back to django-sslserver to 0.19 solved that problem.

Next, Chrome/Stripe will no longer let you ignore the SSL certificate warnings. This blog post by Alexander Zeitler does a pretty good job explaining how solve this problem. If you run into this problem:

error on line -1 of /dev/fd/11
140736435860488:error:02001009:system library:fopen:Bad file descriptor:bss_file.c:175:fopen('/dev/fd/11','rb')
140736435860488:error:2006D002:BIO routines:BIO_new_file:system lib:bss_file.c:184:
140736435860488:error:0E078002:configuration file routines:DEF_LOAD:system lib:conf_def.c:197:

remove sudo from createselfsignedcertificate.sh and run the script using sudo.

When all of that is done, you need to tell Chrome to trust your Certificate Authority by going to “Advanced Settings -> Manage Certificates”, then “Authorities/Import. Select the rootCA.pem in the ssl directory created by the scripts above.

This probably already setup on your machine, but you need to check the file /etc/hosts to make sure localhost points to the IP address django-sslserver is using (most likely 127.0.0.1). Then in the browser go to:

https://localhost:8000/

Launch django-sslserver using something like:

python manage.py runsslserver --certificate ~/ssl/server.crt --key ~/ssl/server.key

 

Advertisements

Securing SSL Certificates Using Ansible Vault

Here is how to secure your SSL Certificates using Ansible Vault:

1. Create a file to hold your secrets – secrets.yml

2. Put the .crt and .pub files in secrets.yml as variables:

---

private_ssl_key: |
  -----BEGIN CERTIFICATE-----
  BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMC ...
  ....
  -----END CERTIFICATE-----

public_ssl_key: |
  -----BEGIN PRIVATE KEY-----
  tk8R+HCtWefwfRhDRD4AH7g59WROn....
  ....
  -----END PRIVATE KEY-----

If you have xclip installed, you can use it to copy files directly to your clipboard:

cat filename | xclip -selection clipboard

Note that when you paste the keys into secrets.yml, you need to indent the keys.

3. Create the SSL files on the virtual machine:

- name: install SSL private key
  copy: content="{{ private_ssl_key }}"
        dest={{ ssl_dest_dir }}/{{ application_name }}.crt

- name: install SSL public key
  copy: content="{{ public_ssl_key }}"
        dest={{ ssl_dest_dir }}/{{ application_name }}.key

4. Setup Nginx:

server {
    listen      80;
    server_name {{ nginx_server_name }};
    return      301 https://$host$request_uri;
}

server {
   listen              443;
   server_name         {{ nginx_server_name }};
   ssl on;
   ssl_certificate     {{ ssl_dest_dir }}/{{ application_name }}.crt;
   ssl_certificate_key {{ ssl_dest_dir }}/{{ application_name }}.key;

   OTHER SETTINGS....

5. Add secrets.yml to your playbook vars.

6. Notice, secrets.yml is not encrypted yet. Test this setup to make sure things are working. DO NOT PUT secrets.yml UNDER VERSION CONTROL YET.

I do local testing using Vagrant. I connect to the website via the IP address 192.168.33.15. This is not the domain name that my SSL certificate is for. So how to test locally? The simplest thing to do is to click on the x-ed out lock on the browser address bar and at least confirm that the browser used the correct SSL certificate, but found that 192.168.33.15 did not match.

Or if you want to get fancy, you can edit the /etc/hosts file and point that IP address to the domain name that your SSL certificate covers. If you have a wildcard SSL certificate, this works well because you can create a domain name that will not interfere with the production domain name. For example: local.my_domain_name.com.

If your certificate is for a single domain name, altering /etc/hosts might lead to errors if you forget to delete the domain name when you want to test the site on a remote server. If you decide to go with this, to be on the safe side, you might want to install an extension to your browser so that you can check the IP address of the domain name. For Chrome, I use ivpfoo.

7. If everything works, then encrypt secrets.yml:

ansible-vault encrypt secret_vars.yml

8. I use Vagrant to configure a local VM. To decrypt the file you need to add this to your Vagrantfile:

# Ansible provisioner.
  config.vm.provision "ansible" do |ansible|
    ansible.playbook = "ansible-django-stack/vagrant.yml"
    ansible.host_key_checking = false
    ansible.verbose = "v"
    ansible.ask_vault_pass = true
  end

9. Confirm  things still work with Vagrant

10. Add encrypted secret_vars.yml to version control and push to repo (e.g. Bitbucket)

11. Run Ansible to configure live server.