<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: ATAC Jan</title>
    <description>The latest articles on DEV Community by ATAC Jan (@can_atac).</description>
    <link>https://dev.to/can_atac</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F13909%2F042b7b21-b222-4c86-a7ab-23313d41be15.jpg</url>
      <title>DEV Community: ATAC Jan</title>
      <link>https://dev.to/can_atac</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/can_atac"/>
    <language>en</language>
    <item>
      <title>Implement HTTPS for API Access on localhost with Nginx </title>
      <dc:creator>ATAC Jan</dc:creator>
      <pubDate>Wed, 09 Oct 2019 08:25:49 +0000</pubDate>
      <link>https://dev.to/can_atac/implement-https-for-api-access-on-localhost-with-nginx-22e6</link>
      <guid>https://dev.to/can_atac/implement-https-for-api-access-on-localhost-with-nginx-22e6</guid>
      <description>&lt;p&gt;Our open source app, &lt;a href="https://github.com/BREDFactory/flex-server"&gt;FlexOffice&lt;/a&gt; which provides employees a way to find and book a desk in, has a technical stack based on React with Typescript, Node and mongodb.&lt;/p&gt;

&lt;p&gt;Our app is composed of two-parts : a web Front which access resources through JSON calls to RestAPI provided by a web back-end.&lt;br&gt;
Our production target is to have a Front-end with a different URL than the Back-end. Front-end makes a &lt;em&gt;cross-domain request&lt;/em&gt; to access back-end resources. As such, we are assigned to follow &lt;a href="https://www.moesif.com/blog/technical/cors/Authoritative-Guide-to-CORS-Cross-Origin-Resource-Sharing-for-REST-APIs/"&gt;CORS&lt;/a&gt; policy by specifying the allowed &lt;em&gt;origin&lt;/em&gt; URL to get resources.&lt;/p&gt;

&lt;p&gt;As we have at least two different ports on localhost, serving each the Front and the Back end, we make &lt;em&gt;de facto&lt;/em&gt; cross-domain requests when accessing back-end APIs.&lt;/p&gt;

&lt;p&gt;In this post, we'll use a radically different domain name for our back-end : &lt;code&gt;local.web.com&lt;/code&gt;, but you're free to choose another one, and we'd like to secure API access and test it locally.&lt;/p&gt;

&lt;p&gt;To attach this domain name to our IPv4 loopback (127.0.0.1), you must add a record on &lt;code&gt;/etc/hosts&lt;/code&gt;as such :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost
127.0.0.1       local.web.com 
(...)
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To use HTTPS protocol, we need local self-signed certificates, plus a root CA certificate which will give full confidence to our browsers (with a green lock).&lt;/p&gt;

&lt;p&gt;Let's create our local certificates :&lt;br&gt;
Let's just follow this &lt;a href="https://gist.github.com/cecilemuller/9492b848eb8fe46d462abeb26656c4f8"&gt;great gist here&lt;/a&gt;:&lt;/p&gt;

&lt;p&gt;Generate :&lt;br&gt;
The root certificate : &lt;code&gt;RootCA.crt&lt;/code&gt;&lt;br&gt;
The key : &lt;code&gt;RootCA.key&lt;/code&gt;&lt;br&gt;
The pem : &lt;code&gt;RootCA.pem&lt;/code&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl req -x509 -nodes -new -sha256 -days 1024 -newkey rsa:2048 -keyout RootCA.key -out RootCA.pem -subj "/C=US/CN=FlexOffice-Root-CA"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl x509 -outform pem -in RootCA.pem -out RootCA.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;First, create a file &lt;code&gt;domains.ext&lt;/code&gt; that lists all your local domains&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;authorityKeyIdentifier=keyid,issuer
basicConstraints=CA:FALSE
keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
subjectAltName = @alt_names
[alt_names]
DNS.1 = local.web.com
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Generate &lt;code&gt;localhost.key&lt;/code&gt;, &lt;code&gt;localhost.csr&lt;/code&gt;, and &lt;code&gt;localhost.crt&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl req -new -nodes -newkey rsa:2048 -keyout localhost.key -out localhost.csr -subj "/C=FR/ST=IDF/L=Paris/O=FlexOffice-Certificates/CN=local.web.com"
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;





&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ openssl x509 -req -sha256 -days 1024 -in localhost.csr -CA RootCA.pem -CAkey RootCA.key -CAcreateserial -extfile domains.ext -out localhost.crt
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Once you get your certificate, you can declare them in &lt;code&gt;nginx.conf&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The back end is accessible locally at &lt;code&gt;http://local.web.com:3000&lt;/code&gt;, but we'd like to avoid that and force API users to pass through &lt;code&gt;https://local.web.com:443&lt;/code&gt;. Our Front is located at &lt;code&gt;http://local.web.com:1025&lt;/code&gt;, and must be authorized to access the Back-end. This can be done by adding a header with &lt;code&gt;Access-Control-Allow-Origin&lt;/code&gt; valued with the Front URL a.k.a the 'origin' of the request.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# nginx Configuration File
    # http://wiki.nginx.org/Configuration


    # Run as a less privileged user for security reasons.
    #user nginx;

    worker_processes auto;

    events {
        worker_connections 1024;
    }

    #pid /var/run/nginx.pid;

    http {

        sendfile        off;

        upstream backend {
            server local.web.com:3000;
        }        

    #Redirect to https, using 307 instead of 301 to preserve post data
        server {
            listen 80 default_server;
            server_name _;
            return 307 https://$host$request_uri;
        }

        server {
            listen 443 ssl ;

            server_name local.web.com;

            # Protect against the BEAST attack by not using SSLv3 at all. If you need to support older browsers (IE6) you may need to add
            # SSLv3 to the list of protocols below.
            ssl_protocols TLSv1.2 TLSv1.1 TLSv1 ;


            # Ciphers set to best allow protection from Beast, while providing forwarding secrecy, as defined by Mozilla - https://wiki.mozilla.org/Security/Server_Side_TLS#Nginx

            ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-DSS-AES128-GCM-SHA256:kEDH+AESGCM:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA:ECDHE-ECDSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-DSS-AES128-SHA256:DHE-RSA-AES256-SHA256:DHE-DSS-AES256-SHA:DHE-RSA-AES256-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:ECDHE-RSA-RC4-SHA:ECDHE-ECDSA-RC4-SHA:AES128:AES256:RC4-SHA:HIGH:!aNULL:!eNULL:!EXPORT:!DES:!3DES:!MD5:!PSK;
            ssl_prefer_server_ciphers on;

            # Optimize SSL by caching session parameters for 10 minutes. This cuts down on the number of expensive SSL handshakes.
            # The handshake is the most CPU-intensive operation, and by default it is re-negotiated on every new/parallel connection.
            # By enabling a cache (of type "shared between all Nginx workers"), we tell the client to re-use the already negotiated state.
            # Further optimization can be achieved by raising keepalive_timeout, but that shouldn't be done unless you serve primarily HTTPS.
            ssl_session_cache shared:SSL:10m; # a 1mb cache can hold about 4000 sessions, so we can hold 40000 sessions
            ssl_session_timeout 24h;


            # Use a higher keepalive timeout to reduce the need for repeated handshakes
            keepalive_timeout 300; # up from 75 secs default

            # remember the certificate for a year and automatically connect to HTTPS
            #add_header Strict-Transport-Security 'max-age=31536000; includeSubDomains';
            add_header Strict-Transport-Security 'max-age=0;';

            ssl_certificate /&amp;lt;CERTIFICATE_PATH&amp;gt;/&amp;lt;your_certificate&amp;gt;.crt; // &amp;lt;- CHANGE IT !
            ssl_certificate_key /&amp;lt;KEY_PATH&amp;gt;/&amp;lt;your_key&amp;gt;.key; // &amp;lt;- CHANGE IT !

            access_log /&amp;lt;LOG_PATH&amp;gt;/&amp;lt;your_log&amp;gt;.log; // &amp;lt;- CHANGE IT !

            location / {

                add_header 'Access-Control-Allow-Origin'  '*' always;
                add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS' always;
                    #
                    # Custom headers and headers various browsers *should* be OK with but aren't
                    #
                add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With' always;             add_header 'Access-Control-Expose-Headers' 'Access-Control-Allow-Origin';
                add_header    'Access-Control-Allow-Credentials' 'true' always;

                proxy_pass http://local.web.com:3000;
                proxy_set_header Connection "";
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $remote_addr;

                if ($request_method = 'OPTIONS') {

                    #
                    # Tell client that this pre-flight info is valid for 20 days
                    #
                    add_header 'Access-Control-Max-Age' 1728000;
                    add_header 'Content-Type' 'application/json;';
                    #add_header 'Content-Length' 0;
                    return 204;
                }
                if ($request_method = 'POST') {
                    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
                }
                if ($request_method = 'GET') {
                    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
                }
                proxy_pass_request_headers      on;
            }
            location /api/ {

                proxy_pass http://local.web.com:3000;

                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_set_header authorization $http_authorization;
                proxy_pass_request_headers      on;
                proxy_no_cache $cookie_nocache  $arg_nocache$arg_comment;
                proxy_no_cache $http_pragma     $http_authorization;
                proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment;
                proxy_cache_bypass $http_pragma $http_authorization;

                    #
                    # Custom headers and headers various browsers *should* be OK with but aren't
                    #

                if ($request_method = 'OPTIONS') {

                    #
                    # Tell client that this pre-flight info is valid for 20 days
                    #
                    add_header 'Access-Control-Max-Age' 1728000;
                    add_header 'Access-Control-Allow-Origin'  'http://local.web.com:1025' always;
                    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD' always;
                    add_header 'Access-Control-Allow-Headers' 'authorization, Origin, X-Requested-With, Content-Type, Accept' always;
                    add_header  'Access-Control-Allow-Credentials' 'true' always;
                    return 204;
                }
                if ($request_method = 'POST') {
                    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
                    add_header 'Access-Control-Allow-Origin'  'http://local.web.com:1025' always;
                    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD' always;
                    add_header 'Access-Control-Allow-Headers' 'authorization, Origin, X-Requested-With, Content-Type, Accept' always;
                    add_header    'Access-Control-Allow-Credentials' 'true' always;
                }
                if ($request_method = 'GET') {
                    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
                    add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
                    add_header 'Access-Control-Allow-Origin'  'http://local.web.com:1025' always;
                    add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS, HEAD' always;
                    add_header 'Access-Control-Allow-Headers' 'authorization, Origin, X-Requested-With, Content-Type, Accept' always;
                    add_header    'Access-Control-Allow-Credentials' 'true' always;

                }

            }

        }
    }



&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Reload or stop and restart nginx.&lt;br&gt;
You could run &lt;code&gt;nginx -t&lt;/code&gt; to test your configuration file and verify the file path.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nginx -t
nginx: the configuration file /usr/local/etc/nginx/nginx.conf syntax is ok
nginx: configuration file /usr/local/etc/nginx/nginx.conf test is successful
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Start nginx&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ nginx
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can now call your back-end through HTTPS. &lt;/p&gt;

&lt;p&gt;Please feel free to comment or suggest improvements.&lt;/p&gt;

&lt;p&gt;Tested with environment :&lt;br&gt;
macOS v10.13.6 (High Sierra)&lt;br&gt;
node v10.15&lt;br&gt;
npm v6.4.1&lt;br&gt;
MongoDB shell version v3.6.6&lt;br&gt;
curl 7.60.0&lt;br&gt;
nginx v1.5.7&lt;br&gt;
Firefox Quantum 69.0.2 (64 bits)&lt;br&gt;
Chrome v77.0.3865.90 (official Build) (64 bits)&lt;br&gt;
Safari v13.0.1 (13608.2.11.1.10)&lt;/p&gt;

&lt;p&gt;Useful resources :&lt;/p&gt;

&lt;h6&gt;
  
  
  [1] CORS, detailed explanation, &lt;a href="https://www.moesif.com/blog/technical/cors/Authoritative-Guide-to-CORS-Cross-Origin-Resource-Sharing-for-REST-APIs/"&gt;https://www.moesif.com/blog/technical/cors/Authoritative-Guide-to-CORS-Cross-Origin-Resource-Sharing-for-REST-APIs/&lt;/a&gt;
&lt;/h6&gt;

&lt;h6&gt;
  
  
  [2] Root CA creation, &lt;a href="https://gist.github.com/cecilemuller/9492b848eb8fe46d462abeb26656c4f8"&gt;https://gist.github.com/cecilemuller/9492b848eb8fe46d462abeb26656c4f8&lt;/a&gt;
&lt;/h6&gt;

</description>
      <category>nginx</category>
      <category>certificate</category>
      <category>api</category>
    </item>
  </channel>
</rss>
