My stint with Runc vulnerability

Today I was given a task to set up a new QA environment. I said no issue should be done quickly as we use Docker, so I just need to provision VM run the already available QA ready docker image on this newly provisioned VM. So I started and guess what Today was not my day. I got below error while running by App image.

docker: Error response from daemon: OCI runtime create failed: container_linux.go:344: starting container process caused “process_linux.go:293: copying bootstrap data to pipe caused \”write init-p: broken pipe\””: unknown.

I figured out my Valentine’s Day gone for a toss. As usual I took help of Google God to figure out what this issue is all about, after few minutes I found out a blog pretty close to issue that I was facing

https://medium.com/@dirk.avery/docker-error-response-from-daemon-1d46235ff61d

Bang on I got the issue identified. There is a new runc vulnerability identified few days back.

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5736

The fix of this vulnerability was released by Docker on February 11, but the catch was that this fix makes docker incompatible with 3.13 Kernel version.

While setting up QA environment I installed latest stable version of docker 18.09.2 and since the kernel version was 3.10.0-327.10.1.el7.x86_64 thus docker was not able to function properly.

So as suggested in the blog I upgraded the Kernel version to 4.x.

rpm –import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
yum repolist
yum –enablerepo=elrepo-kernel install kernel-ml
yum repolist all
awk -F\’ ‘$1==”menuentry ” {print i++ ” : ” $2}’ /etc/grub2.cfg
grub2-set-default 0
grub2-mkconfig -o /boot/grub2/grub.cfg
reboot

And here we go post that everything is working like a charm.

So word of caution to every even
We have a major vulnerability in docker CVE-2019-5736, for more details go through the link

https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-5736

As a fix, upgrade your docker to 18.09.2, as well make sure that you have Kernel 4+ as suggested in the blog.

https://docs.docker.com/engine/release-notes/

Now I can go for my Valentine Party 👫

Using Ansible Dynamic Inventory with Azure can save the day for you.

As a DevOps Engineer, I always love to make things simple and convenient by automating them. Automation can be done on many fronts like infrastructure, software, build and release etc.

Ansible is primarily a software configuration management tool which can also be used as an infrastructure provisioning tool.
One of the thing that I love about Ansible is its integration with different cloud providers. This integration makes things really loosely coupled, For ex:- we don’t require to manage whole information of cloud in Ansible (Like we don’t need instance metadata information for provisioning it).

Ansible Inventory

Ansible uses a term called inventory to refer to the set of systems or machines that our Ansible playbook or command work against. There are two ways to manage inventory:-
  • Static Inventory
  • Dynamic Inventory
By default, the static inventory is defined in /etc/ansible/hosts in which we provide information about the target system. In most of the cloud platform when the server gets reboot then it reassigns a new public address and again we have to update that in our static inventory, so this can’t be the lasting option.
Luckily Ansible supports the concept of dynamic inventory in which we have some python scripts and a .ini file through which we can provision machines dynamically without knowing its public or private address. Ansible Dynamic Inventory is fed by using external python scripts and .ini files provided by Ansible for cloud infrastructure platforms like Amazon, Azure, DigitalOcean, Rackspace.
In this blog, we will talk about how to configure dynamic inventory on the Azure Cloud Platform.

Ansible Dynamic Inventory on Azure

The first thing that always required to run anything is software and its dependencies. So let’s install the software and its dependencies first. First, we need the python modules of azure that we can install via pip.

$ pip install 'ansible[azure]'
After this, we need to download azure_rm.py

$ wget https://raw.githubusercontent.com/ansible/ansible/devel/contrib/inventory/azure_rm.py

Change the permission of file using chmod command.

$ chmod +x azure_rm.py

Then we have to log in to Azure account using azure-cli

$ az login
To sign in, use a web browser to open the page https://aka.ms/devicelogin and enter the code XXXXXXXXX to authenticate.

The az login command output will provide you a unique code which you have to enter in the webpage i.e.
https://aka.ms/devicelogin

As part of the best practice, we should always create an Active Directory for different services or apps to restrict privileges. Once you logged in Azure account you can create an Active Directory app for Ansible

$ az ad app create --password ThisIsTheAppPassword --display-name opstree-ansible --homepage ansible.opstree.com --identifier-uris ansible.opstree.com

Don’t forget to change your password ;). Note down the appID from the output of the above command.

Once the app is created, create a service principal to associate it with.

$ az ad sp create --id appID

Replace the appID with actual app id and copy the objectID from the output of the above command.
Now we just need the subscription id and tenant id, which we can get by a simple command

$ az account show

Note down the id and tenantID from the output of the above command.

Let’s assign a contributor role to service principal which is created above.

$ az role assignment create --assignee objectID --role contributor

Replace the objectID with the actual object id output.

All the azure side setup is done. Now we have to make some changes to your system.

Let’s start with creating an azure home directory

$ mkdir ~/.azure 

In that directory, we have to create a credentials file

$ vim ~/.azure/credentials

[default]
subscription_id=id
client_id=appID
secret=ThisIsTheAppPassword
tenant=tenantID

Please replace the id, appID, password and tenantID with the above-noted things.

All set !!!! Now we can test it by below command

$ python ./azure_rm.py --list | jq

and the output should be like this:-

{
"azure": [
"ansibleMaster"
],
"westeurope": [
"ansibleMaster"
],
"ansibleMasterNSG": [
"ansibleMaster"
],
"ansiblelab": [
"ansibleMaster"
],
"_meta": {
"hostvars": {
"ansibleMaster": {
"powerstate": "running",
"resource_group": "ansiblelab",
"tags": {},
"image": {
"sku": "7.3",
"publisher": "OpSTree",
"version": "latest",
"offer": "CentOS"
},
"public_ip_alloc_method": "Dynamic",
"os_disk": {
"operating_system_type": "Linux",
"name": "osdisk_vD2UtEJhpV"
},
"provisioning_state": "Succeeded",
"public_ip": "52.174.19.210",
"public_ip_name": "masterPip",
"private_ip": "192.168.1.4",
"computer_name": "ansibleMaster",
...
}
}
}
}

Now you are ready to use Ansible in Azure with dynamic inventory. Good Luck 🙂

Working With AWS KMS

Thousands of organizations use Amazon Web Services (AWS) to host their applications and manage their data in the cloud. The advantage of geographic availability, scalability and reliability make AWS a great choice.
Due to recent and more frequently-occurring breaches in security in a number of environments, It is necessary for us to take data protection strategy seriously.

We all can agree that Information security is always of paramount importance, whether data is stored on-premises or in the cloud.
In this article we will go through AWS KMS and how to use KMS in our existing AWS account.

AWS KEY MANAGEMENT SERVICE
AWS Key Management Service (AWS KMS) is a managed service that makes it easy for us to create, control, rotate, and use encryption keys.

It also centralizes key management, with one dashboard that offers creation, rotation, and lifecycle management functions.

AWS KMS Concept

1. Customer Master Key

Customer Master Keys (CMKs) or Master Encryption Key(MEK) are used to generate, encrypt, and decrypt the data keys(DK) that you use outside of AWS KMS to encrypt your data. This strategy is known as envelope encryption. CMKs are created in AWS KMS and never leave AWS KMS unencrypted. They can only be accessed through AWS KMS.
The master keys are protected by FIPS 140-2 validated cryptographic modules.

2. Data Keys

Data keys are encryption keys that you can use to encrypt data, including large amounts of data and other data encryption keys.
You can use AWS KMS customer master keys (CMKs) to generate, encrypt, and decrypt data keys.

3. Encrypting Data

1. First of all a Customer Master Key is created in KMS console.
2. Then to create a data key, AWS KMS uses the CMK to generate a data key. The operation returns a plaintext copy of the data key and a copy of the data key encrypted under the CMK.

3. Now we have both the Master Key and Data Key, we can use the data key to encrypt the data.

4. After using the plaintext data key to encrypt data, we remove it from memory and can store the encrypted data key with the encrypted data so it is available to decrypt the data.

4. Decrypting Data

1. To decrypt your data, pass the encrypted data key to the Decrypt operation.

2. AWS KMS uses CMK to decrypt the data key and then it returns the plaintext data key.
3. Use the plaintext data key to decrypt your data and then remove the plaintext data key from memory as soon as possible.

5. Envelope Encryption 

Envelope encryption is the practice of encrypting plaintext data with a data key, and then encrypting the data key under another key. AWS KMS uses MEK to encrypt the Data Key(DEK).

Hands On Lab: What we are going to do?

We will be Creating a Customer Master Key in AWS-KMS console and will try to upload file on S3 Using KMS Master-Key Encryption. Then try to access the encrypted file.

Step-1: Creating Master Key in AWS-KMS
1. First of all login to AWS Management console and then go to IAM Dashboard and select Encryption Keys, this will open AWS-KMS console.
2. In AWS KMS console select the Region and click on Create Key.

3. Create an Alias for KMS Master Key and add a meaningful tag.

 4. Define Key Administrative and Usage Permissions.

5. Review the Policy and click on create.

6. You can see in the KMS console a new Master Key is created.

Step-2: Create a Bucket in S3

1. Go to S3 console in AWS and click on create a Bucket.
2. Specify the Bucket name and Region and click on create.

3. Once the bucket is created try to upload some data in next step.

Step-3: Upload data to Bucket created in S3

1. Click on Upload to upload file in S3 bucket created in previous step.

2. Select the file and in the next step, define who can access the bucket and access permissions.

3. In the next step choose the storage class and Encryption method.

 4. In Encryption method select Encryption using AWS KMS master-key, and select the Master-Key generated in the earlier step for data encryption.

5. Review and click on Upload. Once uploaded verify the object properties.

6. Now try to access the uploaded data by clicking on download. You will see that you are able to download the file without any issue.

Step-4:Disable the Master key
1. Now let’s disable the Master Key from KMS console and check again.

2. Now try again to access the uploaded file in S3 after disabling the MK.

Step-4:Enable the Master key

1. To enable the Master Key again go to KMS console and enable the MK.

 

Step-5: Try to access the S3 object with different IAM user.

1. Try to access the S3 bucket uploaded file with a different IAM user who does not have Usage access to KMS Master Key.


What’s Happening Behind the Scene

1. Encryption Using KMS Customer Master Key

2. Decryption Using KMS Customer Master Key


Conclusion.

KMS is a fully managed service because it automatically handles all of the availability, scalability, physical security, and hardware maintenance for the underlying Key Management Infrastructure (KMI).
With no up-front cost and usage-based pricing that starts at $1 per Customer.
Master Key (CMK) per month, KMS makes it easy for us to encrypt data stored in S3, EBS, RDS, Redshift, and any other AWS service that’s integrated with KMS.

Log Parsing of Windows Servers on Instance Termination

 
As we all know that how critical are Logs as a part of any system, they give you deep insights about your application, what your system is doing and what caused the error. Depending on how logging is configured logs may contain transaction history, timestamps and amounts debited/credited into client’s account and a lot more.
 

On an enterprise level application, your system goes to multiple hosts, managing the logs across multiple hosts can be complicated. Debugging the error in the application across hundreds of log files on hundreds of servers can be very time consuming and complicated and not the right approach so it is always better to move the logs to a centralized location.

Lately in my company I faced a situation which I assume is a very commonly faced scenario in Amazon’s Cloud where we might have to retain application logs from multiple instances behind an Auto Scaling group.  Let’s assume an example for better understanding. 
Suppose your application is configured to be logging into C:\Source\Application\web\logs Directory. The Application running has variant incoming traffic, sometimes it receives requests which can be handled by 2 servers, other times it may require 20 servers to handle the traffic.
When there is a hike in traffic, Amazon Ec2’s smart AutoScaling Group uses the configuration and scales from 2 server to many (According to ASG Policy) and during this phase, the application running in the newly launched Ec2’s also log into C:\Source\Application\web\logs …. but when there’s a drop in traffic, the ASG triggers a scale down policy, resulting to termination of instances, which also results in deletion of all the log files inside the instances launched via ASG during high traffic time.

Faced a similar situation ?  No worries, now in order to retain logs I figured out an absolute solution.

Here, in this blog, the motive is to sync the logs from dying instances at the time of their termination. This will be done using AWS Services, the goal is to trigger a Powershell Script in the instance using SSM which sync logs to S3 Bucket with sufficient information about the dying instances. For this we will require 2 things:
1) Configuring SSM agent to be able to talk to Ec2 Instances
2) Ec2 Instances being able to write to S3 Buckets

For the tutorial we will be using Microsoft Windows Server 2012 R2 Base with the AMI ID: ami-0f7af6e605e2d2db5

A Blueprint of the scenario to be understood below:

1) Configuring SSM agent to be able to talk to Ec2 Instances

SSM Agent is installed by default on Windows Server 2016 instances and instances created from Windows Server 2003-2012 R2 AMIs published in November 2016 or later. Windows AMIs published before November 2016 use the EC2Config service to process requests and configure instances.

If your instance is a Windows Server 2003-2012 R2 instance created before November 2016, then EC2Config must be upgraded on the existing instances to use the latest version of EC2Config. By using the latest EC2Config installer, you install SSM Agent side-by-side with EC2Config. This side-by-side version of SSM Agent is compatible with your instances created from earlier Windows AMIs and enables you to use SSM features published after November 2016.
This simple script can be used to update Ec2Config and then layer it with the latest version of SSM agent. This will always install AwsCli which is used to push logged archives to S3

#ScriptBlock

if(!(Test-Path -Path C:\Scripts )){
mkdir C:\Tmp
}
cd C:/Tmp
wget https://s3.ap-south-1.amazonaws.com/asg-termination-logs/Ec2Install.exe -OutFile Ec2Config.exe
wget https://s3.amazonaws.com/ec2-downloads-windows/SSMAgent/latest/windows_amd64/AmazonSSMAgentSetup.exe -OutFile ssmagent.exe
wget https://s3.amazonaws.com/aws-cli/AWSCLI64PY3.msi -OutFile awscli.msi
wget https://s3.amazonaws.com/aws-cli/AWSCLISetup.exe -OutFile awscli.exe
Invoke-Command -ScriptBlock {C:\Tmp\Ec2Config.exe /Ec /S /v/qn }
sleep 20
Invoke-Command -ScriptBlock {C:\Tmp\awscli.exe /Ec /S /v/qn }
sleep 20
Invoke-Command -ScriptBlock {C:\Tmp\ssmagent.exe /Ec /S /v/qn }
sleep 10
Restart-Service AmazonSSMAgent
Remove-Item C:\Tmp

An IAM Role is Required for SSM to Ec2 Instance Conversation:

IAM instance role: Verify that the instance is configured with an AWS Identity and Access Management (IAM) role that enables the instance to communicate with the Systems Manager API.
Add instance profile permissions for Systems Manager managed instances to an existing role
  • Open the IAM console at https://console.aws.amazon.com/iam/.
  • In the navigation pane, choose Roles, and then choose the existing role you want to associate with an instance profile for Systems Manager operations.
  • On the Permissions tab, choose Attach policy.
  • On the Attach policy page, select the check box next to AmazonEC2RoleforSSM, and then choose Attach policy.
Now, Navigate to Roles > and select your role. 
That should look like:

2) Ec2 Instances being able to write to S3 Buckets
An IAM Role is Required for Ec2 to be able to write to S3:
IAM instance role: Verify that the instance is configured with an AWS Identity and Access Management (IAM) role that enables the instance to communicate with the S3 API.
Add instance profile permissions for Systems Manager managed instances to an existing role
  • Open the IAM console at https://console.aws.amazon.com/iam/.
  • In the navigation pane, choose Roles, and then choose the existing role you want to associate with an instance profile for Systems Manager operations.
  • On the Permissions tab, choose Attach policy.
  • On the Attach policy page, select the check box next to AmazonS3FullAccess, and then choose Attach policy. 

That should look like:


This Powershell script saved in C:/Scripts/termination.ps1 will pick up log files from: $SourcePathWeb:
and will output logs into:

$DestFileWeb
with a IP and date-stamp to recognize and identify the instances and where the logs originate from later.
Make sure that the s3 bucket name and –region and source of log files is changed according to the preferences. 


#ScriptBlock

$Date=Get-Date -Format yyyy-MM-dd
$InstanceName=”TerminationEc2″
$LocalIP=curl http://169.254.169.254/latest/meta-data/local-ipv4 -UseBasicParsing

if((Test-Path -Path C:\Users\Administrator\workdir\$InstanceName-$LocalIP-$Date/$Date )){
Remove-Item “C:\Users\Administrator\workdir\$InstanceName-$LocalIP-$Date/$Date” -Force -Recurse
}

New-Item -path “C:\Users\Administrator\workdir\$InstanceName-$LocalIP-$Date/$Date” -type directory
$SourcePathWeb=”C:\Source\Application\web\logs”
$DestFileWeb=”C:\Users\Administrator\workdir\$InstanceName-$LocalIP-$Date/$Date/logs.zip”

Add-Type -assembly “system.io.compression.filesystem”
[io.compression.zipfile]::CreateFromDirectory($SourcePathWeb, $DestFileWeb)

C:\’Program Files’\Amazon\AWSCLI\bin\aws.cmd s3 cp C:\Users\Administrator\workdir s3://terminationec2 –recursive –exclude “*.ok” –include “*” –region us-east-1

If the above settings are done fine then manually the script should produce a success suggesting output:

p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }


Check your S3, Bucket for seeing if it has synced logs to there. Now, because the focus of this blog trigger a Powershell Script in the instance using SSM which syncs the logs to S3 Bucket so we will try running the script through SSM > Run Command.


Select and run of the instances having the above script and configuration. The output should be pleasing.




The AMI used by the ASG should have the above configuration (Can be archived via created a ami from ec2 having above config and then adding it into Launch Configuration of the ASG). The ASG we have here for the tutorial is named after my last name : “group_kaien”.

Now, the last and the Most important step is configuration theCloudwatch > Event > Rules.


Navigating to Cloudwatch>Event>Rules: Create Rule.

This would return the following JSON config:

{
“source”: [
“aws.autoscaling”

],
“detail-type”: [
“EC2 Instance Terminate Successful”,
“EC2 Instance-terminate Lifecycle Action”
],
“detail”: {
“AutoScalingGroupName”: [
“group_kaien”
]
}
}
On the right side of Targets:
Select
SSM Run Command:
  • Document: AwsRunPowerShellScript
  • Target key: “Instanceids or tag:
  • Target Values:
 Configure parameter
  • Commands: .\termination.ps1
  • WorkingDirectory: C:\Scripts.ps1
  • ExecutionTimeout: 3600 is default
Making sure that on termination event happening, the powershell script is run and it syncs logs to S3. This is what our configuration looks like:




 

For more on setting up Cloudwatch Events refer :
https://docs.aws.amazon.com/AmazonCloudWatch/latest/events/CWE_GettingStarted.html


Wait for the AutoScaling Policies to run such that new instances are created and terminated, with above configuration. The terminating instances will sync their logs S3 before they are fully terminated. Here’s the output on S3 for me after a scale down activity was done.


 

Conclusion

Now with this above, we have learned how to export logs to S3 automatically from a dying instance, with the correct date/time stamp as mentioned in the termination.ps1 script.
Hence, fulfilling the scope of the blog.
Stay tuned for more

p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }

p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }
p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }

p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }
p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }
p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }

p { margin-bottom: 0.25cm; line-height: 120%; }a:link { }

Git-Submodule

Rocket Science has always fascinated me, but one thing which totally blows my mind is the concept of modules aka. modular rockets. The literal definition of modules statesA modular rocket is a type of multistage rocket which features components that can be interchanged for specific mission requirements.” In simple terms, you can say that the Super Rocket depends upon those Submodules to get the things done.
Similarly is the case in the Software world, where super projects have multiple dependencies on other objects. And if we talk about managing projects Git can’t be ignored, Moreover Git has a concept of Submodules which is slightly inspired by the amazing rocket science of modules.

Hour of Need

Being a DevOps Specialist we need to do provisioning of the Infrastructure of our clients which is sometimes common for most of the clients. We decided to Automate it, which a DevOps is habitual of. Hence, Opstree Solutions initiated an Internal project named OSM. In which we create Ansible Roles of different opensource software with the contribution of each member of our organization. So that those roles can be used in the provisioning of the client’s infrastructure.
This makes the client projects dependent on our OSM. Which creates a problem statement to manage all dependencies which might get updated over the period. And to do that there is a lot of copy paste, deleting the repository and cloning them again to get the updated version, which is itself a hair-pulling task and obviously not the best practice.
Here comes the git-submodule as a modular rocket to take our Super Rocket to its destination.

Let’s Liftoff with Git-Submodules

A submodule is a repository embedded inside another repository. The submodule has its own history; the repository it is embedded in is called a superproject.

In simple terms, a submodule is a git repository inside a Superproject’s git repository, which has its own .git folder which contains all the information that is necessary for your project in version control and all the information about commits, remote repository address etc. It is like an attached repository inside your main repository, which can be used to reuse a code inside it as a “module“.
Let’s get a practical use case of submodules.
We have a client let’s call it “Armstrong” who needs few of our ansible roles of OSM for their provisioning of Infrastructure. Let’s have a look at their git repository below.

$    cd provisioner
$ ls -a
. .. ansible .git inventory jenkins playbooks README.md roles
$ cd roles
$ ls -a
apache java nginx redis tomcat
We can see in this Armstrong’s provisioner repository(a git repository) depends upon five roles which are available in OSM’s repository to help Armstrong to provision their infrastructure. So we’ll add submodules osm_java and others.

$    cd java
$ git submodule add -b armstrong git@gitlab.com:oosm/osm_java.git osm_java
Cloning into './provisioner/roles/java/osm_java'...
remote: Enumerating objects: 23, done.
remote: Counting objects: 100% (23/23), done.
remote: Compressing objects: 100% (17/17), done.
remote: Total 23 (delta 3), reused 0 (delta 0)
Receiving objects: 100% (23/23), done.
Resolving deltas: 100% (3/3), done.

With the above command, we are adding a submodule named osm_java whose URL is git@gitlab.com:oosm/osm_java.git and branch is armstrong. The name of the branch is coined armstrong because to keep the configuration of each of our client’s requirement isolated, we created individual branches of OSM’s repositories on the basis of client name.
Now if take a look at our superproject provisioner we can see a file named .gitmodules which has the information regarding the submodules.

$    cd provisioner
$ ls -a
. .. ansible .git .gitmodules inventory jenkins playbooks README.md roles
$ cat .gitmodules
[submodule "roles/java/osm_java"]
path = roles/java/osm_java
url = git@gitlab.com:oosm/osm_java.git
branch = armstrong

Here you can clearly see that a submodule osm_java has been attached to the superproject provisioner.

What if there was no submodule?

If that was a case, then we need to clone the repository from osm and paste it to the provisioner then add & commit it to the provisioner phew….. that would also have worked.
But what if there is some update has been made in the osm_java which have to be used in provisioner, we can not easily sync with the OSM. We would need to delete osm_java, again clone, copy, and paste in the provisioner which sounds clumsy and not a best way to automate the process.
Being a osm_java as a submodule we can easily update that this dependency without messing up the things.

$    git submodule status
-d3bf24ff3335d8095e1f6a82b0a0a78a5baa5fda roles/java/osm_java
$ git submodule update --remote
remote: Enumerating objects: 3, done.
remote: Counting objects: 100% (3/3), done.
remote: Total 2 (delta 0), reused 2 (delta 0), pack-reused 0
Unpacking objects: 100% (2/2), done.
From
git@gitlab.com:oosm/osm_java.git 0564d78..04ca88b armstrong -> origin/armstrong
Submodule path 'roles/java/osm_java': checked out '04ca88b1561237854f3eb361260c07824c453086'

By using the above update command we have successfully updated the submodule which actually pulled the changes from OSM’s origin armstrong branch.

What have we learned? 

In this blog post, we learned to make use of git-submodules to keep our dependent repositories updated with our super project, and not getting our hands dirty with gullible copy and paste.
Kick-off those practices which might ruin the fun, sit back and enjoy the automation.

Referred links:
Image: google.com
Documentation: https://git-scm.com/docs/gitsubmodules


How DHCP and DNS are managed in AWS VPC – Part 1

In our day-to-day life, we take a lot of things for granted: our body, food, friends, family, internet, domain name (URL) of facebook, IP address of instagram, etc. In our ignorance, we forget to consider how much their absence would affect us, the hardship and inconvenience we’d face, if, one day, any of these just vanishes. Let’s leave the leisure of friends, food, etc to be discussed sometime later. For now, we’ll limit our thoughts to how DHCP and DNS are managed in AWS. These are responsible for domain names and IP addresses. Combined, they are the backbone to connections among hosts and servers over a network.

Both of these services work a little differently in AWS than in a physical infrastructure. In a physical set-up, DHCP can either be managed by a router or by dedicated DHCP servers depending on the size of our infrastructure whereas in AWS, through option sets. Similarly, DNS can either be managed by Internet Service Provider (ISP) or by dedicated DNS servers, whereas AWS, apart from having reserved DNS servers (AmazonProvidedDNS), has a few other tricks up its sleeve as well. To understand this better, we’ll go through both of them one by one.

Dynamic Host Configuration Protocol (DHCP)

Helps our hosts/systems obtain a unique identity over a network. We call this identity IP address but it’s not just IP address, DHCP provides much more information than that like subnet mask, default gateway, DNS server addresses. All of these together form network configuration information. DHCP is client-server based protocol which uses 4-way handshake to establish connection. Below is a visual representation:
Fig. 1: 4 – way handshake connection
In case of AWS VPC, this configuration information is contained in DHCP option sets. Option sets contain only those DHCP options that are customizable. They do not contain IP address since IP addresses are provided by default when an instance is launched. There are five options currently supported in DHCP option sets. When an instance is launched in a VPC and requests Dynamic Configuration, AWS DHCP server provides that configuration with the below details, i.e., entries of option set:
  1. domain-name-server: This implies which DNS server the host will query. We can specify IP addresses of up to four custom domain name servers (DNS) separated by comma or we can leave it to AmazonProvidedDNS which is there by default.
  2. domain-name: This specifies our domain name. Example: google.com, opstree.com, etc. When using AmazonProvidedDNS, depending upon our region, domain name could vary for public and private instances. When using custom DNS, we provide our own domain name and DNS servers which we will discuss in detail later.
  3. ntp-servers: NTP servers synchornize clock across servers in our network. They are precise to upto tens of millisecond even on the internet and even more accurate on a local network. We can specify IP addresses of up to four NTP servers.
  4. netbios-name-servers: They are similar to DNS servers, in that, they both provide identification to hosts in a network. DNS provides host identification over the internet but NetBIOS identifies hosts that use a local network. They do not need internet and are always available to the hosts connected directly to them. No need to register name in hosts. They allow applications on different connected hosts to communicate with each other. We can specify IP addresses of up to four NetBIOS name servers.
  5. netbios-node-type: There are four NetBIOS node types using which a windows machine resolves NetBIOS names: 1, 2, 4, and 8. AWS recommends specifying 2 (point-to-point node) mostly because 1 (broadcast)  and 4 (multicast) nodes are not yet supported.

Fig. 2: DHCP option set

These option sets are immutable. So, if we need to make changes, add or remove an option, we must create new option sets. A VPC can have several option sets but only one can be associated to it at a time. If we replace an option of VPC, we do not need to restart our running instances. Changes will reflect automatically depending on lease time of the DHCP configuration to an instance. We could always connect to instance and explicitly renew the lease, if need be. When a VPC is deleted, DHCP options set associated with it is also deleted.

Domain name service (DNS)

Maps names over the internet to their corresponding IP addresses. DNS hostname of a host is unique and indisputable. It consists of a host name and a domain name. A DNS server resolves DNS hostnames to their corresponding IP addresses. They are how we can reach our intended website by just typing its name in the browser while not needing to remember its IP address at all. The image below gives a far better idea of how it works, it really is amazing:
Fig. 3: Working of DNS
AWS provides us a default DNS which is called AmazonProvidedDNS. We can also configure our VPC to use custom DNS servers through DHCP option sets as we saw above. There are two attributes we need to keep in mind if we need DNS support in our VPC: enableDNSHostname and enableDnsSupport
enableDnsHostname, if set true ensures that the instances in the VPC get public DNS hostnames but only if enableDnsSupport is also set to true. This is because true value of enableDnsSupport ensures that the Amazon-provided DNS server in VPC resolves public DNS hostnames to IP addresses
This means, AWS DNS service, for instances in a VPC, works if both are true and does not work if either of them is false. Having said that, we should keep in mind custom DNS service will work even when both are false if we associate custom DHCP option set with the VPC.  Let’s dive deeper to understand this.

AmazonProvidedDNS (both enableDnsHostname and enableDnsSupport are true)

As mentioned earlier, when we create a VPC, it is provided a DHCP option set which has a default DNS called AmazonProvidedDNS and a default domain name as per naming convention. Any instance launched in this VPC will get a private DNS hostname. Public instances will get public DNS hostname if both the attributes mentioned earlier (enableDnsHostname and enableDnsSupport) are set to true. Private DNS hostname resolves to private IP address of the instance and public DNS hostname resolves to public IP address. 
Both the attributes, enableDnsHostname and enableDnsSupport are true by default in default VPC of a region or VPC created through wizard. In VPC’s created any other way, CLI, API, or manually through console, enableDnsSupport is true by default. Other needs to be set true.
 
There is a reserved IP address provided to Amazon provided DNS in our VPC CIDR block. It is the base of CIDR block plus two, i.e. if CIDR block is 10.0.0.0/16, DNS IP address is 10.0.0.2. Apart from this, queries to DNS can also hit 169.254.169.253 server which is also an Amazon provided DNS server.
Fig. 4: This is a default DHCP option set. In options field, we can see that domain-name is provided by default and name-sever is AmazonProvidedDNS

Custom DNS servers (either of enableDnsHostname and enableDnsSupport is false)

We can use our own DNS servers as well. Amazon allows this, and it can be configured rather easily. All we have to do is create a new DHCP option set and fill required options with specific details. In Domain name servers option, we can specify comma separated IP addresses of our DNS servers and in Domain name, we can specify respective domain names. Once the DHCP option set is created, we can associate it with VPC and voila, it is done! DNS servers can be of any vendor or our own.
Fig. 5: This is a custom DHCP option set that I created. I have put my own domain name and IP address of DNS name servers.

Conclusion

Both DHCP and DNS are paramount to internet connectivity. If not for these services, we’d still be writing with pen and paper and using post offices. AWS leaves no stone unturned in making these services available and customizable. In the next blog, i.e., “How DHCP and DNS are managed in AWS VPC – Part 2” , I will show how we can host our own website on EC2 instances and make it accessible to the world using these AWS services.

Setting up MySQL Monitoring with Prometheus

One thing that I love about Prometheus is that it has a multitude of Integration with different services, both officially supported and the third party supported.
Let’s see how can we monitor MySQL with Prometheus.

Those who are the starter or new to Prometheus can refer to our this blog.

MySQL is a popular opensource relational database system, which exposed a large number of metrics for monitoring but not in Prometheus format. For capturing that data in Prometheus format we use mysqld_exporter.

I am assuming that you have already setup MySQL Server.

Configuration changes in MySQL

For setting up MySQL monitoring, we need a user with reading access on all databases which we can achieve by an existing user also but the good practice is that we should always create a new user in the database for new service.
CREATE USER 'mysqld_exporter'@'localhost' IDENTIFIED BY 'password' WITH MAX_USER_CONNECTIONS 3;
After creating a user we simply have to provide permission to that user.
GRANT PROCESS, REPLICATION CLIENT, SELECT ON *.* TO 'mysqld_exporter'@'localhost';

Setting up MySQL Exporter

Download the mysqld_exporter from GitHub. We are downloading the 0.11.0 version as per latest release now, change the version in future if you want to download the latest version.

wget https://github.com/prometheus/mysqld_exporter/releases/download/v0.11.0/mysqld_exporter-0.11.0.linux-amd64.tar.gz

Then simply extract the tar file and move the binary file at the appropriate location.
tar -xvf mysqld_exporter-0.11.0.linux-amd64.tar.gz
mv mysqld_exporter /usr/bin/
Although we can execute the binary simply, but the best practice is to create service for every Third Party binary application. Also, we are assuming that systemd is already installed in your system. If you are using init then you have to create init service for the exporter.

useradd mysqld_exporter
vim /etc/systemd/system/mysqld_exporter.service
[Unit]
Description=MySQL Exporter Service
Wants=network.target
After=network.target

[Service]
User=mysqld_exporter
Group=mysqld_exporter
Environment="DATA_SOURCE_NAME=mysqld_exporter:password@unix(/var/run/mysqd/mysqld.sock)"
Type=simple
ExecStart=/usr/bin/mysqld_exporter
Restart=always


[Install]
WantedBy=multi-user.target

You may need to adjust the socket location of Unix according to your environment
If you go and visit the http://localhost.com:9104/metrics, you will be able to see them.

Prometheus Configurations

For scrapping metrics from mysqld_exporter in Prometheus we have to make some configuration changes in Prometheus, the changes are not fancy, we just have to add another job for mysqld_exporter, like this:-
vim /etc/prometheus/prometheus.yml
  - job_name: 'mysqld_exporter'
static_configs:
- targets:
- :9104
After the configuration changes, we just have to restart the Prometheus server.

systemctl restart prometheus

Then, if you go to the Prometheus server you can find the MySQL metrics there like this:-

So In this blog, we have covered MySQL configuration changes for Prometheus, mysqld_exporter setup and Prometheus configuration changes.
In the next part, we will discuss how to create a visually impressive dashboard in Grafana for better visualization of MySQL metrics. See you soon… 🙂

Gitlab-CI with Nexus

Recently I was asked to set up a CI- Pipeline for a Spring based application.
I said “piece of cake”, as I have already worked on jenkins pipeline, and knew about maven so that won’t be a problem. But there was a hitch, “pipeline of Gitlab CI“. I said “no problem, I’ll learn about it” with a Ninja Spirit.
So for starters what is gitlab-ci pipeline. For those who have already work on Jenkins and maven, they know about the CI workflow of  Building a code , testing the code, packaging, and deploy it using maven. You can add other goals too, depending upon the requirement.
The CI process in GitLab CI is defined within a file in the code repository itself using a YAML configuration syntax.
The work is then dispatched to machines called runners, which are easy to set up and can be provisioned on many different operating systems. When configuring runners, you can choose between different executors like Docker, shell, VirtualBox, or Kubernetes to determine how the tasks are carried out.

What we are going to do?
We will be establishing a CI/CD pipeline using gitlab-ci and deploying artifacts to NEXUS Repository.

Resources Used:

  1. Gitlab server, I’m using gitlab to host my code.   
  2. Runner server, It could be vagrant or an ec2 instance. 
  3. Nexus Server, It could be vagrant or an ec2 instance.

     Before going further, let’s get aware of few terminologies. 

  • Artifacts: Objects created by a build process, Usually project jars, library jar. These can include use cases, class diagrams, requirements, and design documents.
  • Maven Repository(NEXUS): A repository is a directory where all the project jars, library jar, plugins or any other project specific artifacts are stored and can be used by Maven easily, here we are going to use NEXUS as a central Repository.
  • CI: A software development practice in which you build and test software every time a developer pushes code to the application, and it happens several times a day.
  • Gitlab-runner: GitLab Runner is the open source project that is used to run your jobs and send the results back to GitLab. It is used in conjunction with GitLab CI, the open-source continuous integration service included with GitLab that coordinates the jobs.
  • .gitlab-ci.yml: The YAML file defines a set of jobs with constraints stating when they should be run. You can specify an unlimited number of jobs which are defined as top-level elements with an arbitrary name and always have to contain at least the script clause. Whenever you push a commit, a pipeline will be triggered with respect to that commit.

Strategy to Setup Pipeline

Step-1:  Setting up GitLab Repository. 

I’m using a Spring based code Spring3Hibernate, with a directory structure like below.
$    cd spring3hibernateapp
$ ls
pom.xml pom.xml~ src

# Now lets start pushing this code to gitlab

$    git remote -v
origin git@gitlab.com:/spring3hibernateapp.git (fetch)
origin git@gitlab.com:/spring3hibernateapp.git (push)
# Adding the code to the working directory

$    git add -A
# Committing the code

$    git commit -m "[Master][Add] Adding the code "
# Pushing it to gitlab

$    git push origin master

Step-2:  Install GitLab Runner manually on GNU/Linux

# Simply download one of the binaries for your system:

$    sudo wget -O /usr/local/bin/gitlab-runner https://gitlab-runner-downloads.s3.amazonaws.com/latest/binaries/gitlab-runner-linux-386

# Give it permissions to execute:

$    sudo chmod +x /usr/local/bin/gitlab-runner 

# Optionally, if you want to use Docker, install Docker with:

$    curl -sSL https://get.docker.com/ | sh 

# Create a GitLab CI user:

$    sudo useradd --comment 'GitLab Runner' --create-home gitlab-runner --shell /bin/bash 

# Install and run as service:

$    sudo gitlab-runner install --user=gitlab-runner --working-directory=/home/gitlab-runner
$ sudo gitlab-runner start

Step-3: Registering a Runner

To get the runner configuration you need to move to gitlab > spring3hibernateapp > CI/CD setting > Runners
And get the registration token for runners.

# Run the following command:

$     sudo gitlab-runner register
Runtime platform arch=amd64 os=linux pid=1742 revision=3afdaba6 version=11.5.0
Running in system-mode.
 

# Please enter the gitlab-ci coordinator URL (e.g. https://gitlab.com/):

https://gitlab.com/

# Please enter the gitlab-ci token for this runner:

****8kmMfx_RMr****

# Please enter the gitlab-ci description for this runner:

[gitlab-runner]: spring3hibernate

# Please enter the gitlab-ci tags for this runner (comma separated):

build
Registering runner... succeeded runner=ZP3TrPCd

# Please enter the executor: docker, docker-ssh, shell, ssh, virtualbox, docker+machine, parallels, docker-ssh+machine, kubernetes:

docker

# Please enter the default Docker image (e.g. ruby:2.1):

maven       
Runner registered successfully. Feel free to start it, but if it's running already the config should be automatically reloaded!

# You can also create systemd service in /etc/systemd/system/gitlab-runner.service.

[Unit]
Description=GitLab Runner
After=syslog.target network.target
ConditionFileIsExecutable=/usr/local/bin/gitlab-runner

[Service]
StartLimitInterval=5
StartLimitBurst=10
ExecStart=/usr/local/bin/gitlab-runner "run" "--working-directory" "/home/gitlab-runner" "--config" "/etc/gitlab-runner/config.toml" "--service" "gitlab-runner" "--syslog" "--user" "gitlab-runner"
Restart=always
RestartSec=120

[Install]
WantedBy=multi-user.target

Step-4: Setting up Nexus Repository
You can setup a repository installing the open source version of Nexus you need to visit Nexus OSS and download the TGZ version or the ZIP version.
But to keep it simple, I used docker container for that.
# Install docker

$    curl -sSL https://get.docker.com/ | sh

# Launch a NEXUS container and bind the port

$    docker run -d -p 8081:8081 --name nexus sonatype/nexus:oss

You can access your nexus now on http://:8081/nexus.
And login as admin with password admin123.

Step-5: Configure the NEXUS deployment

Clone your code and enter the repository

$    cd spring3hibernateapp/

# Create a folder called .m2 in the root of your repository

$    mkdir .m2

# Create a file called settings.xml in the .m2 folder

$    touch .m2/settings.xml

# Copy the following content in settings.xml

<settings xsi:schemaLocation="http://maven.apache.org/SETTINGS/1.1.0 http://maven.apache.org/xsd/settings-1.1.0.xsd"
xmlns="http://maven.apache.org/SETTINGS/1.1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">


central
${env.NEXUS_REPO_USER}
${env.NEXUS_REPO_PASS}


snapshots
${env.NEXUS_REPO_USER}
${env.NEXUS_REPO_PASS}


 Username and password will be replaced by the correct values using variables.
# Updating Repository path in pom.xml


central
Central
${env.NEXUS_REPO_URL}central/


snapshots
Snapshots
${env.NEXUS_REPO_URL}snapshots/

Step-6: Configure GitLab CI/CD for simple maven deployment.

GitLab CI/CD uses a file in the root of the repo, named, .gitlab-ci.yml, to read the definitions for jobs that will be executed by the configured GitLab Runners.
First of all, remember to set up variables for your deployment. Navigate to your project’s Settings > CI/CD > Variables page and add the following ones (replace them with your current values, of course):

Now it’s time to define jobs in .gitlab-ci.yml and push it to the repo:

image: maven

variables:
MAVEN_CLI_OPTS: "-s .m2/settings.xml --batch-mode"
MAVEN_OPTS: "-Dmaven.repo.local=.m2/repository"

cache:
paths:
- .m2/repository/
- target/

stages:
- build
- test
- package
- deploy

codebuild:
tags:
- build
stage: build
script:
- mvn compile

codetest:
tags:
- build
stage: test
script:
- mvn $MAVEN_CLI_OPTS test
- echo "The code has been tested"

Codepackage:
tags:
- build
stage: package
script:
- mvn $MAVEN_CLI_OPTS package -Dmaven.test.skip=true
- echo "Packaging the code"
artifacts:
paths:
- target/*.war
only:
- master

Codedeploy:
tags:
- build
stage: deploy
script:
- mvn $MAVEN_CLI_OPTS deploy -Dmaven.test.skip=true
- echo "installing the package in local repository"
only:
- master

Now add the changes, commit them and push them to the remote repository on gitlab. A pipeline will be triggered with respect to your commit. And if everything goes well our mission will be accomplished.
Note: You might get some issues with maven plugins, which will need to managed in pom.xml, depending upon the environment.
In this blog, we covered the basic steps to use a Nexus Maven repository to automatically publish and consume artifacts.

Ansible Variables and Variables Precedence

Ansible Variables and Variables Precedence

What is a variable?

A variable is reference to data stored in a computer program.

How to define variable in Ansible?

Variables can be defined in inventory in two ways –
1. Host variables
[web]
host1 http_port=80
host2 http_port=443
2. Group variables
[web]
host1
host2

[web:vars]
http_port=80
proxy=proxy.example.com

You can directly define variables in a playbook:
– hosts: appgroup
 vars:
   http_port: 80

You can define your variables in a YAML file and store it in VARS folder.
You can include your variable file in your task file “using include_vars”.

Eg – include_vars: variable.yml

You can also use the variables defined in the remote system as facts.
To gather all the information about remote server you can run the command:

ansible hostname -m setup

You can also write own facts modules. “Facts.d” is one way to control some aspect of 
how systems are managed.

If a remotely managed system has an /etc/ansible/facts.d directory, any files in this directory 
ending in .fact, can be JSON, INI, or executable files returning JSON, and these can supply 
local facts in Ansible.

Eg – assume /etc/ansible/facts.d/http.fact contains:
[general]
http_port=80
ssl_port=22

You can also store result of a command into a variable.

– hosts: appgroup

 tasks:

    – shell: /usr/bin/foo
      register: foo_result
      ignore_errors: True

    – shell: /usr/bin/bar
      when: foo_result.rc == 5

You can also define your variables in different files.

– hosts: appgroup
 vars:
    http_port: 80
 vars_files:
   – /vars/http.yml

You can also set the variables in command line using –extra-vars or -e.

Eg: ansible-playbook test.yml -e “http_port=80”

Variable Precedence

If multiple variables of the same name are defined in different places, they get overwritten in a 
certain order.
Here is the order of variable precedence from low to high.
  • role defaults 
  • inventory file or script group vars 
  • inventory group_vars/all  
  • playbook group_vars/all 
  • inventory group_vars/* 
  • playbook group_vars/* 
  • inventory file or script host vars 
  • inventory host_vars/* 
  • playbook host_vars/* 
  • host facts / cached set_facts 
  • play vars 
  • play vars_prompt 
  • play vars_files 
  • role vars (defined in role/vars/main.yml) 
  • block vars (only for tasks in block) 
  • task vars (only for the task) 
  • include_vars 
  • set_facts / registered vars 
  • role (and include_role) params 
  • include params 
  • extra vars (always win precedence)

To verify this what I did, is to define the variables in different locations and then remove 
the values from those location one by one.
Here I have created a role to create a user in linux. You can see the files where I have 
defined those variables.
default/main.yml

# defaults file for ansible/roles/user

user_name: john_cena

/etc/ansible/hosts (group vars)

# This is the default ansible ‘hosts’ file.
#
# It should live in /etc/ansible/hosts
#
#   – Comments begin with the ‘#’ character
#   – Blank lines are ignored
#   – Groups of hosts are delimited by [header] elements
#   – You can enter hostnames or ip addresses
#   – A hostname/ip can be a member of multiple groups

[appgroup]
appserver1
appserver4

[appgroup:vars]
user_name=batista
/etc/ansible/hosts (host vars)

# This is the default ansible ‘hosts’ file.
#
# It should live in /etc/ansible/hosts
#
#   – Comments begin with the ‘#’ character
#   – Blank lines are ignored
#   – Groups of hosts are delimited by [header] elements
#   – You can enter hostnames or ip addresses
#   – A hostname/ip can be a member of multiple groups

[appgroup]
appserver1 user_name=roman_reigns
appserver4  user_name=roman_reigns

/tasks/main.yml (play vars)

# tasks file for ansible/roles/user

#- name: Include user yml file.
#  include_vars: “user.yml”

– name: Add the user
 vars:
    user_name: triple_h
 user:
    name: “{{ user_name }}”
    comment: John Doe
    uid: 1045
    group: vagrant
/vars/user.yml (include_file)

 user_name: edge

/tasks/main.yml (set_fact)

# tasks file for ansible/roles/user

– name: Set user name.
 set_fact:
    user_name: undertaker

– name: Add the user
 user:
    name: “{{ user_name }}”
    comment: John Doe
    uid: 1051
    group: vagrant

Extra vars

ansible-playbook add_user.yml -e “ user_name=cm_punk”
In this way you can check precedence of variables in ansible role.

Refernce

Linux Namespaces – Part 1

Overview

First of all I would like to give credit to Docker which motivated me to write this blog, I’ve been using docker for more then 6 months but I always wondered how things are happening behind the scene. So I started in depth learning of Docker and here I am talking about Namespace which is the core concept used by Docker.

Before talking about Namespaces in Linux, it is very important to know that what namespaces actually is?

Let’s take an example, We have two people with the same first name Abhishek Dubey and Abhishek Rawat but we can differentiate them on the basis of their surname Dubey and Rawat. So you can think surname as a namespace.

In Linux, namespaces are used to provide isolation for objects from other objects. So that anything will happen in namespaces will remain in that particular namespace and doesn’t affect other objects of other namespaces. For example:- we can have the same type of objects in different namespaces as they are isolated from each other.

In short, due to isolation, namespaces limits how much we can see.

Now you would be having a good conceptual idea of Namespace let’s try to understand them in the context of Linux Operating System.

Linux Namespaces

Linux namespace forms a single hierarchy, with all processes and that is init. Usually, privileged processes and services can trace or kill other processes. Linux namespaces provide the functionality to have many hierarchies of processes with their own “subtrees”, such that, processes in one subtree can’t access or even know those of another.
A namespace wraps a global system resource (For ex:- PID) using the abstraction that makes it appear to processes within the namespace that they have, using their own isolated instance of the said resource.

In the above figure, we have a process named 1 which is the first PID and from 1 parent process there are new PIDs are generated just like a tree. If you see the 6th PID in which we are creating a subtree, there actually we are creating a different namespace. In the new namespace, 6th PID will be its first and parent PID. So the child processes of 6th PID cannot see the parent process or namespace but the parent process can see the child PIDs of the subtree.

Let’s take PID namespace as an example to understand it more clearly. Without namespace, all processes descend(move downwards) hierarchically from First PID i.e. init. If we create PID namespace and run a process in it, the process becomes the First PID in that namespace. In this case, we wrap a global system resource(PID). The process that creates the namespace still remains in the parent namespace but makes it child for the root of the new process tree.
This means that the processes within the new namespace cannot see the parent process but the parent process can see the child namespace process. 
I hope you have got a clear understanding of Namespaces concepts & what purpose they serve in a Linux OS. The next blog of this series will talk about how we use namespace to restrict usage of system resources such as network, mounts, cgroups…
    Design a site like this with WordPress.com
    Get started