Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save andreydelpozo2/2c991871153e1c4e528b to your computer and use it in GitHub Desktop.
Save andreydelpozo2/2c991871153e1c4e528b to your computer and use it in GitHub Desktop.

Revisions

  1. @jed jed revised this gist Aug 6, 2013. 2 changed files with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -183,7 +183,7 @@ Note that this will satisfy Chrome and Safari, but since Firefox doesn't inherit

    #### How do you HTTPS?

    This guide is a snapshot of my thoughts on how to simply the HTTPS workflow for web development, but if you have any feedback or want to share some tips about your HTTPS workflow, drop me a line at [@jedschmidt](https://twitter.com/jedschmidt) or [leave a comment](https://gist.github.com/jed/6147872#comments) below.
    This guide is a snapshot of my thoughts on how to simplify HTTPS workflow for web development, but if you have any feedback or want to share some tips about your workflow, drop a line at [@jedschmidt](https://twitter.com/jedschmidt) or [leave a comment](https://gist.github.com/jed/6147872#comments) below.

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
    Binary file modified trust_certificate.gif
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  2. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -126,7 +126,7 @@ There's a better way; we can tell OS X to trust the certificate we just created

    Since our certificate is self-signed, we'll always get a warning when using it for our HTTPS site. We need to use __Keychain Access__ to tell OS X to enhance its calm for this domain.

    ![keychain access](/jed/6147872/raw/8047f0a0459aba06eaa9d04e3012affe71ae3254/trust_certificate.gif)
    ![keychain access](/jed/6147872/raw/2fc6ecf2ccd8ecd8830124ee9b2fd3a7cff389db/trust_certificate.gif)

    1. Open the certificate in __Keychain Access__.

  3. @jed jed revised this gist Aug 6, 2013. 1 changed file with 0 additions and 0 deletions.
    Binary file modified trust_certificate.gif
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  4. @jed jed revised this gist Aug 6, 2013. 1 changed file with 2 additions and 10 deletions.
    12 changes: 2 additions & 10 deletions how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -128,20 +128,12 @@ Since our certificate is self-signed, we'll always get a warning when using it f

    ![keychain access](/jed/6147872/raw/8047f0a0459aba06eaa9d04e3012affe71ae3254/trust_certificate.gif)

    1. Open __Keychain Access__.
    1. Open the certificate in __Keychain Access__.

    ```bash
    open /Applications/Utilities/Keychain\ Access.app
    open /Applications/Utilities/Keychain\ Access.app ssl.crt
    ```

    1. From the __Keychains__ list on the left, select __System__.

    1. Click the [] button under the list of certificates.

    1. Choose the `ssl.crt` file created in the previous step, make sure __System__ is selected for __Destination Keychain__, and click __Open__.

    1. When prompted, enter your password and click __Modify Keychain__.

    1. Click __Don't Trust__.

    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the [] button.
  5. @jed jed revised this gist Aug 6, 2013. 1 changed file with 63 additions and 51 deletions.
    114 changes: 63 additions & 51 deletions how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -32,7 +32,7 @@ With a little more work up front, we can streamline our development workflow by

    One tool that can help us do this is [Dnsmasq][], a lightweight DNS forwarder. Here's how we'll install it:

    ```sh
    ```bash
    brew install dnsmasq
    mkdir -pv $(brew --prefix)/etc
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    @@ -44,14 +44,18 @@ Once it's installed and running, we need to choose what TLD we'd like to resolve

    All of the following shell scripts assume you're cool with using your user id as your top-level domain, but feel to change them accordingly by replacing `$(whoami)` with your chosen TLD.

    echo "address=/.$(whoami)/127.0.0.1" | sudo tee -a $(brew --prefix)/etc/dnsmasq.conf
    echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/$(whoami)
    ```bash
    echo "address=/.$(whoami)/127.0.0.1" | sudo tee -a $(brew --prefix)/etc/dnsmasq.conf
    echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/$(whoami)
    ```

    Now we need to make sure that worked, by spinning up a server and hitting it.

    cd /Applications
    sleep 1 && open "http://some.domain.$(whoami):9520" &
    python -m SimpleHTTPServer 9520
    ```bash
    cd /Applications
    sleep 1 && open "http://some.domain.$(whoami):9520" &
    python -m SimpleHTTPServer 9520
    ```

    This should open a new browser that shows the contents of your Applications folder, like this:

    @@ -70,38 +74,42 @@ Now that we know our DNS works, we need to create an SSL certificate for our dev

    First, let's create a new directory named for our project and `cd` into it.

    mkdir ~/Desktop/myproject && cd $_
    ```bash
    mkdir ~/Desktop/myproject && cd $_
    ```

    Next, let's create a temporary configuration file, and feed it into `openssl` to create our certificate.

    cat > openssl.cnf <<-EOF
    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    CN = *.${PWD##*/}.$(whoami)
    [v3_req]
    keyUsage = keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = *.${PWD##*/}.$(whoami)
    DNS.2 = ${PWD##*/}.$(whoami)
    EOF

    openssl req \
    -new \
    -newkey rsa:2048 \
    -sha1 \
    -days 3650 \
    -nodes \
    -x509 \
    -keyout ssl.key \
    -out ssl.crt \
    -config openssl.cnf

    rm openssl.cnf
    ```bash
    cat > openssl.cnf <<-EOF
    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    CN = *.${PWD##*/}.$(whoami)
    [v3_req]
    keyUsage = keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = *.${PWD##*/}.$(whoami)
    DNS.2 = ${PWD##*/}.$(whoami)
    EOF

    openssl req \
    -new \
    -newkey rsa:2048 \
    -sha1 \
    -days 3650 \
    -nodes \
    -x509 \
    -keyout ssl.key \
    -out ssl.crt \
    -config openssl.cnf

    rm openssl.cnf
    ```

    Now we have two files in our project directory: `ssl.key`, the private key used to sign the certificate, and `ssl.crt`, the certificate itself.

    @@ -122,7 +130,9 @@ Since our certificate is self-signed, we'll always get a warning when using it f

    1. Open __Keychain Access__.

    `open /Applications/Utilities/Keychain\ Access.app`
    ```bash
    open /Applications/Utilities/Keychain\ Access.app
    ```

    1. From the __Keychains__ list on the left, select __System__.

    @@ -149,25 +159,27 @@ Since our certificate is self-signed, we'll always get a warning when using it f

    Now that OS X knows that our self-signed certificate is legit, let's spin up an HTTPS server to make sure it all works. You can use Apache or Nginx or whatever you like, but here we'll use [nodejs][]:

    sleep 1 && open "https://myproject.$(whoami):8443" &
    sleep 1 && open "https://subdomain.myproject.$(whoami):8443" &
    ```bash
    sleep 1 && open "https://myproject.$(whoami):8443" &
    sleep 1 && open "https://subdomain.myproject.$(whoami):8443" &

    node <<-EOF
    var https = require("https")
    var fs = require("fs")
    node <<-EOF
    var https = require("https")
    var fs = require("fs")
    var options = {
    key: fs.readFileSync("ssl.key"),
    cert: fs.readFileSync("ssl.crt")
    }
    var options = {
    key: fs.readFileSync("ssl.key"),
    cert: fs.readFileSync("ssl.crt")
    }
    var server = https.createServer(options, function(req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"})
    res.end("It worked!\n")
    })
    var server = https.createServer(options, function(req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"})
    res.end("It worked!\n")
    })
    server.listen(8443, console.log)
    EOF
    server.listen(8443, console.log)
    EOF
    ```

    As you can see, the correct content is being served by both the apex domain and subdomain.

  6. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -32,7 +32,7 @@ With a little more work up front, we can streamline our development workflow by

    One tool that can help us do this is [Dnsmasq][], a lightweight DNS forwarder. Here's how we'll install it:

    ```bash
    ```sh
    brew install dnsmasq
    mkdir -pv $(brew --prefix)/etc
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
  7. @jed jed revised this gist Aug 6, 2013. 1 changed file with 7 additions and 5 deletions.
    12 changes: 7 additions & 5 deletions how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -32,11 +32,13 @@ With a little more work up front, we can streamline our development workflow by

    One tool that can help us do this is [Dnsmasq][], a lightweight DNS forwarder. Here's how we'll install it:

    brew install dnsmasq
    mkdir -pv $(brew --prefix)/etc
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
    sudo mkdir -pv /etc/resolver
    ```bash
    brew install dnsmasq
    mkdir -pv $(brew --prefix)/etc
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
    sudo mkdir -pv /etc/resolver
    ```

    Once it's installed and running, we need to choose what TLD we'd like to resolve to our local box. OS X [reserves][] the `.local` TLD, and [many developers][] use `.dev`, but I like the idea of using names as TLDs, to make it easier to reason about my local environment within the context of other developers on the same project. So I'm going to have Dnsmasq locally resolve all hosts ending in my OS X user id (also known as a _short name_, and what you get when you type `whoami` in the terminal). Since my user id is `jed`, that means everything from `apple.jed` to `zebras.jed` to `apple.zebras.jed` will resolve to `127.0.0.1`.

  8. @jed jed revised this gist Aug 6, 2013. 2 changed files with 1 addition and 2 deletions.
    3 changes: 1 addition & 2 deletions how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -103,8 +103,6 @@ Next, let's create a temporary configuration file, and feed it into `openssl` to

    Now we have two files in our project directory: `ssl.key`, the private key used to sign the certificate, and `ssl.crt`, the certificate itself.

    ![finder](/jed/6147872/raw/ddc046475c14ffa462fda80f1694255e280b7be7/screenshot-3.png)

    At this point we have almost everything we need to fire up a local HTTPS server, but there's one problem.

    ![certificate not trusted](/jed/6147872/raw/938a9c00fc5260310e605520d59f1aafa33e2e95/screenshot-4.png)
    @@ -151,6 +149,7 @@ Now that OS X knows that our self-signed certificate is legit, let's spin up an

    sleep 1 && open "https://myproject.$(whoami):8443" &
    sleep 1 && open "https://subdomain.myproject.$(whoami):8443" &

    node <<-EOF
    var https = require("https")
    var fs = require("fs")
    Binary file removed screenshot-3.png
    Binary file not shown.
  9. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -64,7 +64,7 @@ In this case, we'll need to restart the machine to make sure the setup above has
    <a name="step-2"></a>
    #### Create a wildcard SSL certificate for each project

    Now that we know our DNS works, we need to create an SSL certificate for our development environment. Since (for good reason) browsers [won't trust][] certificates that cover an entire TLD, we need to create a wildcard certificate for each domain we want to use. Also, since we want to cover both any subdomain (*.yourproject.tld) _and_ the domain apex (yourproject.tld), we'll need to use [Subject Alternative Name][] extension.
    Now that we know our DNS works, we need to create an SSL certificate for our development environment. Since (for good reason) browsers [won't trust][] certificates that cover an entire TLD, we need to create a wildcard certificate for each domain we want to use. Also, since we want to cover both arbitrary subdomains (*.yourproject.tld) _and_ the domain apex (yourproject.tld), we'll need to use the [Subject Alternative Name][] X.509 extension.

    First, let's create a new directory named for our project and `cd` into it.

  10. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -178,7 +178,7 @@ Note that this will satisfy Chrome and Safari, but since Firefox doesn't inherit

    #### How do you HTTPS?

    This guide is a snapshot of my thoughts on responsible web development, but if you have any feedback or want to share some tips about your HTTPS workflow, drop me a line at [@jedschmidt](https://twitter.com/jedschmidt) or [leave a comment](https://gist.github.com/jed/6147872#comments) below.
    This guide is a snapshot of my thoughts on how to simply the HTTPS workflow for web development, but if you have any feedback or want to share some tips about your HTTPS workflow, drop me a line at [@jedschmidt](https://twitter.com/jedschmidt) or [leave a comment](https://gist.github.com/jed/6147872#comments) below.

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
  11. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -178,7 +178,7 @@ Note that this will satisfy Chrome and Safari, but since Firefox doesn't inherit

    #### How do you HTTPS?

    This guide is a snapshot of my thoughts on responsible web development, but if you have any feedback or want to share some tips about your HTTPS workflow, drop me a line at [@jedschmidt](https://twitter.com/jedschmidt).
    This guide is a snapshot of my thoughts on responsible web development, but if you have any feedback or want to share some tips about your HTTPS workflow, drop me a line at [@jedschmidt](https://twitter.com/jedschmidt) or [leave a comment](https://gist.github.com/jed/6147872#comments) below.

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
  12. @jed jed revised this gist Aug 6, 2013. 1 changed file with 48 additions and 0 deletions.
    48 changes: 48 additions & 0 deletions please-stop-reading-the-article-is-over.md
    Original file line number Diff line number Diff line change
    @@ -1,49 +1,97 @@
    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    .

    yeah, so you should stop reading now, unless you want a data dump of all the images used in this missive.
  13. @jed jed revised this gist Aug 6, 2013. 1 changed file with 48 additions and 48 deletions.
    96 changes: 48 additions & 48 deletions please-stop-reading-the-article-is-over.md
    Original file line number Diff line number Diff line change
    @@ -1,49 +1,49 @@
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
     
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    .
    yeah, so you should stop reading now, unless you want a data dump of all the images used in this missive.
  14. @jed jed revised this gist Aug 6, 2013. 1 changed file with 49 additions and 0 deletions.
    49 changes: 49 additions & 0 deletions please-stop-reading-the-article-is-over.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,49 @@
















































    yeah, so you should stop reading now, unless you want a data dump of all the images used in this missive.
  15. @jed jed revised this gist Aug 6, 2013. 1 changed file with 5 additions and 1 deletion.
    6 changes: 5 additions & 1 deletion how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -174,7 +174,11 @@ As you can see, the correct content is being served by both the apex domain and

    ![subdomain](/jed/6147872/raw/7d78d97668b7891832da3fa1de41eb15338dc472/screenshot-6.png)

    This will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...__, and then __Confirm Security Exception___ when prompted.
    Note that this will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...__, and then __Confirm Security Exception___ when prompted.

    #### How do you HTTPS?

    This guide is a snapshot of my thoughts on responsible web development, but if you have any feedback or want to share some tips about your HTTPS workflow, drop me a line at [@jedschmidt](https://twitter.com/jedschmidt).

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
  16. @jed jed revised this gist Aug 6, 2013. 1 changed file with 187 additions and 0 deletions.
    187 changes: 187 additions & 0 deletions how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,187 @@
    ### How to set up stress-free SSL on an OS X development machine

    One of the best ways to reduce complexity (read: _stress_) in web development is to minimize the differences between your development and production environments. After being frustrated by attempts to unify the approach to SSL on my local machine and in production, I searched for a workflow that would make the protocol invisible to me between all environments.

    Most workflows make the following compromises:

    - __Use HTTPS in production but HTTP locally__. This is annoying because it makes the environments inconsistent, and the protocol choices leak up into the stack. For example, your web application needs to understand the underlying protocol when using the `secure` flag for cookies. If you don't get this right, your HTTP development server won't be able to read the cookies it writes, or worse, your HTTPS production server could pass sensitive cookies over an insecure connection.

    - __Use production SSL certificates locally__. This is annoying because SSL credentials shouldn't be passed around lightly, and ideally should only exist on blessed machines. Plus, even with a wildcard certificate, coming up with a scheme to put development hosts and production hosts under the same namespace has some weird edge cases (if the production app is served off of `myproject.com`, where is the development app served from?)

    - __Give up on HTTPS entirely__. This is annoying because it's like, 2013 already, amirite.

    So here's my approach for a modern HTTPS workflow, in four steps:

    1. [Resolve a top-level domain for all development work](#step-1),
    2. [Create a wildcard SSL certificate for each project](#step-2),
    3. [Avoid HTTPS warnings by telling OS X to trust the certificate](#step-3), and
    4. [Bask in easy HTTPS](#step-4).

    Let's get started.

    #### Install [Homebrew][] if it's not already installed

    ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

    <a name="step-1"></a>
    #### Resolve a top-level domain for all development work

    The most common way to get a given host to resolve to your local machine is to manually edit your `/private/etc/hosts` file, which is annoyingly _O(n<sup>2</sup>)_, since you need to add an entry for each subdomain of each project you're working on.

    With a little more work up front, we can streamline our development workflow by resolving a single [top-level domain][] (herein as _TLD_) to our development box and [never touch the hosts file again][].

    One tool that can help us do this is [Dnsmasq][], a lightweight DNS forwarder. Here's how we'll install it:

    brew install dnsmasq
    mkdir -pv $(brew --prefix)/etc
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
    sudo mkdir -pv /etc/resolver

    Once it's installed and running, we need to choose what TLD we'd like to resolve to our local box. OS X [reserves][] the `.local` TLD, and [many developers][] use `.dev`, but I like the idea of using names as TLDs, to make it easier to reason about my local environment within the context of other developers on the same project. So I'm going to have Dnsmasq locally resolve all hosts ending in my OS X user id (also known as a _short name_, and what you get when you type `whoami` in the terminal). Since my user id is `jed`, that means everything from `apple.jed` to `zebras.jed` to `apple.zebras.jed` will resolve to `127.0.0.1`.

    All of the following shell scripts assume you're cool with using your user id as your top-level domain, but feel to change them accordingly by replacing `$(whoami)` with your chosen TLD.

    echo "address=/.$(whoami)/127.0.0.1" | sudo tee -a $(brew --prefix)/etc/dnsmasq.conf
    echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/$(whoami)

    Now we need to make sure that worked, by spinning up a server and hitting it.

    cd /Applications
    sleep 1 && open "http://some.domain.$(whoami):9520" &
    python -m SimpleHTTPServer 9520

    This should open a new browser that shows the contents of your Applications folder, like this:

    ![applications folder](/jed/6147872/raw/b44236dd3a3410b0b44226272a62f6688530b14b/screenshot-1.png)

    The browser might tell us that the DNS lookup failed, like this:

    ![failed dns](/jed/6147872/raw/df31903f98f34b0cb04ae3ca1154e2ece6e66a6f/screenshot-2.png)

    In this case, we'll need to restart the machine to make sure the setup above has taken effect.

    <a name="step-2"></a>
    #### Create a wildcard SSL certificate for each project

    Now that we know our DNS works, we need to create an SSL certificate for our development environment. Since (for good reason) browsers [won't trust][] certificates that cover an entire TLD, we need to create a wildcard certificate for each domain we want to use. Also, since we want to cover both any subdomain (*.yourproject.tld) _and_ the domain apex (yourproject.tld), we'll need to use [Subject Alternative Name][] extension.

    First, let's create a new directory named for our project and `cd` into it.

    mkdir ~/Desktop/myproject && cd $_

    Next, let's create a temporary configuration file, and feed it into `openssl` to create our certificate.

    cat > openssl.cnf <<-EOF
    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    CN = *.${PWD##*/}.$(whoami)
    [v3_req]
    keyUsage = keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = *.${PWD##*/}.$(whoami)
    DNS.2 = ${PWD##*/}.$(whoami)
    EOF

    openssl req \
    -new \
    -newkey rsa:2048 \
    -sha1 \
    -days 3650 \
    -nodes \
    -x509 \
    -keyout ssl.key \
    -out ssl.crt \
    -config openssl.cnf

    rm openssl.cnf

    Now we have two files in our project directory: `ssl.key`, the private key used to sign the certificate, and `ssl.crt`, the certificate itself.

    ![finder](/jed/6147872/raw/ddc046475c14ffa462fda80f1694255e280b7be7/screenshot-3.png)

    At this point we have almost everything we need to fire up a local HTTPS server, but there's one problem.

    ![certificate not trusted](/jed/6147872/raw/938a9c00fc5260310e605520d59f1aafa33e2e95/screenshot-4.png)

    Any content we try to serve over HTTPS from this domain gets [IMMA-LET-YOU-FINISH](http://knowyourmeme.com/memes/events/kanye-interrupts-imma-let-you-finish)ed by a scary message like the one above, warning us that the presented certificate is not trusted. This message differs by browser, and you might be tempted to go ahead and ignore it, but that's not a good habit to get into, and is likely to lead to development complexity down the road.

    There's a better way; we can tell OS X to trust the certificate we just created so that we don't have to see this screen ever again, by adding the certificate to our keychain.

    <a name="step-3"></a>
    #### Avoid HTTPS warnings by telling OS X to trust the certificate

    Since our certificate is self-signed, we'll always get a warning when using it for our HTTPS site. We need to use __Keychain Access__ to tell OS X to enhance its calm for this domain.

    ![keychain access](/jed/6147872/raw/8047f0a0459aba06eaa9d04e3012affe71ae3254/trust_certificate.gif)

    1. Open __Keychain Access__.

    `open /Applications/Utilities/Keychain\ Access.app`

    1. From the __Keychains__ list on the left, select __System__.

    1. Click the [] button under the list of certificates.

    1. Choose the `ssl.crt` file created in the previous step, make sure __System__ is selected for __Destination Keychain__, and click __Open__.

    1. When prompted, enter your password and click __Modify Keychain__.

    1. Click __Don't Trust__.

    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the [] button.

    1. In the popup window, click the ▶ button to the left of __Trust__, and select __Always Trust__ for __When using this certificate:__.

    1. Close the popup window.

    1. When prompted, enter your password again and click __Update Settings__.

    1. Close __Keychain Access__.

    <a name="step-4"></a>
    #### Bask in easy HTTPS

    Now that OS X knows that our self-signed certificate is legit, let's spin up an HTTPS server to make sure it all works. You can use Apache or Nginx or whatever you like, but here we'll use [nodejs][]:

    sleep 1 && open "https://myproject.$(whoami):8443" &
    sleep 1 && open "https://subdomain.myproject.$(whoami):8443" &
    node <<-EOF
    var https = require("https")
    var fs = require("fs")

    var options = {
    key: fs.readFileSync("ssl.key"),
    cert: fs.readFileSync("ssl.crt")
    }

    var server = https.createServer(options, function(req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"})
    res.end("It worked!\n")
    })

    server.listen(8443, console.log)
    EOF

    As you can see, the correct content is being served by both the apex domain and subdomain.

    ![apex domain](/jed/6147872/raw/97f05c6ebafaf359dfe75270ea9492b4fa65d249/screenshot-5.png)

    ![subdomain](/jed/6147872/raw/7d78d97668b7891832da3fa1de41eb15338dc472/screenshot-6.png)

    This will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...__, and then __Confirm Security Exception___ when prompted.

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
    [top-level domain]: http://en.wikipedia.org/wiki/Top-level_domain
    [never touch the hosts file again]: http://www.echoditto.com/blog/never-touch-your-local-etchosts-file-os-x-again
    [reserves]: http://en.wikipedia.org/wiki/.local#mDNS_implementations
    [won't trust]: http://security.stackexchange.com/questions/6873/can-a-wildcard-ssl-certificate-be-issued-for-a-second-level-domain
    [Subject Alternative Name]: http://en.wikipedia.org/wiki/SubjectAltName
    [many developers]: http://pow.cx
    [nodejs]: http://nodejs.org
  17. @jed jed revised this gist Aug 6, 2013. 2 changed files with 0 additions and 374 deletions.
    187 changes: 0 additions & 187 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -1,187 +0,0 @@
    ### How to set up stress-free SSL on an OS X development machine

    One of the best ways to reduce complexity (read: _stress_) in web development is to minimize the differences between your development and production environments. After being frustrated by attempts to unify the approach to SSL on my local machine and in production, I searched for a workflow that would make the protocol invisible to me between all environments.

    Most workflows make the following compromises:

    - __Use HTTPS in production but HTTP locally__. This is annoying because it makes the environments inconsistent, and the protocol choices leak up into the stack. For example, your web application needs to understand the underlying protocol when using the `secure` flag for cookies. If you don't get this right, your HTTP development server won't be able to read the cookies it writes, or worse, your HTTPS production server could pass sensitive cookies over an insecure connection.

    - __Use production SSL certificates locally__. This is annoying because SSL credentials shouldn't be passed around lightly, and ideally should only exist on blessed machines. Plus, even with a wildcard certificate, coming up with a scheme to put development hosts and production hosts under the same namespace has some weird edge cases (if the production app is served off of `myproject.com`, where is the development app served from?)

    - __Give up on HTTPS entirely__. This is annoying because it's like, 2013 already, amirite.

    So here's my approach for a modern HTTPS workflow, in four steps:

    1. [Resolve a top-level domain for all development work](#step-1),
    2. [Create a wildcard SSL certificate for each project](#step-2),
    3. [Avoid HTTPS warnings by telling OS X to trust the certificate](#step-3), and
    4. [Bask in easy HTTPS](#step-4).

    Let's get started.

    #### Install [Homebrew][] if it's not already installed

    ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

    <a name="step-1"></a>
    #### Resolve a top-level domain for all development work

    The most common way to get a given host to resolve to your local machine is to manually edit your `/private/etc/hosts` file, which is annoyingly _O(n<sup>2</sup>)_, since you need to add an entry for each subdomain of each project you're working on.

    With a little more work up front, we can streamline our development workflow by resolving a single [top-level domain][] (herein as _TLD_) to our development box and [never touch the hosts file again][].

    One tool that can help us do this is [Dnsmasq][], a lightweight DNS forwarder. Here's how we'll install it:

    brew install dnsmasq
    mkdir -pv $(brew --prefix)/etc
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
    sudo mkdir -pv /etc/resolver

    Once it's installed and running, we need to choose what TLD we'd like to resolve to our local box. OS X [reserves][] the `.local` TLD, and [many developers][] use `.dev`, but I like the idea of using names as TLDs, to make it easier to reason about my local environment within the context of other developers on the same project. So I'm going to have Dnsmasq locally resolve all hosts ending in my OS X user id (also known as a _short name_, and what you get when you type `whoami` in the terminal). Since my user id is `jed`, that means everything from `apple.jed` to `zebras.jed` to `apple.zebras.jed` will resolve to `127.0.0.1`.

    All of the following shell scripts assume you're cool with using your user id as your top-level domain, but feel to change them accordingly by replacing `$(whoami)` with your chosen TLD.

    echo "address=/.$(whoami)/127.0.0.1" | sudo tee -a $(brew --prefix)/etc/dnsmasq.conf
    echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/$(whoami)

    Now we need to make sure that worked, by spinning up a server and hitting it.

    cd /Applications
    sleep 1 && open "http://some.domain.$(whoami):9520" &
    python -m SimpleHTTPServer 9520

    This should open a new browser that shows the contents of your Applications folder, like this:

    ![applications folder](/jed/6147872/raw/b44236dd3a3410b0b44226272a62f6688530b14b/screenshot-1.png)

    The browser might tell us that the DNS lookup failed, like this:

    ![failed dns](/jed/6147872/raw/df31903f98f34b0cb04ae3ca1154e2ece6e66a6f/screenshot-2.png)

    In this case, we'll need to restart the machine to make sure the setup above has taken effect.

    <a name="step-2"></a>
    #### Create a wildcard SSL certificate for each project

    Now that we know our DNS works, we need to create an SSL certificate for our development environment. Since (for good reason) browsers [won't trust][] certificates that cover an entire TLD, we need to create a wildcard certificate for each domain we want to use. Also, since we want to cover both any subdomain (*.yourproject.tld) _and_ the domain apex (yourproject.tld), we'll need to use [Subject Alternative Name][] extension.

    First, let's create a new directory named for our project and `cd` into it.

    mkdir ~/Desktop/myproject && cd $_

    Next, let's create a temporary configuration file, and feed it into `openssl` to create our certificate.

    cat > openssl.cnf <<-EOF
    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    CN = *.${PWD##*/}.$(whoami)
    [v3_req]
    keyUsage = keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = *.${PWD##*/}.$(whoami)
    DNS.2 = ${PWD##*/}.$(whoami)
    EOF

    openssl req \
    -new \
    -newkey rsa:2048 \
    -sha1 \
    -days 3650 \
    -nodes \
    -x509 \
    -keyout ssl.key \
    -out ssl.crt \
    -config openssl.cnf

    rm openssl.cnf

    Now we have two files in our project directory: `ssl.key`, the private key used to sign the certificate, and `ssl.crt`, the certificate itself.

    ![finder](/jed/6147872/raw/ddc046475c14ffa462fda80f1694255e280b7be7/screenshot-3.png)

    At this point we have almost everything we need to fire up a local HTTPS server, but there's one problem.

    ![certificate not trusted](/jed/6147872/raw/938a9c00fc5260310e605520d59f1aafa33e2e95/screenshot-4.png)

    Any content we try to serve over HTTPS from this domain gets [IMMA-LET-YOU-FINISH](http://knowyourmeme.com/memes/events/kanye-interrupts-imma-let-you-finish)ed by a scary message like the one above, warning us that the presented certificate is not trusted. This message differs by browser, and you might be tempted to go ahead and ignore it, but that's not a good habit to get into, and is likely to lead to development complexity down the road.

    There's a better way; we can tell OS X to trust the certificate we just created so that we don't have to see this screen ever again, by adding the certificate to our keychain.

    <a name="step-3"></a>
    #### Avoid HTTPS warnings by telling OS X to trust the certificate

    Since our certificate is self-signed, we'll always get a warning when using it for our HTTPS site. We need to use __Keychain Access__ to tell OS X to enhance its calm for this domain.

    ![keychain access](/jed/6147872/raw/8047f0a0459aba06eaa9d04e3012affe71ae3254/trust_certificate.gif)

    1. Open __Keychain Access__.

    `open /Applications/Utilities/Keychain\ Access.app`

    1. From the __Keychains__ list on the left, select __System__.

    1. Click the [] button under the list of certificates.

    1. Choose the `ssl.crt` file created in the previous step, make sure __System__ is selected for __Destination Keychain__, and click __Open__.

    1. When prompted, enter your password and click __Modify Keychain__.

    1. Click __Don't Trust__.

    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the [] button.

    1. In the popup window, click the ▶ button to the left of __Trust__, and select __Always Trust__ for __When using this certificate:__.

    1. Close the popup window.

    1. When prompted, enter your password again and click __Update Settings__.

    1. Close __Keychain Access__.

    <a name="step-4"></a>
    #### Bask in easy HTTPS

    Now that OS X knows that our self-signed certificate is legit, let's spin up an HTTPS server to make sure it all works. You can use Apache or Nginx or whatever you like, but here we'll use [nodejs][]:

    sleep 1 && open "https://myproject.$(whoami):8443" &
    sleep 1 && open "https://subdomain.myproject.$(whoami):8443" &
    node <<-EOF
    var https = require("https")
    var fs = require("fs")

    var options = {
    key: fs.readFileSync("ssl.key"),
    cert: fs.readFileSync("ssl.crt")
    }

    var server = https.createServer(options, function(req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"})
    res.end("It worked!\n")
    })

    server.listen(8443, console.log)
    EOF

    As you can see, the correct content is being served by both the apex domain and subdomain.

    ![apex domain](/jed/6147872/raw/97f05c6ebafaf359dfe75270ea9492b4fa65d249/screenshot-5.png)

    ![subdomain](/jed/6147872/raw/7d78d97668b7891832da3fa1de41eb15338dc472/screenshot-6.png)

    This will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...__, and then __Confirm Security Exception___ when prompted.

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
    [top-level domain]: http://en.wikipedia.org/wiki/Top-level_domain
    [never touch the hosts file again]: http://www.echoditto.com/blog/never-touch-your-local-etchosts-file-os-x-again
    [reserves]: http://en.wikipedia.org/wiki/.local#mDNS_implementations
    [won't trust]: http://security.stackexchange.com/questions/6873/can-a-wildcard-ssl-certificate-be-issued-for-a-second-level-domain
    [Subject Alternative Name]: http://en.wikipedia.org/wiki/SubjectAltName
    [many developers]: http://pow.cx
    [nodejs]: http://nodejs.org
    187 changes: 0 additions & 187 deletions how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -1,187 +0,0 @@
    ### How to set up stress-free SSL on an OS X development machine

    One of the best ways to reduce complexity (read: _stress_) in web development is to minimize the differences between your development and production environments. After being frustrated by attempts to unify the approach to SSL on my local machine and in production, I searched for a workflow that would make the protocol invisible to me between all environments.

    Most workflows make the following compromises:

    - __Use HTTPS in production but HTTP locally__. This is annoying because it makes the environments inconsistent, and the protocol choices leak up into the stack. For example, your web application needs to understand the underlying protocol when using the `secure` flag for cookies. If you don't get this right, your HTTP development server won't be able to read the cookies it writes, or worse, your HTTPS production server could pass sensitive cookies over an insecure connection.

    - __Use production SSL certificates locally__. This is annoying because SSL credentials shouldn't be passed around lightly, and ideally should only exist on blessed machines. Plus, even with a wildcard certificate, coming up with a scheme to put development hosts and production hosts under the same namespace has some weird edge cases (if the production app is served off of `myproject.com`, where is the development app served from?)

    - __Give up on HTTPS entirely__. This is annoying because it's like, 2013 already, amirite.

    So here's my approach for a modern HTTPS workflow, in four steps:

    1. [Resolve a top-level domain for all development work](#step-1),
    2. [Create a wildcard SSL certificate for each project](#step-2),
    3. [Avoid HTTPS warnings by telling OS X to trust the certificate](#step-3), and
    4. [Bask in easy HTTPS](#step-4).

    Let's get started.

    #### Install [Homebrew][] if it's not already installed

    ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

    <a name="step-1"></a>
    #### Resolve a top-level domain for all development work

    The most common way to get a given host to resolve to your local machine is to manually edit your `/private/etc/hosts` file, which is annoyingly _O_(_n_<sup>_2_</sup>), since you need to add an entry for each subdomain of each project you're working on.

    With a little more work up front, we can streamline our development workflow by resolving a single [top-level domain][] (herein as _TLD_) to our development box and [never touch the hosts file again][].

    One tool that can help us do this is [Dnsmasq][], a lightweight DNS forwarder. Here's how we'll install it:

    brew install dnsmasq
    mkdir -pv $(brew --prefix)/etc
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
    sudo mkdir -pv /etc/resolver

    Once it's installed and running, we need to choose what TLD we'd like to resolve to our local box. OS X [reserves][] the `.local` TLD, and [many developers][] use `.dev`, but I like the idea of using names as TLDs, to make it easier to reason about my local environment within the context of other developers on the same project. So I'm going to have Dnsmasq locally resolve all hosts ending in my OS X user id (also known as a _short name_, and what you get when you type `whoami` in the terminal). Since my user id is `jed`, that means everything from `apple.jed` to `zebras.jed` to `apple.zebras.jed` will resolve to `127.0.0.1`.

    All of the following shell scripts assume you're cool with using your user id as your top-level domain, but feel to change them accordingly by replacing `$(whoami)` with your chosen TLD.

    echo "address=/.$(whoami)/127.0.0.1" | sudo tee -a $(brew --prefix)/etc/dnsmasq.conf
    echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/$(whoami)

    Now we need to make sure that worked, by spinning up a server and hitting it.

    cd /Applications
    sleep 1 && open "http://some.domain.$(whoami):9520" &
    python -m SimpleHTTPServer 9520

    This should open a new browser that shows the contents of your Applications folder, like this:

    ![applications folder](/jed/6147872/raw/b44236dd3a3410b0b44226272a62f6688530b14b/screenshot-1.png)

    The browser might tell us that the DNS lookup failed, like this:

    ![failed dns](/jed/6147872/raw/df31903f98f34b0cb04ae3ca1154e2ece6e66a6f/screenshot-2.png)

    In this case, we'll need to restart the machine to make sure the setup above has taken effect.

    <a name="step-2"></a>
    #### Create a wildcard SSL certificate for each project

    Now that we know our DNS works, we need to create an SSL certificate for our development environment. Since (for good reason) browsers [won't trust][] certificates that cover an entire TLD, we need to create a wildcard certificate for each domain we want to use. Also, since we want to cover both any subdomain (*.yourproject.tld) _and_ the domain apex (yourproject.tld), we'll need to use [Subject Alternative Name][] extension.

    First, let's create a new directory named for our project and `cd` into it.

    mkdir ~/Desktop/myproject && cd $_

    Next, let's create a temporary configuration file, and feed it into `openssl` to create our certificate.

    cat > openssl.cnf <<-EOF
    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    CN = *.${PWD##*/}.$(whoami)
    [v3_req]
    keyUsage = keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = *.${PWD##*/}.$(whoami)
    DNS.2 = ${PWD##*/}.$(whoami)
    EOF

    openssl req \
    -new \
    -newkey rsa:2048 \
    -sha1 \
    -days 3650 \
    -nodes \
    -x509 \
    -keyout ssl.key \
    -out ssl.crt \
    -config openssl.cnf

    rm openssl.cnf

    Now we have two files in our project directory: `ssl.key`, the private key used to sign the certificate, and `ssl.crt`, the certificate itself.

    ![finder](/jed/6147872/raw/ddc046475c14ffa462fda80f1694255e280b7be7/screenshot-3.png)

    At this point we have almost everything we need to fire up a local HTTPS server, but there's one problem.

    ![certificate not trusted](/jed/6147872/raw/938a9c00fc5260310e605520d59f1aafa33e2e95/screenshot-4.png)

    Any content we try to serve over HTTPS from this domain gets [IMMA-LET-YOU-FINISH](http://knowyourmeme.com/memes/events/kanye-interrupts-imma-let-you-finish)ed by a scary message like the one above, warning us that the presented certificate is not trusted. This message differs by browser, and you might be tempted to go ahead and ignore it, but that's not a good habit to get into, and is likely to lead to development complexity down the road.

    There's a better way; we can tell OS X to trust the certificate we just created so that we don't have to see this screen ever again, by adding the certificate to our keychain.

    <a name="step-3"></a>
    #### Avoid HTTPS warnings by telling OS X to trust the certificate

    Since our certificate is self-signed, we'll always get a warning when using it for our HTTPS site. We need to use __Keychain Access__ to tell OS X to enhance its calm for this domain.

    ![keychain access](/jed/6147872/raw/8047f0a0459aba06eaa9d04e3012affe71ae3254/trust_certificate.gif)

    1. Open __Keychain Access__.

    open /Applications/Utilities/Keychain\ Access.app

    1. From the __Keychains__ list on the left, select __System__.

    1. Click the [] button under the list of certificates.

    1. Choose the `ssl.crt` file created in the previous step, make sure __System__ is selected for __Destination Keychain__, and click __Open__.

    1. When prompted, enter your password and click __Modify Keychain__.

    1. Click __Don't Trust__.

    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the [] button.

    1. In the popup window, click the ▶ button to the left of __Trust__, and select __Always Trust__ for __When using this certificate:__.

    1. Close the popup window.

    1. When prompted, enter your password again and click __Update Settings__.

    1. Close __Keychain Access__.

    <a name="step-4"></a>
    #### Bask in easy HTTPS

    Now that OS X knows that our self-signed certificate is legit, let's spin up an HTTPS server to make sure it all works. You can use Apache or Nginx or whatever you like, but here we'll use [nodejs][]:

    sleep 1 && open "https://myproject.$(whoami):8443" &
    sleep 1 && open "https://subdomain.myproject.$(whoami):8443" &
    node <<-EOF
    var https = require("https")
    var fs = require("fs")

    var options = {
    key: fs.readFileSync("ssl.key"),
    cert: fs.readFileSync("ssl.crt")
    }

    var server = https.createServer(options, function(req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"})
    res.end("It worked!\n")
    })

    server.listen(8443, console.log)
    EOF

    As you can see, the correct content is being served by both the apex domain and subdomain.

    ![apex domain](/jed/6147872/raw/97f05c6ebafaf359dfe75270ea9492b4fa65d249/screenshot-5.png)

    ![subdomain](/jed/6147872/raw/7d78d97668b7891832da3fa1de41eb15338dc472/screenshot-6.png)

    This will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...__, and then __Confirm Security Exception___ when prompted.

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
    [top-level domain]: http://en.wikipedia.org/wiki/Top-level_domain
    [never touch the hosts file again]: http://www.echoditto.com/blog/never-touch-your-local-etchosts-file-os-x-again
    [reserves]: http://en.wikipedia.org/wiki/.local#mDNS_implementations
    [won't trust]: http://security.stackexchange.com/questions/6873/can-a-wildcard-ssl-certificate-be-issued-for-a-second-level-domain
    [Subject Alternative Name]: http://en.wikipedia.org/wiki/SubjectAltName
    [many developers]: http://pow.cx
    [nodejs]: http://nodejs.org
  18. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -122,7 +122,7 @@ Since our certificate is self-signed, we'll always get a warning when using it f

    1. Open __Keychain Access__.

    open /Applications/Utilities/Keychain\ Access.app
    `open /Applications/Utilities/Keychain\ Access.app`

    1. From the __Keychains__ list on the left, select __System__.

  19. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -122,7 +122,7 @@ Since our certificate is self-signed, we'll always get a warning when using it f

    1. Open __Keychain Access__.

    open /Applications/Utilities/Keychain\ Access.app
    open /Applications/Utilities/Keychain\ Access.app

    1. From the __Keychains__ list on the left, select __System__.

  20. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -122,7 +122,7 @@ Since our certificate is self-signed, we'll always get a warning when using it f

    1. Open __Keychain Access__.

    open /Applications/Utilities/Keychain\ Access.app
    open /Applications/Utilities/Keychain\ Access.app

    1. From the __Keychains__ list on the left, select __System__.

  21. @jed jed revised this gist Aug 6, 2013. 1 changed file with 187 additions and 0 deletions.
    187 changes: 187 additions & 0 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,187 @@
    ### How to set up stress-free SSL on an OS X development machine

    One of the best ways to reduce complexity (read: _stress_) in web development is to minimize the differences between your development and production environments. After being frustrated by attempts to unify the approach to SSL on my local machine and in production, I searched for a workflow that would make the protocol invisible to me between all environments.

    Most workflows make the following compromises:

    - __Use HTTPS in production but HTTP locally__. This is annoying because it makes the environments inconsistent, and the protocol choices leak up into the stack. For example, your web application needs to understand the underlying protocol when using the `secure` flag for cookies. If you don't get this right, your HTTP development server won't be able to read the cookies it writes, or worse, your HTTPS production server could pass sensitive cookies over an insecure connection.

    - __Use production SSL certificates locally__. This is annoying because SSL credentials shouldn't be passed around lightly, and ideally should only exist on blessed machines. Plus, even with a wildcard certificate, coming up with a scheme to put development hosts and production hosts under the same namespace has some weird edge cases (if the production app is served off of `myproject.com`, where is the development app served from?)

    - __Give up on HTTPS entirely__. This is annoying because it's like, 2013 already, amirite.

    So here's my approach for a modern HTTPS workflow, in four steps:

    1. [Resolve a top-level domain for all development work](#step-1),
    2. [Create a wildcard SSL certificate for each project](#step-2),
    3. [Avoid HTTPS warnings by telling OS X to trust the certificate](#step-3), and
    4. [Bask in easy HTTPS](#step-4).

    Let's get started.

    #### Install [Homebrew][] if it's not already installed

    ruby -e "$(curl -fsSL https://raw.github.com/mxcl/homebrew/go)"

    <a name="step-1"></a>
    #### Resolve a top-level domain for all development work

    The most common way to get a given host to resolve to your local machine is to manually edit your `/private/etc/hosts` file, which is annoyingly _O(n<sup>2</sup>)_, since you need to add an entry for each subdomain of each project you're working on.

    With a little more work up front, we can streamline our development workflow by resolving a single [top-level domain][] (herein as _TLD_) to our development box and [never touch the hosts file again][].

    One tool that can help us do this is [Dnsmasq][], a lightweight DNS forwarder. Here's how we'll install it:

    brew install dnsmasq
    mkdir -pv $(brew --prefix)/etc
    sudo cp -v $(brew --prefix dnsmasq)/homebrew.mxcl.dnsmasq.plist /Library/LaunchDaemons
    sudo launchctl load -w /Library/LaunchDaemons/homebrew.mxcl.dnsmasq.plist
    sudo mkdir -pv /etc/resolver

    Once it's installed and running, we need to choose what TLD we'd like to resolve to our local box. OS X [reserves][] the `.local` TLD, and [many developers][] use `.dev`, but I like the idea of using names as TLDs, to make it easier to reason about my local environment within the context of other developers on the same project. So I'm going to have Dnsmasq locally resolve all hosts ending in my OS X user id (also known as a _short name_, and what you get when you type `whoami` in the terminal). Since my user id is `jed`, that means everything from `apple.jed` to `zebras.jed` to `apple.zebras.jed` will resolve to `127.0.0.1`.

    All of the following shell scripts assume you're cool with using your user id as your top-level domain, but feel to change them accordingly by replacing `$(whoami)` with your chosen TLD.

    echo "address=/.$(whoami)/127.0.0.1" | sudo tee -a $(brew --prefix)/etc/dnsmasq.conf
    echo "nameserver 127.0.0.1" | sudo tee /etc/resolver/$(whoami)

    Now we need to make sure that worked, by spinning up a server and hitting it.

    cd /Applications
    sleep 1 && open "http://some.domain.$(whoami):9520" &
    python -m SimpleHTTPServer 9520

    This should open a new browser that shows the contents of your Applications folder, like this:

    ![applications folder](/jed/6147872/raw/b44236dd3a3410b0b44226272a62f6688530b14b/screenshot-1.png)

    The browser might tell us that the DNS lookup failed, like this:

    ![failed dns](/jed/6147872/raw/df31903f98f34b0cb04ae3ca1154e2ece6e66a6f/screenshot-2.png)

    In this case, we'll need to restart the machine to make sure the setup above has taken effect.

    <a name="step-2"></a>
    #### Create a wildcard SSL certificate for each project

    Now that we know our DNS works, we need to create an SSL certificate for our development environment. Since (for good reason) browsers [won't trust][] certificates that cover an entire TLD, we need to create a wildcard certificate for each domain we want to use. Also, since we want to cover both any subdomain (*.yourproject.tld) _and_ the domain apex (yourproject.tld), we'll need to use [Subject Alternative Name][] extension.

    First, let's create a new directory named for our project and `cd` into it.

    mkdir ~/Desktop/myproject && cd $_

    Next, let's create a temporary configuration file, and feed it into `openssl` to create our certificate.

    cat > openssl.cnf <<-EOF
    [req]
    distinguished_name = req_distinguished_name
    x509_extensions = v3_req
    prompt = no
    [req_distinguished_name]
    CN = *.${PWD##*/}.$(whoami)
    [v3_req]
    keyUsage = keyEncipherment, dataEncipherment
    extendedKeyUsage = serverAuth
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = *.${PWD##*/}.$(whoami)
    DNS.2 = ${PWD##*/}.$(whoami)
    EOF

    openssl req \
    -new \
    -newkey rsa:2048 \
    -sha1 \
    -days 3650 \
    -nodes \
    -x509 \
    -keyout ssl.key \
    -out ssl.crt \
    -config openssl.cnf

    rm openssl.cnf

    Now we have two files in our project directory: `ssl.key`, the private key used to sign the certificate, and `ssl.crt`, the certificate itself.

    ![finder](/jed/6147872/raw/ddc046475c14ffa462fda80f1694255e280b7be7/screenshot-3.png)

    At this point we have almost everything we need to fire up a local HTTPS server, but there's one problem.

    ![certificate not trusted](/jed/6147872/raw/938a9c00fc5260310e605520d59f1aafa33e2e95/screenshot-4.png)

    Any content we try to serve over HTTPS from this domain gets [IMMA-LET-YOU-FINISH](http://knowyourmeme.com/memes/events/kanye-interrupts-imma-let-you-finish)ed by a scary message like the one above, warning us that the presented certificate is not trusted. This message differs by browser, and you might be tempted to go ahead and ignore it, but that's not a good habit to get into, and is likely to lead to development complexity down the road.

    There's a better way; we can tell OS X to trust the certificate we just created so that we don't have to see this screen ever again, by adding the certificate to our keychain.

    <a name="step-3"></a>
    #### Avoid HTTPS warnings by telling OS X to trust the certificate

    Since our certificate is self-signed, we'll always get a warning when using it for our HTTPS site. We need to use __Keychain Access__ to tell OS X to enhance its calm for this domain.

    ![keychain access](/jed/6147872/raw/8047f0a0459aba06eaa9d04e3012affe71ae3254/trust_certificate.gif)

    1. Open __Keychain Access__.

    open /Applications/Utilities/Keychain\ Access.app

    1. From the __Keychains__ list on the left, select __System__.

    1. Click the [] button under the list of certificates.

    1. Choose the `ssl.crt` file created in the previous step, make sure __System__ is selected for __Destination Keychain__, and click __Open__.

    1. When prompted, enter your password and click __Modify Keychain__.

    1. Click __Don't Trust__.

    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the [] button.

    1. In the popup window, click the ▶ button to the left of __Trust__, and select __Always Trust__ for __When using this certificate:__.

    1. Close the popup window.

    1. When prompted, enter your password again and click __Update Settings__.

    1. Close __Keychain Access__.

    <a name="step-4"></a>
    #### Bask in easy HTTPS

    Now that OS X knows that our self-signed certificate is legit, let's spin up an HTTPS server to make sure it all works. You can use Apache or Nginx or whatever you like, but here we'll use [nodejs][]:

    sleep 1 && open "https://myproject.$(whoami):8443" &
    sleep 1 && open "https://subdomain.myproject.$(whoami):8443" &
    node <<-EOF
    var https = require("https")
    var fs = require("fs")

    var options = {
    key: fs.readFileSync("ssl.key"),
    cert: fs.readFileSync("ssl.crt")
    }

    var server = https.createServer(options, function(req, res) {
    res.writeHead(200, {"Content-Type": "text/plain"})
    res.end("It worked!\n")
    })

    server.listen(8443, console.log)
    EOF

    As you can see, the correct content is being served by both the apex domain and subdomain.

    ![apex domain](/jed/6147872/raw/97f05c6ebafaf359dfe75270ea9492b4fa65d249/screenshot-5.png)

    ![subdomain](/jed/6147872/raw/7d78d97668b7891832da3fa1de41eb15338dc472/screenshot-6.png)

    This will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...__, and then __Confirm Security Exception___ when prompted.

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
    [top-level domain]: http://en.wikipedia.org/wiki/Top-level_domain
    [never touch the hosts file again]: http://www.echoditto.com/blog/never-touch-your-local-etchosts-file-os-x-again
    [reserves]: http://en.wikipedia.org/wiki/.local#mDNS_implementations
    [won't trust]: http://security.stackexchange.com/questions/6873/can-a-wildcard-ssl-certificate-be-issued-for-a-second-level-domain
    [Subject Alternative Name]: http://en.wikipedia.org/wiki/SubjectAltName
    [many developers]: http://pow.cx
    [nodejs]: http://nodejs.org
  22. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion how-to-set-up-stress-free-ssl-on-os-x.md
    Original file line number Diff line number Diff line change
    @@ -26,7 +26,7 @@ Let's get started.
    <a name="step-1"></a>
    #### Resolve a top-level domain for all development work

    The most common way to get a given host to resolve to your local machine is to manually edit your `/private/etc/hosts` file, which is annoyingly O(n<sup>2</sup>), since you need to add an entry for each subdomain of each project you're working on.
    The most common way to get a given host to resolve to your local machine is to manually edit your `/private/etc/hosts` file, which is annoyingly _O_(_n_<sup>_2_</sup>), since you need to add an entry for each subdomain of each project you're working on.

    With a little more work up front, we can streamline our development workflow by resolving a single [top-level domain][] (herein as _TLD_) to our development box and [never touch the hosts file again][].

  23. @jed jed renamed this gist Aug 6, 2013. 1 changed file with 0 additions and 0 deletions.
  24. @jed jed renamed this gist Aug 6, 2013. 1 changed file with 0 additions and 0 deletions.
  25. @jed jed renamed this gist Aug 6, 2013. 1 changed file with 0 additions and 0 deletions.
  26. @jed jed revised this gist Aug 6, 2013. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -126,17 +126,17 @@ Since our certificate is self-signed, we'll always get a warning when using it f

    1. From the __Keychains__ list on the left, select __System__.

    1. Click the :heavy_plus_sign: button under the list of certificates.
    1. Click the [] button under the list of certificates.

    1. Choose the `ssl.crt` file created in the previous step, make sure __System__ is selected for __Destination Keychain__, and click __Open__.

    1. When prompted, enter your password and click __Modify Keychain__.

    1. Click __Don't Trust__.

    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the :information_source: button.
    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the [] button.

    1. In the popup window, click the :arrow_forward: button to the left of __Trust__, and select __Always Trust__ for __When using this certificate:__.
    1. In the popup window, click the button to the left of __Trust__, and select __Always Trust__ for __When using this certificate:__.

    1. Close the popup window.

  27. @jed jed revised this gist Aug 6, 2013. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -174,7 +174,7 @@ As you can see, the correct content is being served by both the apex domain and

    ![subdomain](/jed/6147872/raw/7d78d97668b7891832da3fa1de41eb15338dc472/screenshot-6.png)

    This will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...___, and then __Confirm Security Exception___ when prompted.
    This will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...__, and then __Confirm Security Exception___ when prompted.

    [Homebrew]: http://brew.sh
    [Dnsmasq]: http://www.thekelleys.org.uk/dnsmasq/doc.html
  28. @jed jed revised this gist Aug 6, 2013. 2 changed files with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion README.md
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,4 @@
    ### How to set up simple, consistent, warning-free SSL on an OS X development machine
    ### How to set up stress-free SSL on an OS X development machine

    One of the best ways to reduce complexity (read: _stress_) in web development is to minimize the differences between your development and production environments. After being frustrated by attempts to unify the approach to SSL on my local machine and in production, I searched for a workflow that would make the protocol invisible to me between all environments.

    Binary file modified trust_certificate.gif
    Loading
    Sorry, something went wrong. Reload?
    Sorry, we cannot display this file.
    Sorry, this file is invalid so it cannot be displayed.
  29. @jed jed revised this gist Aug 6, 2013. 1 changed file with 3 additions and 3 deletions.
    6 changes: 3 additions & 3 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -126,17 +126,17 @@ Since our certificate is self-signed, we'll always get a warning when using it f

    1. From the __Keychains__ list on the left, select __System__.

    1. Click the __[+]__ button under the list of certificates.
    1. Click the :heavy_plus_sign: button under the list of certificates.

    1. Choose the `ssl.crt` file created in the previous step, make sure __System__ is selected for __Destination Keychain__, and click __Open__.

    1. When prompted, enter your password and click __Modify Keychain__.

    1. Click __Don't Trust__.

    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the [i] button.
    1. Select the newly imported certificate, which should appear at the bottom of the certificate list, and click the :information_source: button.

    1. In the popup window, click the button to the left of __Trust__, and select __Always Trust__ for __When using this certificate:__.
    1. In the popup window, click the :arrow_forward: button to the left of __Trust__, and select __Always Trust__ for __When using this certificate:__.

    1. Close the popup window.

  30. @jed jed revised this gist Aug 6, 2013. 1 changed file with 4 additions and 4 deletions.
    8 changes: 4 additions & 4 deletions README.md
    Original file line number Diff line number Diff line change
    @@ -107,7 +107,7 @@ Now we have two files in our project directory: `ssl.key`, the private key used

    At this point we have almost everything we need to fire up a local HTTPS server, but there's one problem.

    [!certificate not trusted](/jed/6147872/raw/938a9c00fc5260310e605520d59f1aafa33e2e95/screenshot-4.png)
    ![certificate not trusted](/jed/6147872/raw/938a9c00fc5260310e605520d59f1aafa33e2e95/screenshot-4.png)

    Any content we try to serve over HTTPS from this domain gets [IMMA-LET-YOU-FINISH](http://knowyourmeme.com/memes/events/kanye-interrupts-imma-let-you-finish)ed by a scary message like the one above, warning us that the presented certificate is not trusted. This message differs by browser, and you might be tempted to go ahead and ignore it, but that's not a good habit to get into, and is likely to lead to development complexity down the road.

    @@ -118,7 +118,7 @@ There's a better way; we can tell OS X to trust the certificate we just created

    Since our certificate is self-signed, we'll always get a warning when using it for our HTTPS site. We need to use __Keychain Access__ to tell OS X to enhance its calm for this domain.

    [keychain access](/jed/6147872/raw/8047f0a0459aba06eaa9d04e3012affe71ae3254/trust_certificate.gif)
    ![keychain access](/jed/6147872/raw/8047f0a0459aba06eaa9d04e3012affe71ae3254/trust_certificate.gif)

    1. Open __Keychain Access__.

    @@ -170,9 +170,9 @@ Now that OS X knows that our self-signed certificate is legit, let's spin up an

    As you can see, the correct content is being served by both the apex domain and subdomain.

    [!apex domain](/jed/6147872/raw/97f05c6ebafaf359dfe75270ea9492b4fa65d249/screenshot-5.png)
    ![apex domain](/jed/6147872/raw/97f05c6ebafaf359dfe75270ea9492b4fa65d249/screenshot-5.png)

    [!subdomain](/jed/6147872/raw/7d78d97668b7891832da3fa1de41eb15338dc472/screenshot-6.png)
    ![subdomain](/jed/6147872/raw/7d78d97668b7891832da3fa1de41eb15338dc472/screenshot-6.png)

    This will satisfy Chrome and Safari, but since Firefox doesn't inherit the same keychain from OS X, it will tell you that the certificate is untrusted. In this case, click __I Understand the Risks__, then __Add Exception...___, and then __Confirm Security Exception___ when prompted.