<?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: Tobi Akanji</title>
    <description>The latest articles on DEV Community by Tobi Akanji (@tboyak).</description>
    <link>https://dev.to/tboyak</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%2F287365%2F1ed368ba-014d-4652-b8da-fa33370beba1.png</url>
      <title>DEV Community: Tobi Akanji</title>
      <link>https://dev.to/tboyak</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tboyak"/>
    <language>en</language>
    <item>
      <title>Add New User to EC2 Linux Instance with Google MFA: Ubuntu</title>
      <dc:creator>Tobi Akanji</dc:creator>
      <pubDate>Wed, 19 Jan 2022 21:52:52 +0000</pubDate>
      <link>https://dev.to/tboyak/add-new-user-to-ec2-linux-instance-with-google-mfa-ubuntu-35hg</link>
      <guid>https://dev.to/tboyak/add-new-user-to-ec2-linux-instance-with-google-mfa-ubuntu-35hg</guid>
      <description>&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrxxpf825zgzwd3nzozo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fbrxxpf825zgzwd3nzozo.png" alt="User administration"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;This tutorial is not for absolute beginners, but persons with some experience in Linux, CLI and a bit of cloud technology.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;The actions listed here are to be carried out by a superuser (preferrably the root user), unless otherwise stated within context.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Where your machine or instance does not have the &lt;code&gt;nano&lt;/code&gt; text editor, use &lt;code&gt;vi&lt;/code&gt; (for the vim text editor).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;&amp;lt;...&amp;gt;&lt;/code&gt; represents placeholders in code snippets and configurations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;[...]&lt;/code&gt; represents optional parameters in code snippets and configurations.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ensure you are logged in to the EC2 instance as a superuser.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Get Key Pair for The New User Account
&lt;/h2&gt;

&lt;p&gt;You can generate a new key pair, or use an existing one.&lt;/p&gt;

&lt;p&gt;To generate a new key pair&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ec2 create-key-pair &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--key-name&lt;/span&gt; &amp;lt;key-pair-name&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--key-type&lt;/span&gt; rsa &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"KeyMaterial"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--output&lt;/span&gt; text &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &amp;lt;key-pair-name&amp;gt;.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure to get this file and keep it in a safe place where you will not lose it, e.g.:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;in a secure and accessible cloud storage.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;in an accessible key vault.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If your local machine is a Linux or Mac, ensure only the current user has access to this file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;400 &amp;lt;key-pair-name&amp;gt;.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retrieve the public key from the key pair&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh-keygen &lt;span class="nt"&gt;-y&lt;/span&gt; &lt;span class="nt"&gt;-f&lt;/span&gt; /&amp;lt;path_to_key_pair&amp;gt;/&amp;lt;key-pair-name&amp;gt;.pem
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you would be using the same private key as that which was used to launch the instance, you can get the public key by running the follow command&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;TOKEN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; PUT &lt;span class="s2"&gt;"http://169.254.169.254/latest/api/token"&lt;/span&gt; &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token-ttl-seconds: 21600"&lt;/span&gt;&lt;span class="sb"&gt;`&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
&lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; curl &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"X-aws-ec2-metadata-token: &lt;/span&gt;&lt;span class="nv"&gt;$TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; –v http://169.254.169.254/latest/meta-data/public-keys/0/openssh-key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copy the public key and keep it for later use.&lt;/p&gt;

&lt;h2&gt;
  
  
  Create New User
&lt;/h2&gt;

&lt;p&gt;Switch to root&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;su -
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The above might request for password that you did not set, nor do you want to keep inputting passwords. In such situations, go with the below snippet&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add new user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;adduser &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If user is to be a superuser&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;adduser &amp;lt;username&amp;gt; &lt;span class="nt"&gt;--disabled-password&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Make User A Superuser
&lt;/h2&gt;

&lt;p&gt;Add User to Sudo Group&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;usermod &lt;span class="nt"&gt;-aG&lt;/span&gt; &lt;span class="nb"&gt;sudo&lt;/span&gt; &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;-aG&lt;/code&gt;: Append to group.&lt;/p&gt;

&lt;p&gt;Verify user belongs to the superusers group&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;groups&lt;/span&gt; &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Update The Superuser to Not Use Password
&lt;/h2&gt;

&lt;p&gt;Superusers are usually not expected to need password for access once authenticated. Hence, the convention overlooking password authorization for superusers.&lt;/p&gt;

&lt;p&gt;Open the sudo permissions file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;visudo
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add this to the opened file in the editor, save the file and exit the editor.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&amp;lt;username&amp;gt; &lt;span class="nv"&gt;ALL&lt;/span&gt;&lt;span class="o"&gt;=(&lt;/span&gt;ALL&lt;span class="o"&gt;)&lt;/span&gt; NOPASSWD:ALL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To prevent the need for using a password for the user at any password prompt, delete the user password&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;passwd &lt;span class="nt"&gt;-d&lt;/span&gt; &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Henceforth, just press Enter whenever there is a password prompt, and you get authenticated.&lt;/p&gt;

&lt;p&gt;In the case where the user is not a superuser, and you used the &lt;code&gt;--disabled-password&lt;/code&gt; flag you can set a new password for the user by the &lt;code&gt;root&lt;/code&gt; user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;passwd &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Follow the prompts and press &lt;code&gt;Enter&lt;/code&gt; when done.&lt;/p&gt;

&lt;h2&gt;
  
  
  Implement SSH Authentication
&lt;/h2&gt;

&lt;p&gt;Switch to the new user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;su - &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Add .ssh directory to the new user’s home directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; .ssh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create the authorized_keys file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;touch&lt;/span&gt; .ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restrict read-write access to the new user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod &lt;/span&gt;600 .ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Update the new user account credentials by pasting the public key into the file&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nano .ssh/authorized_keys
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file and close the editor.&lt;/p&gt;

&lt;p&gt;You can also verify the user belongs to the superusers group&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;id&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You should get a similar output as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;uid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1004&lt;span class="o"&gt;(&lt;/span&gt;&amp;lt;username&amp;gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nv"&gt;gid&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1004&lt;span class="o"&gt;(&lt;/span&gt;&amp;lt;username&amp;gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;groups&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;1004&lt;span class="o"&gt;(&lt;/span&gt;&amp;lt;username&amp;gt;,sudo&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Implement Google Auth MFA
&lt;/h2&gt;

&lt;p&gt;Download the Google Auth mobile app on your mobile device if you do not have it. For Android, it is available on Play Store.&lt;/p&gt;

&lt;p&gt;Login with your Google account to the mobile app.&lt;/p&gt;

&lt;p&gt;Ensure you are on the new user, else switch to the new user&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;su - &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Install the Google Auth app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;yum &lt;span class="nb"&gt;install &lt;/span&gt;google-authenticator &lt;span class="nt"&gt;-y&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Initialize the app&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;google-authenticator
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the Google Auth mobile app, press the plus button ➕ and scan the QR code in the CLI.&lt;/p&gt;

&lt;p&gt;You can rename the new authentication on your mobile app to something more intuitive.&lt;/p&gt;

&lt;p&gt;Answer the prompted questions in this manner&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Do you want authentication tokens to be time-based &lt;span class="o"&gt;(&lt;/span&gt;y/n&lt;span class="o"&gt;)&lt;/span&gt; y

Do you want me to update your &lt;span class="s2"&gt;"/home/ec2-user/.google_authenticator"&lt;/span&gt; file &lt;span class="o"&gt;(&lt;/span&gt;y/n&lt;span class="o"&gt;)&lt;/span&gt; y

Do you want to disallow multiple uses of the same authentication token?
This restricts you to one login about every 30s,
but it increases your chances to notice or even prevent man-in-the-middle attacks &lt;span class="o"&gt;(&lt;/span&gt;y/n&lt;span class="o"&gt;)&lt;/span&gt; y

&lt;span class="c"&gt;# Select ‘n’ for the next question for 3 valid codes in a 1:30-minute window&lt;/span&gt;
By default,
tokens are good &lt;span class="k"&gt;for &lt;/span&gt;30 seconds and &lt;span class="k"&gt;in &lt;/span&gt;order to compensate &lt;span class="k"&gt;for &lt;/span&gt;possible time-skew between the client and the server,
we allow an extra token before and after the current time.
If you experience problems with poor &lt;span class="nb"&gt;time &lt;/span&gt;synchronization,
you can increase the window from its default size of 1:30min to about 4min.
Do you want to &lt;span class="k"&gt;do &lt;/span&gt;so &lt;span class="o"&gt;(&lt;/span&gt;y/n&lt;span class="o"&gt;)&lt;/span&gt; n

If the computer that you are logging into isn&lt;span class="s1"&gt;'t hardened against brute-force login attempts,
you can enable rate-limiting for the authentication module.
By default, this limits attackers to no more than 3 login attempts every 30s.
Do you want to enable rate-limiting (y/n) y
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Configure SSH to use the Google Pluggable Authentication Module&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/pam.d/sshd
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Append the following to the opened file in the editor&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;auth required pam_google_authenticator.so &lt;span class="o"&gt;[&lt;/span&gt;nullok]
auth required pam_permit.so
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Disable the password requirement by ensuring this line commented out&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#auth       substack     password-auth&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the SSH configuration to prompt for a second authentication&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/ssh/sshd_config
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the opened file, modify as follows&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#ChallengeResponseAuthentication no&lt;/span&gt;
ChallengeResponseAuthentication &lt;span class="nb"&gt;yes&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the same file, let SSH know that it should ask for SSH key and verification code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;AuthenticationMethods publickey,keyboard-interactive
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Save the file and close the editor.&lt;/p&gt;

&lt;p&gt;Restart the SSH to for the changes to take effect using either&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo&lt;/span&gt; /etc/init.d/sshd restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;OR&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service sshd restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Test Connection and Privileges
&lt;/h2&gt;

&lt;p&gt;Open a new terminal on your local machine.&lt;/p&gt;

&lt;p&gt;SSH into the instance with the new username and the associated private key&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; /&amp;lt;path_to_key_pair&amp;gt;/&amp;lt;key-pair-name&amp;gt;.pem &amp;lt;username&amp;gt;@&amp;lt;instance-public-dns-name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It should prompt you for a verification code. Open your Google Auth app to get the latest verification code for this instance, input it into the text box, and press the &lt;code&gt;Enter&lt;/code&gt; key.&lt;/p&gt;

&lt;p&gt;You should now be logged in to the instance as the new user who can perform sudo activities.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It sure was a journey coming this far, and I can but say, well done and congratulations!!! You have found something that works: for Ubuntu and most other Linux instances (some needing little tweaks).&lt;/p&gt;

&lt;p&gt;Thanks for reading through. But do not forget the references below: they could open you up to more possibilities beyond the scope of this tutorial.&lt;/p&gt;

&lt;p&gt;As much as I have practiced this over time, and tried out different things with different documentations, &lt;strong&gt;your contributions are very welcome in the comments&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Enjoy exploring, learning, and growing!!!&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Title&lt;/th&gt;
&lt;th&gt;Website&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Add New User to a Linux Instance&lt;/td&gt;
&lt;td&gt;&lt;a href="https://aws.amazon.com/premiumsupport/knowledge-center/new-user-accounts-linux-instance" rel="noopener noreferrer"&gt;https://aws.amazon.com/premiumsupport/knowledge-center/new-user-accounts-linux-instance&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Setup Multi-factor Authentication&lt;/td&gt;
&lt;td&gt;&lt;a href="https://aws.amazon.com/blogs/startups/securing-ssh-to-amazon-ec2-linux-hosts" rel="noopener noreferrer"&gt;https://aws.amazon.com/blogs/startups/securing-ssh-to-amazon-ec2-linux-hosts&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Create and Retrieve Key Pairs&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair" rel="noopener noreferrer"&gt;https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-key-pairs.html#having-ec2-create-your-key-pair&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Set or Change User Password&lt;/td&gt;
&lt;td&gt;&lt;a href="https://www.cyberciti.biz/faq/linux-set-change-password-how-to" rel="noopener noreferrer"&gt;https://www.cyberciti.biz/faq/linux-set-change-password-how-to&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Poster image&lt;/td&gt;
&lt;td&gt;&lt;a href="//freepik.com"&gt;Freepik&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

</description>
      <category>linux</category>
      <category>aws</category>
      <category>security</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Three (3) NodeJS Lessons I Have Learned In 2021</title>
      <dc:creator>Tobi Akanji</dc:creator>
      <pubDate>Sun, 01 Aug 2021 23:49:55 +0000</pubDate>
      <link>https://dev.to/tboyak/three-3-nodejs-lessons-i-have-learned-in-2021-4cjc</link>
      <guid>https://dev.to/tboyak/three-3-nodejs-lessons-i-have-learned-in-2021-4cjc</guid>
      <description>&lt;p&gt;Hello dear reader, happy second half of the year 🎉! It has been quite a year for many of us, and within the past months, we have experienced situations which we eventually learnt from and gained some wisdom.&lt;/p&gt;

&lt;p&gt;From the tonnes of things I learnt in the earlier six (6) months of the year 2021, I would love to share some of them with the entire Dev community, that is, you inclusive. So, let's go ✈️!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Pass In Required Variables Directly In An Object
&lt;/h2&gt;

&lt;p&gt;When it comes to chaining object properties to get a required value, many times we can never be too sure that even the very first variable, which is supposed to be an object is even defined.&lt;/p&gt;

&lt;p&gt;Take for example &lt;code&gt;v1.r1.r2&lt;/code&gt;.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;v1&lt;/code&gt; could be anything including null and undefined&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;r1&lt;/code&gt; might not be a defined variable of the object &lt;code&gt;v1&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notice that if either 1 or 2 is true, the chained variable would throw an exception for &lt;strong&gt;Property of undefined&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Now, say we pass this chained variables into an object as a key-value as thus, given either of the two (2) above listed conditions&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;{
  p1: v1.r1.r2
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The error will be thrown from within the scope of the object, leaving the error message and error stack behind.&lt;/p&gt;

&lt;p&gt;Hopefully we have a logging system across the modules (.js files) in our app, all the that the log will contain sounds something like&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;WARNING&lt;/strong&gt;: 😕 Hey, there was an error somewhere around, go check it, and maybe fix it!&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Except you have only three (3) statements that make up your project, then you most likely do not even have an idea of what you is happening, lest even what to do.&lt;/p&gt;

&lt;p&gt;To fix this, the right approach is to define the required variable outside the scope of the object. For example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;r2&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;val&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will help with logging and debugging, because the exception will be thrown and caught with the error message and stack.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;NOTE&lt;/strong&gt;: Chaining variables (property or method) on a variable that is undefined will throw an exception. Usually, NodeJS will log the error trace to the console (default) and/or wherever else specified. Therefore, these logs should contain information that is intuitive enough for easily approachable debugging.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Another way to avoid this exception is to check the defined state of each chained property, that is, property after property. For example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;p1&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;v1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;r1&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;r2&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  2. Never Pass Undefined Value To Knex &lt;code&gt;where&lt;/code&gt; Function
&lt;/h2&gt;

&lt;p&gt;Passing an undefined value to a column in a Knex &lt;code&gt;where&lt;/code&gt; function will throw an exception. This is quite similar to lesson 1, in that, if we apply some of the principles we learnt there, we could somewhat avert the feasibility of encountering this issue.&lt;/p&gt;

&lt;p&gt;You might expect that such query should take the default value specified in the schema; right? Which in many cases will be null. Unfortunately for that assumption, it is not so, and it is outrightly stated in the &lt;a href="https://knexjs.org/#Builder-wheres"&gt;documentation&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;h3&gt;
  
  
  Where Clauses
&lt;/h3&gt;

&lt;p&gt;...&lt;br&gt;
&lt;strong&gt;Important:&lt;/strong&gt; Supplying knex with an undefined value to any of the where functions will cause knex to throw an error during sql compilation...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Ouch! Sorry, you did not see that coming. But well, exceptions are errors you do not expect, nor do you specifically write code for, although we should always prepare our projects (codebases) for them.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Log Errors/Exceptions With Directives Included
&lt;/h2&gt;

&lt;p&gt;Not all error logs contain enough information for instant debugging, intuitive debugging, refactors or fixes. For this to occur, the error log should include enough information, which anyone can use to streamline the issue.&lt;/p&gt;

&lt;p&gt;For this reason, it will be more effective to construct the error log in such a way that anyone including you and anyone with authorized access to the logs, can determine what the exact issue is, and the next cause of action, in the case that any issue arises.&lt;/p&gt;

&lt;p&gt;Properties such as &lt;code&gt;source&lt;/code&gt; and &lt;code&gt;action&lt;/code&gt; in addition to a log will be very helpful in this regard. For example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json-doc"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Specified column does not exist"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="c1"&gt;// Optional&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"source"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"database"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"action"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Update User Password"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;the entire error stack&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="w"&gt;  &lt;/span&gt;&lt;span class="c1"&gt;// Could represent both error message and stack&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;For the meantime, these are few of the things I learnt while working on NodeJS projects this year, which I have been eager to share. Kindly share your thoughts on the three (3) lessons noted above.&lt;/p&gt;

&lt;p&gt;I would gladly appreciate that you share your own discoveries likewise in the comment section, as I look to feature them in future iterations of this article.&lt;/p&gt;

&lt;p&gt;Thanks for having me! 👍&lt;/p&gt;

</description>
      <category>node</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Build and Host Containerized Micro-Services</title>
      <dc:creator>Tobi Akanji</dc:creator>
      <pubDate>Wed, 27 Jan 2021 17:36:49 +0000</pubDate>
      <link>https://dev.to/tboyak/learning-to-build-and-host-containerized-micro-services-2nbc</link>
      <guid>https://dev.to/tboyak/learning-to-build-and-host-containerized-micro-services-2nbc</guid>
      <description>&lt;p&gt;In this tutorial, we would be deploying the main components of a containerised node app, as services on an AWS server.&lt;/p&gt;

&lt;p&gt;We will go through three (3) major steps here, including:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Containerization&lt;/li&gt;
&lt;li&gt;Docker-compose&lt;/li&gt;
&lt;li&gt;Proxying&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Connecting To Your Server Via SSH
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;ssh &lt;span class="nt"&gt;-i&lt;/span&gt; &amp;lt;path&amp;gt;/&amp;lt;to&amp;gt;/&amp;lt;.pem file&amp;gt; &amp;lt;user&amp;gt;@&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.&amp;lt;region&amp;gt;.compute.amazonaws.com
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Tools
&lt;/h2&gt;

&lt;p&gt;The minimum requirements to follow this tutorial are:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;CLI (e.g. Bash, Powershell…)&lt;/li&gt;
&lt;li&gt;Docker&lt;/li&gt;
&lt;li&gt;Docker Compose&lt;/li&gt;
&lt;li&gt;NGINX&lt;/li&gt;
&lt;li&gt;AWS-CLI&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For general CLI activities, I use Git Bash.&lt;/p&gt;

&lt;h3&gt;
  
  
  Docker
&lt;/h3&gt;

&lt;p&gt;Docker will be used to containerize our application, which in this case, is a microservice.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-and-use-docker-on-ubuntu-20-04"&gt;Install docker&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Docker Compose
&lt;/h3&gt;

&lt;p&gt;Docker Compose will be used to define how microservices for your application will relate.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-docker-compose-on-ubuntu-20-04"&gt;Install docker-compose&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a docker-compose.yml file a folder at your home (~) directory. E.g.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; ~ &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;mkdir &lt;/span&gt;my_new_app &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;touch &lt;/span&gt;docker-compose.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  NGINX
&lt;/h3&gt;

&lt;p&gt;NGINX will be used to define how the outside world will be able to relate to, and secure our application.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://www.digitalocean.com/community/tutorials/how-to-install-nginx-on-ubuntu-20-04"&gt;Install NGINX&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Containerizing Your Microservices
&lt;/h2&gt;

&lt;p&gt;Open your CLI tool and go into your app root directory.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; &amp;lt;path/to/app/root/directory&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You might want to confirm your current directory first, to guide on how to get to your root directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;dir&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In your app root directory, create a Dockerfile named Dockerfile with no file extension.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;Dockerfile
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ensure that whatever you are using to create the file does not add an extension to the Dockerfile. You can confirm this by running the following command on your current directory:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ls&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up The Dockerfile
&lt;/h3&gt;

&lt;p&gt;The minimum requirement for a Dockerfile is&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:14.15-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package*.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;CMD&lt;/span&gt;&lt;span class="s"&gt; ["npm", "start"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;OR&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;To allow for further process automation e.g. database migration, the actual commands to be executed when the container starts running, will be in a shell script (&lt;code&gt;.sh&lt;/code&gt; file) created in our app e.g. in the &lt;code&gt;deploy.sh&lt;/code&gt; file below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; /app
npm run migrate:up
npm run start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Dockerfile will be composed similar to:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; node:14.15-alpine&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; package.json package-lock.json ./&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x deploy.sh
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["./deploy.sh"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;&lt;p&gt;FROM: Defines our base image, that is, the foundation upon which we are building our application/image. This could be the programming language (e.g. python), runtime (e.g. node), OS (e.g. ubuntu) etc.&lt;br&gt;
The argument goes with the syntax &lt;code&gt;&amp;lt;name&amp;gt;:&amp;lt;tag&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;WORKDIR: Specifies a folder in our docker image where we will place our work files. Usually you should place your work files (codebase files) in a folder, conventionally &lt;code&gt;./app&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;COPY: Copy files and folders from our local machine to a folder in our docker image. The last argument indicates the docker image folder we are copying to.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;RUN: We use this to run shell commands.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;CMD: Here, we define the command we need to run the application.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ENTRYPOINT: This is a docker command that executes the &lt;code&gt;exec&lt;/code&gt; command when the container begins to run.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The line &lt;code&gt;RUN chmod +x deploy.sh&lt;/code&gt;, is used to switch the permission for the specified file &lt;code&gt;deploy.sh&lt;/code&gt;, which will be used to run the bash script. Without switching the permission, it is most likely that the current user will not be able to run scripts in the container, on the server.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: a &lt;code&gt;migrate:up&lt;/code&gt; script has been set in the package.json&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The first line, &lt;code&gt;#!/bin/bash&lt;/code&gt; defines the symbolic link, and is compulsory, so that the server knows what shell to symbolically link to.&lt;/p&gt;

&lt;h3&gt;
  
  
  Building The Image
&lt;/h3&gt;

&lt;p&gt;On your CLI, still at your app's root directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker build &lt;span class="nt"&gt;-t&lt;/span&gt; registry.tboyak.io/my_own_app:1 &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;docker tag my_own_app:1 registry.tboyak.io/my_own_app:1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...to simply build the image.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;OR&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker run &lt;span class="nt"&gt;-p&lt;/span&gt; &amp;lt;port&amp;gt;:&amp;lt;port&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;...to build the image and spin-off a container.&lt;/p&gt;

&lt;h3&gt;
  
  
  Forking The Image For A Remote Docker Repo
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker tag my_own_app:1 registry.tboyak.io/my_own_app:1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Pushing to Docker Hub
&lt;/h3&gt;

&lt;p&gt;For our app to be accessible online, we would need to push the image of our application to Docker Hub. To do this, we run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker push registry.tboyak.io/my_own_app:1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Setting Up The Docker Compose
&lt;/h3&gt;

&lt;p&gt;The minimum requirements for a &lt;code&gt;docker-compose.yml&lt;/code&gt; file with an app and a database is as setup below:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="c1"&gt;#docker-compose.yml&lt;/span&gt;

&lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;3"&lt;/span&gt;
&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;my_own_app&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;registry.tboyak.io/my_own_app:1&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;80:8080&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PORT=8080&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_CLIENT=pg&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_HOST=localhost&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_PORT=5432&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_DATABASE=my_own_app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_USERNAME=postgres&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DB_PASSWORD=password&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:13-alpine&lt;/span&gt;
    &lt;span class="na"&gt;container_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;5432:5432&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=my_own_app&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=postgres&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=password&lt;/span&gt;

&lt;span class="c1"&gt;## If using mysql&lt;/span&gt;
&lt;span class="c1"&gt;#  db:&lt;/span&gt;
&lt;span class="c1"&gt;#    image: mysql:latest&lt;/span&gt;
&lt;span class="c1"&gt;#    container_name: db&lt;/span&gt;
&lt;span class="c1"&gt;#    ports:&lt;/span&gt;
&lt;span class="c1"&gt;#      - 3306:3306&lt;/span&gt;
&lt;span class="c1"&gt;#    environment:&lt;/span&gt;
&lt;span class="c1"&gt;#       MYSQL_ROOT_PASSWORD: root_password&lt;/span&gt;
&lt;span class="c1"&gt;#       MYSQL_DATABASE: my_own_app&lt;/span&gt;
&lt;span class="c1"&gt;#       MYSQL_USER: wordpress&lt;/span&gt;
&lt;span class="c1"&gt;#       MYSQL_PASSWORD: password&lt;/span&gt;

&lt;span class="c1"&gt;# For MongoDB, there are further requirements that cannot be covered here&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that we're dealing with each main component as a service e.g. our app, and the database.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;version: The version of docker-compose you intend to use. You can always check for the &lt;a href="https://docs.docker.com/compose/"&gt;latest&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;services: The dictionary of microservices you need for your fully running application. In this example, we only need our app, and a database.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;build: This indicates that we are building an image for our own application from our Dockerfile. It takes the value of the root directory of the app we intend to build, which is where the Dockerfile should be.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;image: We indicate the name and tag of the image we intend to use for our app in this format &lt;code&gt;[registry.username/]&amp;lt;name&amp;gt;:&amp;lt;tag&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;ports: The list of ports mappings to the service. This indicates the port we intend to expose to the outside world in order to have access to the internal running port of the services.&lt;br&gt;
The syntax reads &lt;code&gt;&amp;lt;external port&amp;gt;:&amp;lt;internal port&amp;gt;&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;environment: The list of environment variables for the associated service.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;container_name: The default name we intend to give the container we spin-off from the built image.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;depends_on: The list of microservices that the particular microservice depends on.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the advent where your server size is to small to handle your RDBMS, use an AWS RDS (Relational Database Service) instead.&lt;/p&gt;

&lt;h3&gt;
  
  
  Connecting To RDS
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;First of all, you will need your server to be authenticated with the RDS
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws rds generate-db-auth-token &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--hostname&lt;/span&gt; &amp;lt;name&amp;gt;.&amp;lt;&lt;span class="nb"&gt;id&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;.&amp;lt;region&amp;gt;.rds.amazonaws.com &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--port&lt;/span&gt; 3306 &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt; &lt;span class="se"&gt;\&lt;/span&gt;
   &lt;span class="nt"&gt;--username&lt;/span&gt; &amp;lt;username&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Connect to a DB instance on the RDS. The access parameters to the DB will be the env for DB connection on your own app. That is:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;DB_Host=...rds.amazonaws.com&lt;/li&gt;
&lt;li&gt;DB_NAME=&lt;/li&gt;
&lt;li&gt;DB_PORT=&lt;/li&gt;
&lt;li&gt;DB_USERNAME=&lt;/li&gt;
&lt;li&gt;DB_PASSWORD=&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Running The Containers
&lt;/h3&gt;

&lt;p&gt;On your CLI, still at your app's root directory, run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;docker-compose up
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In case your image is hosted on a registry e.g. AWS ECR, you will need to have access to it on your server before you can successfully run the docker-compose. To do that:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html"&gt;Install AWS-CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.aws.amazon.com/AmazonECR/latest/userguide/getting-started-cli.html"&gt;Login to the ECR (Elastic Container Registry)&lt;/a&gt;. Simply run:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;aws ecr get-login-password &lt;span class="nt"&gt;--region&lt;/span&gt; &amp;lt;region&amp;gt; | docker login &lt;span class="nt"&gt;--username&lt;/span&gt; AWS &lt;span class="nt"&gt;--password-stdin&lt;/span&gt; &amp;lt;registry&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You will be requested to provide certain credentials, which you should find on your AWS dashboard/profile:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;AWS_ACCESS_KEY_ID&lt;/li&gt;
&lt;li&gt;AWS_SECRET_ACCESS_KEY&lt;/li&gt;
&lt;li&gt;AWS_DEFAULT_REGION&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Proxy Networking
&lt;/h2&gt;

&lt;p&gt;Create and/or open the file for your site, contained in the sites available to your NGINX&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/conf.d/sites-available/&amp;lt;your_domain_name&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in the file, edit its content to something similar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// /etc/nginx/conf.d/sites-available/&amp;lt;your_domain_name&amp;gt;

server {
        listen 80;
        listen [::]:80;

        server_name &amp;lt;your_domain&amp;gt; [www.&amp;lt;your_domain&amp;gt;];

        location / {
                proxy_pass  http://&amp;lt;your_service&amp;gt;;
                try_files $uri $uri/ =404;
        }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After successfully editing and saving the file, create a &lt;code&gt;sites-enabled&lt;/code&gt; folder if it doesn't exist. This folder will contain sites enabled for access via the NGINX.&lt;/p&gt;

&lt;p&gt;Afterwards, symbolically link the sites-available to the sites-enabled. This will cause automatic updates from the sites available to the sites enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;cd&lt;/span&gt; /etc/nginx/conf.d
&lt;span class="nb"&gt;mkdir &lt;/span&gt;sites-enabled &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;sites-enabled
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;ln&lt;/span&gt; &lt;span class="nt"&gt;-s&lt;/span&gt; ../sites-available/plex.conf &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the NGINX sites lookup reference to the sites-enabled.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/nginx/nginx.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Change the line &lt;code&gt;include /etc/nginx/conf.d/*.conf;&lt;/code&gt; to &lt;code&gt;include /etc/nginx/conf.d/sites-enabled/*.conf;&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;When all is successfully setup, restart the NGINX.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;service nginx restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, you should be able to access the service you just created on your browser, or on your terminal with an http agent e.g. curl, or Postman.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Hopefully this tutorial is helpful. Please leave your comments below. Feedback and more insights are very much welcome.&lt;/p&gt;

</description>
      <category>docker</category>
      <category>nginx</category>
      <category>microservices</category>
      <category>node</category>
    </item>
  </channel>
</rss>
