Wednesday, October 30, 2019

Pacemaker Cluster on OCI


Keepalived Configuration:

[root@vip1 ~]# cat /etc/redhat-release
CentOS release 6.9 (Final)



Simple Keepalived Configuration:

[root@vip1 keepalived]# cat /etc/keepalived/keepalived.conf
global_defs {
   notification_email {
     root@localhost
   }
   notification_email_from svr1@localhost
   smtp_server localhost
   smtp_connect_timeout 30
}
vrrp_instance VRRP1 {
#    debug 2
    state MASTER
#   Specify the network interface to which the virtual address is assigned
    interface eth0
#   The virtual router ID must be unique to each VRRP instance that you define
    virtual_router_id 41
    unicast_src_ip 10.0.0.3
    unicast_peer {
10.0.0.4
    }
#   Set the value of priority higher on the master server than on a backup server
    priority 200
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1066
    }
    virtual_ipaddress {
        10.0.0.100/24
    }
    nopreempt
    notify_master /etc/keepalived/vip.sh
}
[root@vip1 keepalived]#


[root@vip1 keepalived]# cat vip.sh
VNIC=ocid1.vnic.oc1.phx.abyhqljstoq64rxxkzl4yf3f6jixbckjhtxkf22i5znfpqxi2aasqyxltsda
/root/bin/oci network vnic assign-private-ip --vnic-id $VNIC --ip-address 10.0.0.100 --unassign-if-already-assigned --region us-phoenix-1



—> Need Update
[root@vip1 keepalived]# cat keepalived.conf
global_defs {
   notification_email {
     root@localhost
   }
   notification_email_from svr1@localhost
   smtp_server localhost
   smtp_connect_timeout 30
}
vrrp_script chk_httpd {
    script "pidof httpd"
    interval 2
}
vrrp_instance VRRP1 {
#    debug 2
    state MASTER
#   Specify the network interface to which the virtual address is assigned
    interface eth0
#   The virtual router ID must be unique to each VRRP instance that you define
    virtual_router_id 41
    unicast_src_ip 10.0.0.3 # Private IP
    unicast_peer {
10.0.0.4 # Peer IP
    }
#   Set the value of priority higher on the master server than on a backup server
    priority 200
    advert_int 1
    authentication {
        auth_type PASS
        auth_pass 1066
    }
    track_script {
        chk_httpd
    }
    virtual_ipaddress {
        10.0.0.100/24
    }
    nopreempt
    notify_master /etc/keepalived/vip.sh
}
virtual_server 10.0.0.100 80 {
    delay_loop 6
    lb_algo wrr
    lb_kind DR
    nat_mask 255.255.255.0
    persistence_timeout 50
    protocol TCP
real_server 10.0.0.3 80 {
    weight 1
    #notify_down /etc/keepalived/check_httpd.sh
#    
# Health Check
    TCP_CHECK {
        connect_timeout 10
        nb_get_retry 3
        connect_port 80
}
}   
}
[root@vip1 keepalived]


[root@vip1 keepalived]# cat check_httpd.sh
#!/bin/bash
VNIC=ocid1.vnic.oc1.phx.abyhqljsmtjaqvakuivgjqs4fd3rltx2uc2epwicrj6j52fuzaopbgbcs33q
/root/bin/oci network vnic assign-private-ip --vnic-id $VNIC --ip-address 10.0.0.100 --unassign-if-already-assigned --region us-phoenix-1

Logs from Slave node which transitions to master.

[root@vip2 ~]# tail -50  /var/log/messages
Jun 30 09:47:28 vip2 Keepalived[2379]: Starting Keepalived v1.2.13 (03/19,2015)
Jun 30 09:47:28 vip2 Keepalived[2380]: Starting Healthcheck child process, pid=2381
Jun 30 09:47:28 vip2 Keepalived[2380]: Starting VRRP child process, pid=2382
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: Netlink reflector reports IP 10.0.0.4 added
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: Netlink reflector reports IP fe80::200:17ff:fe01:4eae added
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: Registering Kernel netlink reflector
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: Registering Kernel netlink command channel
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: Registering gratuitous ARP shared channel
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: Opening file '/etc/keepalived/keepalived.conf'.
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: Configuration is using : 66719 Bytes
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: Using LinkWatch kernel netlink reflector...
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: VRRP_Instance(VRRP1) Entering BACKUP STATE
Jun 30 09:47:28 vip2 Keepalived_vrrp[2382]: VRRP sockpool: [ifindex(2), proto(112), unicast(1), fd(10,11)]
Jun 30 09:47:29 vip2 Keepalived_vrrp[2382]: VRRP_Script(chk_httpd) succeeded
Jun 30 09:47:29 vip2 kernel: IPVS: Registered protocols (TCP, UDP, SCTP, AH, ESP)
Jun 30 09:47:29 vip2 kernel: IPVS: Connection hash table configured (size=4096, memory=64Kbytes)
Jun 30 09:47:29 vip2 Keepalived_healthcheckers[2381]: Netlink reflector reports IP 10.0.0.4 added
Jun 30 09:47:29 vip2 Keepalived_healthcheckers[2381]: Netlink reflector reports IP fe80::200:17ff:fe01:4eae added
Jun 30 09:47:29 vip2 Keepalived_healthcheckers[2381]: Registering Kernel netlink reflector
Jun 30 09:47:29 vip2 Keepalived_healthcheckers[2381]: Registering Kernel netlink command channel
Jun 30 09:47:29 vip2 Keepalived_healthcheckers[2381]: Opening file '/etc/keepalived/keepalived.conf'.
Jun 30 09:47:29 vip2 Keepalived_healthcheckers[2381]: Configuration is using : 11990 Bytes
Jun 30 09:47:29 vip2 kernel: IPVS: ipvs loaded.
Jun 30 09:47:29 vip2 Keepalived_healthcheckers[2381]: Using LinkWatch kernel netlink reflector...
Jun 30 09:47:29 vip2 Keepalived_healthcheckers[2381]: Activating healthchecker for service [10.0.0.4]:80
Jun 30 09:47:29 vip2 kernel: IPVS: [wrr] scheduler registered.
Jun 30 09:50:42 vip2 Keepalived_vrrp[2382]: VRRP_Instance(VRRP1) Transition to MASTER STATE
Jun 30 09:50:43 vip2 Keepalived_vrrp[2382]: VRRP_Instance(VRRP1) Entering MASTER STATE
Jun 30 09:50:43 vip2 Keepalived_vrrp[2382]: VRRP_Instance(VRRP1) setting protocol VIPs.
Jun 30 09:50:43 vip2 Keepalived_healthcheckers[2381]: Netlink reflector reports IP 10.0.0.100 added
Jun 30 09:50:43 vip2 Keepalived_vrrp[2382]: VRRP_Instance(VRRP1) Sending gratuitous ARPs on eth0 for 10.0.0.100
Jun 30 09:50:44 vip2 ntpd[2238]: Listen normally on 6 eth0 10.0.0.100 UDP 123
Jun 30 09:50:48 vip2 Keepalived_vrrp[2382]: VRRP_Instance(VRRP1) Sending gratuitous ARPs on eth0 for 10.0.0.100






Pacemaker Corosync

  1. Install Cluster packages:
#  yum install -y pacemaker pcs psmisc policycoreutils-python

  1. Setup firewall:
# firewall-cmd --permanent --add-service=high-availability --add-service=http --add-service=https
# firewall-cmd --reload

Ports required to be opened:
TCP ports 2224, 3121, and 21064, and UDP port 5405.

  1. Start pcs daemon:
# systemctl start pcsd.service
# systemctl enable pcsd.service
ln -s '/usr/lib/systemd/system/pcsd.service' '/etc/systemd/system/multi-user.target.wants/pcsd.service’

  1. Setup password for user hacluster:
# echo | passed --stdin hacluster

  1. Configure Corosync:
On one of the nodes:
# pcs cluster auth node1 node2
Username: hacluster
Password:
node1: Authorized
node2: Authorized


# pcs cluster setup --name mycluster node1 node2
Shutting down pacemaker/corosync services...
Redirecting to /bin/systemctl stop  pacemaker.service
Redirecting to /bin/systemctl stop  corosync.service
Killing any remaining services...
Removing all cluster configuration files...
node1: Succeeded
node2: Succeeded

Start the cluster:
# pcs cluster start --all
node1: Starting Cluster...
node2: Starting Cluster...

[root@node1 ~]# corosync-cfgtool -s
Printing ring status.
Local node ID 1
RING ID 0
    id    = 10.0.0.12
    status    = ring 0 active with no faults

[root@node1 ~]# corosync-cmapctl  | grep members
runtime.totem.pg.mrp.srp.members.1.config_version (u64) = 0
runtime.totem.pg.mrp.srp.members.1.ip (str) = r(0) ip(10.0.0.12)
runtime.totem.pg.mrp.srp.members.1.join_count (u32) = 1
runtime.totem.pg.mrp.srp.members.1.status (str) = joined
runtime.totem.pg.mrp.srp.members.2.config_version (u64) = 0
runtime.totem.pg.mrp.srp.members.2.ip (str) = r(0) ip(10.0.0.14)
runtime.totem.pg.mrp.srp.members.2.join_count (u32) = 1
runtime.totem.pg.mrp.srp.members.2.status (str) = joined
[root@node1 ~]#

  1. Disable Stonith:
# pcs property set stonith-enabled=false
# crm_verify -L -V

  1. Add Floating IP:

Update IPaddr2 resource so that it will reassign Private IP on the OCI infrastructure as well.

sudo sed -i '64i\##### OCI vNIC variables\' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '65i\server="`hostname -s`"\' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '66i\node1vnic="ocid1.vnic.oc1.phx.abyhqljs2qwsjkgsi7ujg735xig3xfnq2w5h2slvl33lqw24wn5rtjpfqvia"\' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '67i\node2vnic="ocid1.vnic.oc1.phx.abyhqljs6qpbs6w5peguzucokmx3eh6wvu7jauxwntsgz5zj2krfgrgzclzq"\' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '68i\vnicip="10.0.0.200"\' /usr/lib/ocf/resource.d/heartbeat/IPaddr2


sudo sed -i '614i\##### OCI/IPaddr Integration\' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '615i\        if [ $server = "node1" ]; then\' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '616i\                /root/bin/oci network vnic assign-private-ip --unassign-if-already-assigned --vnic-id $node1vnic  --ip-address $vnicip \' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '617i\        else \' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '618i\                /root/bin/oci network vnic assign-private-ip --unassign-if-already-assigned --vnic-id $node2vnic  --ip-address $vnicip \' /usr/lib/ocf/resource.d/heartbeat/IPaddr2
sudo sed -i '619i\        fi \' /usr/lib/ocf/resource.d/heartbeat/IPaddr2


Updated IPaddr2 resource should contain 

[root@node2 ~]# grep -A5 OCI  /usr/lib/ocf/resource.d/heartbeat/IPaddr2
##### OCI vNIC variables
server="`hostname -s`"
node1vnic=“<Node1 VNIC OCID>"
node2vnic=“"
vnicip=“"

--
##### OCI/IPaddr Integration
        if [ $server = "node1" ]; then
                /root/bin/oci network vnic assign-private-ip --unassign-if-already-assigned --vnic-id $node1vnic  --ip-address $vnicip
        else
                /root/bin/oci network vnic assign-private-ip --unassign-if-already-assigned --vnic-id $node2vnic  --ip-address $vnicip
        fi


  1. Setup Floating IP:
[root@node1 ~]# pcs resource create ClusterIP ocf:heartbeat:IPaddr2 ip=10.0.0.200 cidr_netmask=32 op monitor interval=30s


  1. Specify resource stickiness:
# pcs resource defaults resource-stickiness=100
# pcs resource defaults
resource-stickiness: 100


  1. Setup Nginx:
# yum install nginx

[root@node1 ~]# cat /usr/share/nginx/html/index.html
This is NODE1

Status page:
[root@node1 ~]# cat /usr/share/nginx/html/nginx_status
node1 is alive.

# cat /etc/nginx/default.d/status.conf
location ^~ /nginx_status {
    allow 127.0.0.1;
    deny all;
}

[root@node1 ~]# cat /etc/nginx/nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;

# Load dynamic modules. See /usr/share/nginx/README.dynamic.
include /usr/share/nginx/modules/*.conf;

events {
    worker_connections 1024;
}

http {
    log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

    access_log  /var/log/nginx/access.log  main;

    sendfile            on;
    tcp_nopush          on;
    tcp_nodelay         on;
    keepalive_timeout   65;
    types_hash_max_size 2048;

    include             /etc/nginx/mime.types;
    default_type        application/octet-stream;

#    include /etc/nginx/conf.d/*.conf;

    server {
        listen       80 default_server;
        listen       [::]:80 default_server;
        server_name  _;
        root         /usr/share/nginx/html;

        # Load configuration files for the default server block.
        include /etc/nginx/default.d/*.conf;

        location / {
        }

        error_page 404 /404.html;
            location = /40x.html {
        }

        error_page 500 502 503 504 /50x.html;
            location = /50x.html {
        }
}


  1. Setup Nginx resource
[root@node1 ~]# pcs resource create webserver ocf:heartbeat:nginx configfile=/etc/nginx/nginx.conf status10url="http://localhost/nginx_status" status10regex="node[1-9] is alive." op monitor timeout="30s" interval="10s" op start timeout="40s" interval="0" op stop timeout="60s" interval="0"
[root@node1 ~]#

[root@node1 ~]# pcs resource
ClusterIP    (ocf::heartbeat:IPaddr2):    Started node2
webserver    (ocf::heartbeat:nginx):    Started node1

  1. Create colocation constraint so that web server resource sticks with ClusterIP:
[root@node1 ~]# pcs constraint colocation add webserver with ClusterIP INFINITY

  1. Setup resource startup order:
[root@node1 ~]# pcs constraint order ClusterIP then webserver
Adding ClusterIP webserver (kind: Mandatory) (Options: first-action=start then-action=start)


[root@node1 ~]# pcs status
Cluster name: mycluster
Stack: corosync
Current DC: node2 (version 1.1.18-11.el7_5.2-2b07d5c5a9) - partition with quorum
Last updated: Sat Jun 30 16:23:40 2018
Last change: Sat Jun 30 15:51:20 2018 by root via crm_resource on node2

2 nodes configured
2 resources configured

Online: [ node1 node2 ]

Full list of resources:

ClusterIP    (ocf::heartbeat:IPaddr2):    Started node2
webserver    (ocf::heartbeat:nginx):    Started node2

Daemon Status:
  corosync: active/disabled
  pacemaker: active/disabled
  pcsd: active/enabled

[root@node1 ~]# pcs constraint
Location Constraints:
  Resource: webserver
    Enabled on: node2 (score:INFINITY) (role: Started)
Ordering Constraints:
  start ClusterIP then start webserver (kind:Mandatory)
Colocation Constraints:
  webserver with ClusterIP (score:INFINITY)
Ticket Constraints: 
 
 
 

Wednesday, May 8, 2019

How to view httpd.conf file or any config files in Linux without coments

Hello,

Have you ever wondered how to view the Linux configurations files without those comments? Well those comments are indeed helpful, but think about configuration files such as httpd.conf and squid.conf files. These files have good amount of commented lines.

The issue with the httpd.conf file in particular is that not all commented lines starts with #. Some commented lines start after a tab.

Example:
# Further relax access to the default document root:

    #
    # Possible values for the Options directive are "None", "All",
    # or any combination of:
    #   Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
You could remove those tabbed comments as well using "sed" or using "egrep" with proper Regex.


# egrep -v "^$|*#" /etc/httpd/conf/httpd.conf

Or

# sed '/ *#/d; /^ *$/d' /etc/httpd/conf/httpd.conf


Hope this helps.

Regards,
Jay

Thursday, April 4, 2019

Automatically encrypt Ephemeral volumes on AWS EC2 instances

Encrypting Data at rest if mandatory requirement compliance regulations such as PCI DSS and HIPAA. EBS, S3 has an option to encrypt the data stored in it. However, Instance Store volumes which provides temporary block level storage does have an option to encrypt the data stored in it. Customers need to use configure encryption using tools like dm-crypt.

Lets see how to automate encrypting the ephemeral volumes for Instance Store volumes on EC2 instances. I have written a user-data script which takes care of all the encryption setups.

The script will do the below to make it seamless for the customer:

- When you launch an instance or change the instance type, the script automatically detects the Ephemeral disks available to the instance type, setup encryption and make them available.

- Automatically installs the required packages for encryption if its not installed.

- The script also takes care of encryption in case of instance reboot or stop/start.

- The user-data script automatically take care of Stop/Start, Reboots and instance re-size.

- After stop/start you will lose all the data and configuration on the ephemeral volumes. This is expected, the user-data script will automatically detect this and setup encryption  on the new disks and mounts it.

- When the instance is rebooted, the data persists. On a reboot we just need to decrypt the filesystem and mount it.

- The user data script will encrypt the volume and create a filesystem if its not already encrypted (These actions will be taken if you launch a fresh instance with this user-data or when you do a stop & start of your instance).

Now, if the volumes are already encrypted (this will happen on an instance reboot), the script will simply initialize the encrypted volume and mount it on the respective directory.

- The script will encrypt all the ephemeral volumes, create EXT4 filesystem and mount them under /encrypted_X (X being the last letter of the device name of the NVMe device name).
You may change the filesystem type, mount point as per your requirement, but you will need to update the logic to map volumes to correct mount point on the script.

- The encryption key/passphrase used is encrypted using AWS KMS and kept in a file in your S3 bucket. This is a one time setup and you could use the same encrypted passphrase file for your future installations. Instance need to be attached with an IAM instance profile with permission to download files from the S3 bucket.









Here are the step by step instructions:
1. Create an S3 Bucket for storing encrypted password file (or use an existing bucket in your account).

2. Create a IAM policy with the below policy document, this policy is going to be used for instance profile to allow it download the password file from s3 bucket:

Sign in to the AWS Management Console and navigate to the IAM console: https://console.aws.amazon.com/iam/home?region=us-east-1#/home
In the navigation pane, choose Policies, choose Create Policy, select Create Your Own Policy, name and describe the policy, and paste the following policy. Choose Create Policy.


{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "Stmt1478729875000",
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": [
                "arn:aws:s3:::
/LuksInternalStorageKey"
            ]
        }
    ]
}

 


---> Replace with the one you created/selected in step 1.



3. Create an EC2 role with the above policy.

In the IAM console, choose Roles, and then choose Create New Role.
In Step 1: Role Name, type your role name, and choose Next Step.
In Step 2: Select Role Type, choose Amazon EC2 and choose Next Step.
In Step 3: Established Trust, choose Next Step.
In Step 4: Attach Policy, choose the policy you created in Step 1

4. Create a KMS Key and add usage permission for the role you created above from the KMS console: https://console.aws.amazon.com/iam/home?region=us-east-1#/encryptionKeys/us-east-1
Step 1: Click ‘Create key’.
Step 2: Provide an Alias and Description.
Step 3: Add IAM role you created above as Key user under - ‘Define Key Usage Permissions’.

5. Create a secret password, encrypt it with KMS and store it in S3.

# aws --region us-east-1 kms encrypt --key-id 'alias/EncFSForEC2InternalStorageKey' --plaintext "ThisIs-a-SecretPassword" --query CiphertextBlob --output text | base64 --decode > LuksInternalStorageKey

--> Replace 'alias/EncFSForEC2InternalStorageKey' with the id of the KMS key you created above. The IAM user as which you run the above command should have permission to access the KMS key as well.
—> You should also replace the string “ThisIs-a-SecretPassword" with some strong passphrase.

6. Upload the encrypted key file to S3.

# aws s3 cp LuksInternalStorageKey s3:///LuksInternalStorageKey

7. Add the below userdata script to your instance at launch time. You can add/modify the user-data on a stopped instance as well.

I3, F1 instance type provides NVMe based instance store volumes and those disks are detected as /dev/nvmeXn1, X being the NVMe device number. Other instance types provides HDD/SSD based instance store volumes which gets detected as /dev/sdX. The user-data script detect the device names from EC2 meta-data in case of HDD/SSD and using nvme tool in case of NVMe based disks. I have written separate user data script for these volume types.

Use - user-data_NVMe_InstanceStore.txt for I3, F1 instances with NVMe disks.
Use - user-data_InstanceStore.txt for other instance types.

8. Once the instance is launched, you need to update the cloud-init configuration to make sure the user data runs every time the instance boots.

On Amazon Linux AMI based instances:
# sed -i 's/scripts-user/[scripts-user, always]/' /etc/cloud/cloud.cfg.d/00_defaults.cfg

On RHEL/CentOS instances:
# sed -i 's/scripts-user/[scripts-user, always]/' /etc/cloud/cloud.cfg


9. On Amazon Linux AMI instances, you might need disable crypt module on boot time to make sure the encrypted filesystems are not detected on the early boot stages:
# sed -i 's/#omit_dracutmodules+=""/omit_dracutmodules+="crypt"/' /etc/dracut.conf
# dracut --force

 


User Data Scripts:

-- user-data_InstanceStore.txt
#!/bin/bash

REGION=
S3_Bucket=

# Install required packages if not installed
[ $(which unzip) ] || yum -y install unzip
[ $(which aws) ] || "$(/usr/bin/curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip" ; /usr/bin/unzip -o awscli-bundle.zip; ./awscli-bundle/install -b /usr/bin/aws ; rm -rf /awscli-bundle*)"
[ $(which cryptsetup) ] || yum install -y cryptsetup

# Get the encrypted password file from s3
/usr/bin/aws s3 cp s3://${S3_Bucket}/LuksInternalStorageKey .
# Decrypt and store the passphrase in a variable
LuksClearTextKey=$(/usr/bin/aws --region {REGION} kms decrypt --ciphertext-blob fileb://LuksInternalStorageKey --output text --query Plaintext | base64 --decode)

for ephemeral in $(curl -s http://169.254.169.254/latest/meta-data/block-device-mapping/ |grep ephemeral)
do
DEV=$(curl -s http://169.254.169.254/latest/meta-data/block-device-mapping/$ephemeral |sed 's/s/\/dev\/xv/g')
DEV_1=`echo "${DEV: -1}"`

[ "$(/bin/mount | grep -i ${DEV})" ] && /bin/umount ${DEV}

TYPE=`/usr/bin/file -sL ${DEV} | awk '{print $2}'`

if [ $TYPE == "LUKS" ]
then
    # Open and initialize the encryped volume
    /bin/echo "$LuksClearTextKey" | /sbin/cryptsetup luksOpen ${DEV} encfs_${DEV_1}
    # Check and create mount point if not exists
    [ -d /encrypted_${DEV_1} ] || /bin/mkdir /encrypted_${DEV_1}
    # Mount the filsystem
    /bin/mount /dev/mapper/encfs_${DEV_1} /encrypted_${DEV_1}
else
     # Encrypt the volume sub
     /bin/echo "$LuksClearTextKey" | cryptsetup -y luksFormat ${DEV}
     # Open and initialize the encryped volume
    /bin/echo "$LuksClearTextKey" | cryptsetup luksOpen ${DEV} encfs_${DEV_1}
    # create a filesystem on the encrypted volume, mount it on the required directory
    /sbin/mkfs.ext4 /dev/mapper/encfs_${DEV_1}
    [ -d /encrypted_${DEV_1} ] || /bin/mkdir /encrypted_${DEV_1}
    /bin/mount /dev/mapper/encfs_${DEV_1} /encrypted_${DEV_1}
fi
done
# Unset the passphrase variable and remove the encrypted password file.
unset LuksInternalStorageKey
rm LuksInternalStorageKey
-- user-data_NVMe_InstanceStore.txt
#!/bin/bash

#set -x

REGION=
S3_Bucket=

# Install required packages if not installed
[ $(which unzip) ] || yum install -y zip unzip
[ $(which python) ] || yum install -y python
[ $(which aws) ] || "$(/usr/bin/curl "https://s3.amazonaws.com/aws-cli/awscli-bundle.zip" -o "awscli-bundle.zip" ; /usr/bin/unzip -o awscli-bundle.zip; ./awscli-bundle/install -b /usr/bin/aws ; rm -rf /awscli-bundle*)"
[ "$(which cryptsetup)" ] || yum install -y cryptsetup
[ "$(which nvme)" ] || yum install -y nvme-cli

# Get the encrypted password file from s3
/usr/bin/aws s3 cp s3://${S3_Bucket}/LuksInternalStorageKey .
# Decrypt and store the passphrase in a variable
LuksClearTextKey=$(/usr/bin/aws --region ${REGION} kms decrypt --ciphertext-blob fileb://LuksInternalStorageKey --output text --query Plaintext | base64 --decode)

for ephemeral in $(nvme list | grep dev | awk {'print $1'})
do
DEV_1=$(echo "${ephemeral:9:1}")

[ "$(/bin/mount | grep -i ${ephemeral})" ] && /bin/umount ${ephemeral}

TYPE=`/usr/bin/file -sL ${ephemeral} | awk '{print $2}'`

if [ $TYPE == "LUKS" ]
then
    # Open and initialize the encryped volume
    /bin/echo "$LuksClearTextKey" | /sbin/cryptsetup luksOpen ${ephemeral} encfs_${DEV_1}
    # Check and create mount point if not exists
    [ -d /encrypted_${DEV_1} ] || /bin/mkdir /encrypted_${DEV_1}
    # Mount the filsystem
    /bin/mount /dev/mapper/encfs_${DEV_1} /encrypted_${DEV_1}
else
     # Encrypt the volume sub
     /bin/echo "$LuksClearTextKey" | cryptsetup -y luksFormat ${ephemeral}
     # Open and initialize the encryped volume
    /bin/echo "$LuksClearTextKey" | cryptsetup luksOpen ${ephemeral} encfs_${DEV_1}
    # create a filesystem on the encrypted volume, mount it on the required directory
    /sbin/mkfs.ext4 /dev/mapper/encfs_${DEV_1}
    [ -d /encrypted_${DEV_1} ] || /bin/mkdir /encrypted_${DEV_1}
    /bin/mount /dev/mapper/encfs_${DEV_1} /encrypted_${DEV_1}
fi
done
# Unset the passphrase variable and remove the encrypted password file.
unset LuksInternalStorageKey
rm LuksInternalStorageKey

Regards,
Jay

Thursday, February 28, 2019

How to perform Instance Actions (Instance Stop/Start) using REST API on Oracle Cloud Infrastructure Compute Instances

Hello,

Using REST API to interact with Oracle Cloud Infrastructure is highly desirable for programmatic access to OCI resources from your applications.

For performing instance actions such as STOP, START, RESET and others as mentioned in OCI API documentation [1], you need to pass the parameters as follows rather than via json file. Somehow, I am unable to pass the parameters using json file.

You could create an empty json file as follows and use it on the REST API Call.
 
Load oci-curl function:
# . ./oci-curl

 Create an empty JSON file as we need to use POST method:

# touch empty.json

Syntax:
#oci-curl iaas.us-ashburn-1.oraclecloud.com post ./empty.json "/20160918/instances/?instanceId=&action=STOP"


Refer OCI REST API documentation [2] for more details how to generate signature and authorization strings for accessing API.
 
A copy of the oci-curl script is provided below. You need to update the following parameters in the script though:
local tenancyId="ocid1.tenancy.oc1..aaaaaaaaoumXXXnhkrc72dn6wjs27gq";
local authUserId="ocid1.user.oc1..aaaaaaaa6dmk7XXXhfshe6cacmpt2tjmq";
local keyFingerprint="b3:8f:55:XX:XX:XX:XX:86:a9:be:d4";
local privateKeyPath="/Users/muyself/.oci/oci_api_key.pem";

I am passing the parameters to the rest endpoint after "?". Multiple parameters can be passed using "&".

In the above example, I am passing the 2 required parameters "instanceId" and "action" after ? Separated using &.


Example:
#oci-curl iaas.us-ashburn-1.oraclecloud.com post ./empty.json "/20160918/instances/ocid1.instance.oc1.iad.abuwcljtttj6sgpah77XXXXXXXXXwde5fhdy6apfu5mk7svvgigxq?instanceId=ocid1.instance.oc1.iad.abuwcljtttj6sgpah77XXXXXXXXXwsvvgigxq&action=STOP"

 

Here is the oci-curl script:

# Version: 1.0.2
# Usage:
# oci-curl <host> <method> [file-to-send-as-body] <request-target> [extra-curl-args]
#
# ex:
# oci-curl iaas.us-ashburn-1.oraclecloud.com get "/20160918/instances?compartmentId=some-compartment-ocid"
# oci-curl iaas.us-ashburn-1.oraclecloud.com post ./request.json "/20160918/vcns"
# oci-curl iaas.eu-frankfurt-1.oraclecloud.com post ./empty.json "/20160918/instances/ocid1.instance.oc1.eu-frankfurt-1.antheljt2nxxxxxud53gpggoryh2k?instanceId&action=START"

function oci-curl {
# TODO: update these values to your own
local tenancyId="ocid1.tenancy.oc1..aaaaaaaaouminldguXXXX6snhkrc72dn6wjs27gq";
local authUserId="ocid1.user.oc1..aaaaaaaadmkXXXXXXX2imyg23khy4vuuv6a5aytjmq";
local keyFingerprint="b3:8f:55:5e:xx:xx:xx:xx4:86:a9:be:d4";
local privateKeyPath="/Users/myself/.oci/oci_api_key.pem";

local alg=rsa-sha256
local sigVersion="1"
local now="$(LC_ALL=C \date -u "+%a, %d %h %Y %H:%M:%S GMT")"
local host=$1
local method=$2
local extra_args
local keyId="$tenancyId/$authUserId/$keyFingerprint"

case $method in

"get" | "GET")
local target=$3
extra_args=("${@: 4}")
local curl_method="GET";
local request_method="get";
;;

"delete" | "DELETE")
local target=$3
extra_args=("${@: 4}")
local curl_method="DELETE";
local request_method="delete";
;;

"head" | "HEAD")
local target=$3
extra_args=("--head" "${@: 4}")
local curl_method="HEAD";
local request_method="head";
;;

"post" | "POST")
local body=$3
local target=$4
extra_args=("${@: 5}")
local curl_method="POST";
local request_method="post";
local content_sha256="$(openssl dgst -binary -sha256 < $body | openssl enc -e -base64)";
local content_type="application/json";
local content_length="$(wc -c < $body | xargs)";
;;

"put" | "PUT")
local body=$3
local target=$4
extra_args=("${@: 5}")
local curl_method="PUT"
local request_method="put"
local content_sha256="$(openssl dgst -binary -sha256 < $body | openssl enc -e -base64)";
local content_type="application/json";
local content_length="$(wc -c < $body | xargs)";
;;

*) echo "invalid method"; return;;
esac

# This line will url encode all special characters in the request target except "/", "?", "=", and "&", since those characters are used
# in the request target to indicate path and query string structure. If you need to encode any of "/", "?", "=", or "&", such as when
# used as part of a path value or query string key or value, you will need to do that yourself in the request target you pass in.
local escaped_target="$(echo $( rawurlencode "$target" ))"

local request_target="(request-target): $request_method $escaped_target"
local date_header="date: $now"
local host_header="host: $host"
local content_sha256_header="x-content-sha256: $content_sha256"
local content_type_header="content-type: $content_type"
local content_length_header="content-length: $content_length"
local signing_string="$request_target\n$date_header\n$host_header"
local headers="(request-target) date host"
local curl_header_args
curl_header_args=(-H "$date_header")
local body_arg
body_arg=()

if [ "$curl_method" = "PUT" -o "$curl_method" = "POST" ]; then
signing_string="$signing_string\n$content_sha256_header\n$content_type_header\n$content_length_header"
headers=$headers" x-content-sha256 content-type content-length"
curl_header_args=("${curl_header_args[@]}" -H "$content_sha256_header" -H "$content_type_header" -H "$content_length_header")
body_arg=(--data-binary @${body})
fi

local sig=$(printf '%b' "$signing_string" | \
openssl dgst -sha256 -sign $privateKeyPath | \
openssl enc -e -base64 | tr -d '\n')

curl "${extra_args[@]}" "${body_arg[@]}" -X $curl_method -sS https://${host}${escaped_target} "${curl_header_args[@]}" \
-H "Authorization: Signature version=\"$sigVersion\",keyId=\"$keyId\",algorithm=\"$alg\",headers=\"${headers}\",signature=\"$sig\""
}

# url encode all special characters except "/", "?", "=", and "&"
function rawurlencode {
local string="${1}"
local strlen=${#string}
local encoded=""
local pos c o

for (( pos=0 ; pos<strlen ; pos++ )); do
c=${string:$pos:1}
case "$c" in
[-_.~a-zA-Z0-9] | "/" | "?" | "=" | "&" ) o="${c}" ;;
* ) printf -v o '%%%02x' "'$c"
esac
encoded+="${o}"
done

echo "${encoded}"
}
 
You may copy the oci-curl script and load it to the shell. After that you should be able to use REST API methods.    
 
This is an example of how to use REST API.