├── .gitignore ├── README.md ├── cfssl ├── ca │ └── .placeholder ├── config │ ├── ca-config.json │ ├── int-to-client-config.json │ └── root-to-int-config.json ├── csr │ ├── foo-acme-com-csr.json │ ├── intermediate-ca-csr.json │ └── root-ca-csr.json ├── gen-foo-acme-cert.sh ├── gen-root-and-int-ca.sh ├── init-env.sh └── issued-certs │ └── .placeholder ├── images └── site-green-padlock.png └── nginx.conf /.gitignore: -------------------------------------------------------------------------------- 1 | cfssl/ca 2 | cfssl/issued-certs 3 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Starter Sample to run Nginx with TLS Support 2 | 3 | ## Introduction 4 | 5 | This repo is just a starting point to serve web pages with Nginx using TLS. It is mainly for educational purposes and far far away being production ready so please be careful!! 6 | 7 | TLS certifices are going to be generated using [cfssl](https://github.com/cloudflare/cfssl) of Cloudflare. 8 | 9 | ## Certificate and Private Key Generation using CFSSL 10 | 11 | `cfssl` folder has all the configuration to generate a certificate for Nginx site published to `foo.acme.com`. 12 | 13 | Steps for generating the CAs, certificates and the private keys are below. 14 | 15 | 1. Open a terminal and move to `cfssl` folder, run `./init-env.sh` command to cleanup and initialize the folders. 16 | 2. In `cfssl` folder, run `./gen-root-and-int-ca.sh` to generate Root CA and Intermediate CA certificates and private keys. They will appear under `cfssl/ca` folder. 17 | 3. In `cfssl` folder, run `./gen-foo-acme-cert.sh` to generate a certificate and sign with the intermediate certificate for domain `foo.acme.com`, the certificate and private key (to be fed to Nginx) will appear under `cfssl/issued-certs` folder. 18 | 19 | Below is an example run given: 20 | 21 | ```bash 22 | ➜ nginx-tls git:(master) ✗ cd cfssl 23 | ➜ cfssl git:(master) ✗ ./init-env.sh 24 | ➜ cfssl git:(master) ✗ ./gen-root-and-int-ca.sh 25 | 2018/01/01 13:07:44 [INFO] generate received request 26 | 2018/01/01 13:07:44 [INFO] received CSR 27 | 2018/01/01 13:07:44 [INFO] generating key: rsa-4096 28 | 2018/01/01 13:07:48 [INFO] encoded CSR 29 | 2018/01/01 13:07:48 [INFO] signed certificate with serial number 679137305797398349438266964075278223547092180893 30 | 2018/01/01 13:07:51 [INFO] generate received request 31 | 2018/01/01 13:07:51 [INFO] received CSR 32 | 2018/01/01 13:07:51 [INFO] generating key: rsa-4096 33 | 2018/01/01 13:07:55 [INFO] encoded CSR 34 | 2018/01/01 13:07:55 [INFO] signed certificate with serial number 571806828382796651294254523547316694515355387146 35 | 2018/01/01 13:07:58 [INFO] signed certificate with serial number 585261831146609802545074706562831189681387775313 36 | ➜ cfssl git:(master) ✗ ./gen-foo-acme-cert.sh 37 | 2018/01/01 13:08:07 [INFO] generate received request 38 | 2018/01/01 13:08:07 [INFO] received CSR 39 | 2018/01/01 13:08:07 [INFO] generating key: rsa-2048 40 | 2018/01/01 13:08:08 [INFO] encoded CSR 41 | 2018/01/01 13:08:08 [INFO] signed certificate with serial number 165288814616737824677465696238945145826100141548 42 | 2018/01/01 13:08:08 [WARNING] This certificate lacks a "hosts" field. This makes it unsuitable for 43 | websites. For more information see the Baseline Requirements for the Issuance and Management 44 | of Publicly-Trusted Certificates, v.1.1.6, from the CA/Browser Forum (https://cabforum.org); 45 | specifically, section 10.2.3 ("Information Requirements"). 46 | 47 | ➜ cfssl git:(master) ✗ tree ca 48 | ca 49 | ├── intermediate-ca-key.pem 50 | ├── intermediate-ca.csr 51 | ├── intermediate-ca.pem 52 | ├── root-ca-key.pem 53 | ├── root-ca.csr 54 | └── root-ca.pem 55 | 56 | 0 directories, 6 files 57 | ➜ cfssl git:(master) ✗ tree issued-certs 58 | issued-certs 59 | ├── foo.acme.com-key.pem 60 | ├── foo.acme.com.csr 61 | └── foo.acme.com.pem 62 | 63 | 0 directories, 3 files 64 | ``` 65 | 66 | ## Trusting the Root CA 67 | 68 | In order to see the green padlock in the browser's address bar, the computer needs to trust either the Root CA or the intermediate CA. 69 | 70 | According to the platform you are running on, Root CA should be trusted. 71 | 72 | If you just want to test on command line using curl or OK with the warning messages on the browser, you do NOT need to trust the Root CA. 73 | 74 | ## Edit Host File 75 | 76 | As you can see the certificates are issues for `acme.com` domain and the Nginx site will be run under `foo.acme.com`. Therefore depending on your system add below entry to your hosts file. 77 | 78 | On a Mac, it would be like editing `/etc/hosts` file and adding below line. 79 | 80 | ``` 81 | 127.0.0.1 foo.acme.com 82 | ``` 83 | 84 | ## Running the Nginx Container 85 | 86 | Open a terminal and navigate to the root folder of the remo and issue the following command. 87 | 88 | ``` 89 | docker run --rm -p 443:443 \ 90 | -v $(pwd)/cfssl/issued-certs:/etc/nginx/certs \ 91 | -v $(pwd)/nginx.conf:/etc/nginx/conf.d/default.conf \ 92 | nginx:1.10 93 | ``` 94 | 95 | ## Testing 96 | 97 | If you trusted the Root CA of `acme.com`, opening a web browser and navigating to the `https://foo.acme.com` page, you should see no errors and warnings. The page should look like below. 98 | 99 | ![nginx tls view](https://github.com/gokhansengun/nginx-tls/raw/master/images/site-green-padlock.png "Nginx TLS View") 100 | 101 | If you just want to see it with `curl`, use below command. 102 | 103 | ``` 104 | curl --cacert ./cfssl/ca/root-ca.pem https://foo.acme.com 105 | ``` 106 | 107 | ## Note 108 | 109 | Please note that this is a test setup. Normally, we should be issueing another certificate and private key from the intermediate certificate and use it in the web server but we skipped that for the brevity. -------------------------------------------------------------------------------- /cfssl/ca/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gokhansengun/nginx-tls/0c849a7ca6a044d0cf9b8149eea124c5f7c7cdab/cfssl/ca/.placeholder -------------------------------------------------------------------------------- /cfssl/config/ca-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "signing": { 3 | "default": { 4 | "expiry": "8760h" 5 | }, 6 | "profiles": { 7 | "nginx-web": { 8 | "usages": ["signing", "key encipherment", "server auth", "client auth"], 9 | "expiry": "8760h" 10 | } 11 | } 12 | } 13 | } 14 | 15 | -------------------------------------------------------------------------------- /cfssl/config/int-to-client-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "signing": { 3 | "profiles": { 4 | "CA": { 5 | "usages": ["cert sign"], 6 | "expiry": "8760h" 7 | } 8 | }, 9 | "default": { 10 | "usages": ["digital signature"], 11 | "expiry": "8760h" 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /cfssl/config/root-to-int-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "signing": { 3 | "default": { 4 | "usages": ["digital signature", "cert sign", "crl sign", "signing"], 5 | "expiry": "262800h", 6 | "is_ca": true, 7 | "max_path_len": 1 8 | } 9 | } 10 | } -------------------------------------------------------------------------------- /cfssl/csr/foo-acme-com-csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "foo.acme.com", 3 | "key": { 4 | "algo": "rsa", 5 | "size": 2048 6 | }, 7 | "names": [ 8 | { 9 | "C": "TR", 10 | "L": "Istanbul", 11 | "O": "Acme", 12 | "OU": "Foo Department of Acme" 13 | } 14 | ], 15 | "hosts": ["foo.acme.com"] 16 | } 17 | -------------------------------------------------------------------------------- /cfssl/csr/intermediate-ca-csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Istanbul Coders Level 1 CA", 3 | "key": { 4 | "algo": "rsa", 5 | "size": 4096 6 | }, 7 | "names": [ 8 | { 9 | "C": "TR", 10 | "ST": "Marmara", 11 | "L": "Istanbul", 12 | "O": "Coders", 13 | "OU": "Coders of Istanbul" 14 | } 15 | ], 16 | "ca": { 17 | "expiry": "42720h" 18 | }, 19 | "hosts": ["acme.com"] 20 | } 21 | 22 | -------------------------------------------------------------------------------- /cfssl/csr/root-ca-csr.json: -------------------------------------------------------------------------------- 1 | { 2 | "CN": "Istanbul Coders Root CA", 3 | "key": { 4 | "algo": "rsa", 5 | "size": 4096 6 | }, 7 | "names": [ 8 | { 9 | "C": "TR", 10 | "ST": "Marmara", 11 | "L": "Istanbul", 12 | "O": "Coders", 13 | "OU": "Coders of Istanbul" 14 | } 15 | ], 16 | "ca": { 17 | "expiry": "262800h" 18 | } 19 | } 20 | 21 | -------------------------------------------------------------------------------- /cfssl/gen-foo-acme-cert.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | ## STEP 1: Generate a certificate and private key for foo.acme.com 4 | ## STEP 2: Sign foo.acme.com cert by the intermediate CA's private Key 5 | docker run --rm -v $(pwd):/pad \ 6 | -w /pad \ 7 | cfssl/cfssl:1.2.0 \ 8 | gencert \ 9 | -ca ca/intermediate-ca.pem \ 10 | -ca-key ca/intermediate-ca-key.pem \ 11 | -config config/int-to-client-config.json \ 12 | csr/foo-acme-com-csr.json > /tmp/foo-acme-out.json 13 | 14 | cat /tmp/foo-acme-out.json | \ 15 | docker run -i --rm -v $(pwd):/pad \ 16 | -w /pad/issued-certs \ 17 | --entrypoint=cfssljson cfssl/cfssl:1.2.0 \ 18 | -bare foo.acme.com 19 | 20 | # Concat Intermediate CA's cert to foo.acme.com's 21 | cat ./issued-certs/foo.acme.com.pem > /tmp/foo-acme-com.pem 22 | cat ./ca/intermediate-ca.pem >> /tmp/foo-acme-com.pem 23 | cat ./ca/root-ca.pem >> /tmp/foo-acme-com.pem 24 | 25 | mv /tmp/foo-acme-com.pem ./issued-certs/foo.acme.com.pem 26 | 27 | # remove the intermediate output file 28 | rm -rf /tmp/foo-acme-out.json 29 | -------------------------------------------------------------------------------- /cfssl/gen-root-and-int-ca.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | ## STEP 1: Generate the Root CA cert and private key 4 | docker run --rm -v $(pwd):/pad \ 5 | -w /pad \ 6 | cfssl/cfssl:1.2.0 \ 7 | genkey \ 8 | -initca csr/root-ca-csr.json > /tmp/root-ca-out.json 9 | 10 | cat /tmp/root-ca-out.json | \ 11 | docker run -i --rm -v $(pwd):/pad \ 12 | -w /pad/ca \ 13 | --entrypoint=cfssljson cfssl/cfssl:1.2.0 \ 14 | -bare root-ca 15 | 16 | # remove the intermediate output file 17 | rm -rf /tmp/root-ca-out.json 18 | 19 | ## STEP 2: Generate Intermediate CA cert and private key 20 | docker run --rm -v $(pwd):/pad \ 21 | -w /pad \ 22 | cfssl/cfssl:1.2.0 \ 23 | genkey \ 24 | -initca csr/intermediate-ca-csr.json > /tmp/intermediate-ca-out.json 25 | 26 | 27 | cat /tmp/intermediate-ca-out.json | 28 | docker run -i --rm -v $(pwd):/pad \ 29 | -w /pad/ca \ 30 | --entrypoint=cfssljson cfssl/cfssl:1.2.0 \ 31 | -bare intermediate-ca 32 | 33 | # remove the intermediate output file 34 | rm -rf /tmp/intermediate-ca-out.json 35 | 36 | ## STEP 3: Sign the Intermediate CA with Root CA's Private Key 37 | docker run --rm -v $(pwd):/pad \ 38 | -w /pad \ 39 | cfssl/cfssl:1.2.0 \ 40 | sign \ 41 | -hostname acme.com \ 42 | -ca ca/root-ca.pem \ 43 | -ca-key ca/root-ca-key.pem \ 44 | -config config/root-to-int-config.json \ 45 | ca/intermediate-ca.csr > /tmp/intermediate-ca-sign-out.json 46 | 47 | cat /tmp/intermediate-ca-sign-out.json | \ 48 | docker run -i --rm -v $(pwd):/pad \ 49 | -w /pad/ca \ 50 | --entrypoint=cfssljson cfssl/cfssl:1.2.0 \ 51 | -bare intermediate-ca 52 | 53 | # remove the intermediate output file 54 | rm -rf /tmp/intermediate-ca-sign-out.json -------------------------------------------------------------------------------- /cfssl/init-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash -eu 2 | 3 | # Cleanup just in case not removed at the last run 4 | rm -rf ./ca/* 5 | rm -rf ./issued-certs/* 6 | 7 | mkdir -p ./ca 8 | mkdir -p ./issued-certs 9 | -------------------------------------------------------------------------------- /cfssl/issued-certs/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gokhansengun/nginx-tls/0c849a7ca6a044d0cf9b8149eea124c5f7c7cdab/cfssl/issued-certs/.placeholder -------------------------------------------------------------------------------- /images/site-green-padlock.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gokhansengun/nginx-tls/0c849a7ca6a044d0cf9b8149eea124c5f7c7cdab/images/site-green-padlock.png -------------------------------------------------------------------------------- /nginx.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 443 ssl; 3 | server_name foo.acme.com; 4 | ssl_certificate /etc/nginx/certs/foo.acme.com.pem; 5 | ssl_certificate_key /etc/nginx/certs/foo.acme.com-key.pem; 6 | ssl_protocols TLSv1 TLSv1.1 TLSv1.2; 7 | ssl_prefer_server_ciphers on; 8 | 9 | location / { 10 | root /usr/share/nginx/html; 11 | index index.html index.htm; 12 | } 13 | 14 | # redirect server error pages to the static page /50x.html 15 | # 16 | error_page 500 502 503 504 /50x.html; 17 | location = /50x.html { 18 | root /usr/share/nginx/html; 19 | } 20 | } --------------------------------------------------------------------------------