Screenshot from Terminal

Linux-mint is my choice of Linux distro. I can focus on my things to do without having to care about much of device drivers and other nitty gritty stuff just to start to use the OS.

I was wondering a screen capture tool on it. I purchase SnagIt license for screen capture but I don’t think it is available on Linux. I found a nice command line tool called scrot. To install it…

sudo apt-get install scrot

If you want to select certain area by mouse, execute the following

scrot --select

Here is the example of a capture.

If you want to capture a different tab and need a bit of a delay, you can use sleep command to give yourself a bit of time before it is in a capture mode.

sleep 5;scrot --select

This means you have it wait for 5 seconds before you execute scrot command.

The screen capture files are saved in our home directory.

This tool is very impressive. It can do pretty much what you need it to do getting images for your documentations and other purposes.

checksum for files

Each file can have a finger print depending on its content. The finger print is called checksum. If 2 files have exactly the same content, those 2 files would have the same checksum. We can use md5 on Mac and md5sum on Linux.

I have a file “~/tmp/data.txt”. It’s checksum can be calculated with the following command.

md5 data.txt

Result

MD5 (data.txt) = e1e1415d2433143ff43103b384df7402

I’m going to copy the file as data2.txt and have it calculate the checksum.

To copy…

cp data.txt data2.txt

Then I’m going to pass 2 files to md5 command.

md5 data.txt data2.txt

Result

MD5 (data.txt) = e1e1415d2433143ff43103b384df7402
MD5 (data2.txt) = e1e1415d2433143ff43103b384df7402

As you can see, the hashes are exactly the same. This is really useful to see if the files are exactly the same or not.

You could implement a process to calculate the hash locally and then the files are uploaded to the production systems, you could have another process on the server side to make sure the uploaded files have the same hash.

You could alternatively combine find command and md5 command to have it calculate the selected files like the following.

find . -name 'data*' | xargs md5

Result

MD5 (./data2.txt) = e1e1415d2433143ff43103b384df7402
MD5 (./data.txt) = e1e1415d2433143ff43103b384df7402

And when the same file is copied to another system from Mac to Linux (I have a Linux-Mint machine) and execute md5sum on the file, I get exactly the same md5 hash.

How to Check the Size of Directories

If you want to see how much data a directory contains. Here is the command you can use.

du -sh dev

Result

746M dev

If you want to see the size of all the directory sizes…

du -sh *

Result

4.0K	2020-07-21-06-12-49.081-VBoxSVC-7899.log
34M	apps
241M	bin
48K	CA
175M	data
4.0K	Desktop
746M	dev
86G	Documents
4.6G	Downloads
4.0K	id_rsa
4.0K	id_rsa.pub
4.0K	Music
4.0K	Pictures
4.0K	Public
4.0K	Templates
90M	tmp
4.0K	Videos
0	VMs

Let’s take it a little further. What if you want to check the sizes of directories up to the second level? Here is the command you can use.

sudo du -h --max-depth=2

Result

8.0K ./.vim
4.0K ./.gnupg/private-keys-v1.d
8.0K ./.gnupg
4.0K ./Videos
620M ./dev/docker
4.0K ./Public
4.0K ./Templates
2.6G ./.local/share
2.6G ./.local
4.0K ./Pictures
<SNIP>
19M ./.mozilla/firefox
19M ./.mozilla
8.0K ./.hplip
4.0K ./Desktop
428K ./.mozc
7.1M ./tmp/nginx-1.18.0
34M ./tmp/sublime_text_3
50M ./tmp/docker-rootless-extras
90M ./tmp
96G .

How to Flatten OCI CLI Data

OCI CLI is a very convenient way to get your cloud resource data. Quite often, the data that gets returned from the API is nested. For example, when you get the data on your VM, the command and the data that gets returned looks like this.

oci compute instance list -c $tenancy_id --region ap-tokyo-1

* I have my instance right under the root compartment (tenancy).

Here is the data that gets returned by executing the command.

{
  "data": [
    {
      <SNIP>
      "display-name": "tokyo-proxy",
      "extended-metadata": {},
      "fault-domain": "FAULT-DOMAIN-1",
      "freeform-tags": {},    
      <SNIP>
      "lifecycle-state": "STOPPED",
      "metadata": {
        "ssh_authorized_keys": "ssh-rsa fakepublickeyhere  hiriumi@hayato-mac-mini.local\n"
      },
      "region": "ap-tokyo-1",
      "shape": "VM.Standard.E3.Flex",
      "shape-config": {
        "gpu-description": null,
        "gpus": 0,
        "local-disk-description": null,
        "local-disks": 0,
        "local-disks-total-size-in-gbs": null,
        "max-vnic-attachments": 2,
        "memory-in-gbs": 2.0,
        "networking-bandwidth-in-gbps": 1.0,
        "ocpus": 1.0,
        "processor-description": "2.25 GHz AMD EPYC\u2122 7742 (Rome)"
      },
      "source-details": {
        "boot-volume-size-in-gbs": null,
        "image-id": "ocid1.image.oc1.ap-tokyo-1.aaaaaaaavnulbem7sdol6pzlqadzwrcej2yyg3negvbz7ullnsoaspluzydq",
        "kms-key-id": null,
        "source-type": "image"
      },
      "system-tags": {},
      "time-created": "2020-11-14T07:29:26.033000+00:00",
      "time-maintenance-reboot-due": null
    }
  ]
}

Notice that the highlighted lines are the nested data. To limit the data to a few data points, you could do something like this.

oci compute instance list --region ap-tokyo-1 -c $tenancy_id \
--query 'data[]'.{'name:"display-name",shape_config:"shape-config"'} --output table

By adding –query option, you can limit the data and by adding –output table, you can format the data in an easy-to-see format but the output does not look good because the shape-config node has child nodes.

------------------------------------------------------------------------------------------+
| name        | shape_config                                                                                                                                                                                                                                                                                 |
+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+
| tokyo-proxy | {'ocpus': 1.0, 'memory-in-gbs': 2.0, 'processor-description': '2.25 GHz AMD EPYC™ 7742 (Rome)', 'networking-bandwidth-in-gbps': 1.0, 'max-vnic-attachments': 2, 'gpus': 0, 'gpu-description': None, 'local-disks': 0, 'local-disks-total-size-in-gbs': None, 'local-disk-description': None} |
+-------------+----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+

By executing the following command, we can flatten the child nodes and limit the data to display even further.

oci compute instance list -c $tenancy_id --region ap-tokyo-1 \
--query 'data[]'.{'name:"display-name",cores:"shape-config".ocpus,memory:"shape-config"."memory-in-gbs"'} --output table

Here is the sample output.

+-------+--------+-------------+
| cores | memory | name        |
+-------+--------+-------------+
| 1.0   | 2.0    | tokyo-proxy |
+-------+--------+-------------+

It took me a while to figure this out so I thought I’d share it here in case someone is looking for a way to flatten the nested data using OCI CLI.

What I Wish I Had Known about Terraform When I Started

I feel extremely lucky that I am working for the company that I am working for. It’s a job and there are things I don’t like but I am doing what I have wanted to do. I think Terraform is an important technology to manage cloud resources, so I am listing the things I wish I had known

  1. Terraform is a compiled go-lang executable.
  2. Terraform communicates with Terraform state file. It reads from and writes to the state file.
  3. Terraform communicates with cloud through provider.
  4. Terraform state file can be stored locally or on Object Storage.
  5. Terraform can import existing cloud resources to the state file.

The list can go on. I will add more when they pop in my mind.

How to Check Latency using curl

I’ve learned how to check latency using curl today.

curl -w "\n time_namelookup:  %{time_namelookup}\n \
time_connect:  %{time_connect}\n \
time_appconnect:  %{time_appconnect}\n \
time_pretransfer:  %{time_pretransfer}\n \
time_redirect:  %{time_redirect}\n \
time_starttransfer:  %{time_starttransfer}\n \
----------\n time_total:  %{time_total}\n\n" \
"https://www.google.com/"

Here is the sample result.

<SNIP HTML>
 time_namelookup:  0.022736
 time_connect:  0.040636
 time_appconnect:  0.075071
 time_pretransfer:  0.075225
 time_redirect:  0.000000
 time_starttransfer:  0.136858
 ----------
 time_total:  0.142189

Is it DNS? Connecting to the end point? Redirect? Starting the first byte transfer? This is a very good way to “guess” where the source of the latency is.

Starting with Jenkins CLI

I previously wrote How to Install Jenkins Slave as Windows Service in this blog. It has been one of the most accessed articles on this site. Though the article worked for people who visited here, I thought of taking it to the next level. What if I come up with a way to easily install Jenkins Slave as Windows Service by running scripts? It would save so much time and effort without mistakes.

Before working on the whole script, I want to make sure Jenkins CLI works. Jenkins CLI is different from REST API of Jenkins and it needs some preliminary preparation.

Jenkins CLI

Jenkins CLI is available from Manage Jenkins -> Tools and Actions -> Jenkins CLI.

When I click Jenkins CLI, there is a list of commands available.

I’m going to try to see if help works for sanity check. Before running the command java -jar jenkins-cli.jar -s https://jenkins.linux-mint.local/ help , make sure to install Java and download jenkins-cli.jar from the Jenkins CLI page. When I ran it, I get the following error.

javax.net.ssl.SSLHandshakeException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.ssl.Alert.createSSLException(Alert.java:131)
<SNIP>
at hudson.cli.FullDuplexHttpStream.(FullDuplexHttpStream.java:73)
at hudson.cli.CLI.plainHttpConnection(CLI.java:361)
at hudson.cli.CLI._main(CLI.java:299)
at hudson.cli.CLI.main(CLI.java:96)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:439)
<SNIP>
… 20 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at java.base/sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at java.base/sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.base/java.security.cert.CertPathBuilder.build(CertPathBuilder.java:297)
at java.base/sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:434)
… 25 more

This is because the SSL cert of the Jenkins master server is not trusted by Java. Let’s download the cert and get Java to trust it.

Download SSL Cert

Using openssl, we will download the SSL certificate as a file. Execute the following command.

openssl s_client -showcerts -connect jenkins.linux-mint.local:443 < /dev/null | openssl x509 -outform DER > jenkins.linux-mint.local.cer

Don’t mind some seemingly error message. Now you get a file jenkins.linux-mint.cer.

Trust the Cert

When you have Java on your system, you have a file called cacerts. Basically, you import the SSL cert you just downloaded into the cacerts file. Where is the file? Let’s find out. Execute the following command to locate cacerts.

sudo find /Library/Java -name cacerts

My system right now is a Mac and I happen to have the file at the following location.

/Library/Java/JavaVirtualMachines/jdk-14.0.2.jdk/Contents/Home/lib/security/cacerts

Execute the following command to import the SSL cert into cacerts. You wil be prompted if you really want to import it and type yes.

sudo keytool -import -v -trustcacerts -alias jenkins -file jenkins.linux-mint.local.cer -keystore /Library/Java/JavaVirtualMachines/jdk-14.0.2.jdk/Contents/Home/lib/security/cacerts -keypass changeit -storepass changeit

To check if it has been imported successfully, execute the following command. Enter the default password changeit if you haven’t changed.

keytool -list -keystore /Library/Java/JavaVirtualMachines/jdk-14.0.2.jdk/Contents/Home/lib/security/cacerts -alias jenkins

If you want to remove the certificate, you can execute the following command. (Do not execute it if you want to avoid the error I talked about earlier.)

keytool -delete -alias jenkins -keystore /Library/Java/JavaVirtualMachines/jdk-14.0.2.jdk/Contents/Home/lib/security/cacerts

Try Jenkins CLI

First, you need to generate API token for your user. Follow the steps below.

  1. Login to Jenkins master.
  2. Click on your username around the upper right corner.
  3. Click Configure.
  4. Click Add new Token button.
  5. Copy the generated token in clipboard.

Now try to execute the following command.

java -jar jenkins-cli.jar -s https://jenkins.linux-mint.local/ -auth [Your User]:[Your Token] help

Now you don’t get the error and you will see the list of available commands.

  add-job-to-view
    Adds jobs to view.
  build
    Builds a job, and optionally waits until its completion.
  cancel-quiet-down
    Cancel the effect of the "quiet-down" command.
   Resume using a node for performing builds, to cancel out the earlier "offline-node" command.
<SNIP>
  wait-node-online
    Wait for a node to become online.
  who-am-i
    Reports your credential and permissions.

Recap

I personally like using REST API of Jenkins better than Jenkins CLI but what I am planning to do may require Jenkins CLI. SSL protected Jenkins makes it harder to deal with it via its API but this makes it possible. Remember Java has its own keystore separate from the OS where it resides.

How to Merge JSON Array

I struggled to find out a way to merge JSON arrays, so to save you time, I will show you an example here.

jq

I’m sure there are several ways to accomplish it with programming languages, I will use jq. Just run brew install jq in your terminal if you use Mac.

Sample Data

fruits1.json

{
    "data":[
      {"name":"banana"},
      {"name":"strawberry"},
      {"name":"orange"}
    ]
}

fruits2.json

{
    "data":[
      {"name":"cherry"},
      {"name":"blueberry"},
      {"name":"apple"}
    ]
}

Merging

Execute the following jq command to merge the 2 arrays in 2 different JSON data.

 jq -s '{ data: map(.data[]) }' fruits1.json fruits2.json

Here is the result.

{
  "data": [
    {
      "name": "banana"
    },
    {
      "name": "strawberry"
    },
    {
      "name": "orange"
    },
    {
      "name": "cherry"
    },
    {
      "name": "blueberry"
    },
    {
      "name": "apple"
    }
  ]
}

If you just want a compact version of the same JSON data.

jq -sc '{ data: map(.data[]) }' fruits1.json fruits2.json

Here is the result.

{"data":[{"name":"banana"},{"name":"strawberry"},{"name":"orange"},{"name":"cherry"},{"name":"blueberry"},{"name":"apple"}]}

Though I don’t like jq, I had to accomplish it in a bash script I was working on. I could not contain my joy when I got it. 🙂

How to Create Private SSL Certificate Authority

You usually need to purchase SSL certificate from companies for your public websites that you host. However, it is not necessary to purchase SSL certificates if the websites (any HTTP based service) are privately hosted never exposed to the outside world. You can be your own Certificate Authority.

SSL Certificate Generation Flow

Here is the diagram that shows the SSL certificate generation flow.

Why HTTPS (SSL) Internally?

Here are some reasons I can think of.

  • Malicious attacks can come from internal engineers not just from outside.
  • Dev/test/staging environments are better to be tested mirroring the production environment as much as possible.
  • It is easier to promote the dev/test/staging environments to production by mirroring the production environment meaning less moving parts between different environments.
  • It has been more of a trend in the network security world that encryption must be done from end to end.

How to Create CA Root Certificate

CA stands for Certificate Authority. You can be a small Certificate Authority. Here are the steps.

  • Open terminal. (I use Linux Mint as my Linux machine but other distribution should be OK too.)
  • Create a directory.
    mkdir CA
  • Navigate to the directory.
    cd CA
  • Generate the private key for your CA. Usually 2048 will suffice but we will use 4096 in this example to make it stronger.
    openssl genrsa -des3 -out CA.key 4096
  • Enter pass phrase when prompted. You can choose whatever you want but do not forget it because you will use it later.
  • You now have CA.key (private key) file in ~/CA directory.
  • Out of the private key, we will create the root certificate using the command below.
    openssl req -x509 -new -nodes -key CA.key -sha256 -days 3650 -out CA.pem
  • You will be prompted to enter some information about your root certificate.
    Country Name (2 letter code) [AU]:US
    State or Province Name (full name) [Some-State]:WA
    Locality Name (eg, city) []:Seattle
    Organization Name (eg, company) [Internet Widgits Pty Ltd]:Hayato Iriumi
    Organizational Unit Name (eg, section) []:
    Common Name (e.g. server FQDN or YOUR name) []:hayato-iriumi.net
    mail Address []:hiriumi@gmail.com
  • Now you have the root certificate CA.pem under ~/CA directory.

Download CA Root Certificate

If you have the certificate on a remote machine, you need to download it to a client machine to install it. Follow the steps below.

  • Open your terminal on a client machine.
  • Navigate to the directory where you want to download the CA.pem file.
  • Use scp to download the CA.pem file.
    scp amaterasu48@linux-mint:~/CA/CA.pem .

How to Install Root Certificate (on client side)

  • If you are on macOS, you can execute the command to trust the root certificate.
    sudo security add-trusted-cert -d -r trustRoot -k "/Library/Keychains/System.keychain" CA.pem
  • Please refer to this document to install root certificate on Windows.

Create SSL Certificate Request

Now we need to generate CA key signed certificate for your web servers but first, we need to generate CSR (Certificate Signing Request). Please follow the steps below.

  • Create another private key. Replace linux-mint.local with whatever the web server you want to enable SSL for.
    openssl genrsa -out linux-mint.local.key 4096
  • Now generate the CSR file (linux-mint.local.csr).
    openssl req -new -key linux-mint.local.key -out linux-mint.local.csr
  • Enter information when prompted.
    Country Name (2 letter code) []:US
    State or Province Name (full name) []:WA
    Locality Name (eg, city) []:Covington
    Organization Name (eg, company) []:Hayato Iriumi
    Organizational Unit Name (eg, section) []:dev
    Common Name (eg, fully qualified host name) []:linux-mint.local
    Email Address []:hiriumi@gmail.com
    Please enter the following 'extra' attributes
    to be sent with your certificate request
    A challenge password []:YourPassword

Create SSL Certificate

We will need CA private key, CA certificate and CSR file to generate an SSL certificate. Please follow the steps below.

  • Upload the CSR file from the host where you created it to the CA.
    scp yourdomain.csr yourusername@yourhostname:~/CA
  • There are CA.key, CA.pem and linux-mint.local.csr in ~/CA directory on the host where CA key was generated.
  • Create linux-mint.local.ext file for configuration with the following content on the remote host at ~/CA.
    authorityKeyIdentifier=keyid,issuer
    basicConstraints=CA:FALSE
    keyUsage = digitalSignature, nonRepudiation, keyEncipherment, dataEncipherment
    subjectAltName = @alt_names
    [alt_names]
    DNS.1 = linux-mint.local
  • At this point, I have CA.key, CA.pem, linux-mint.local.csr and linux-mint.local.ext.
  • Execute the following command.
    openssl x509 -req -in linux-mint.local.csr -CA CA.pem -CAkey CA.key -CAcreateserial -out linux-mint.local.crt -days 365 -sha256 -extfile linux-mint.local.ext
  • It has generated linux-mint.local.crt which will be used in your web server.

Implementing SSL Certificate on Web Server

There are many types of web servers. I will pick one of the most popular NGINX as an example to implement the SSL certificate that was generated in this process. Obviously, the prerequisite is that you already have NGINX up and running. There are a lot of documents out there on how to install and run NGINX.

Before you implement the SSL certficate, make sure the port 443 is open to your web server. (ufw for Debian descendent distros and firewall-cmd for RedHat descendent distros).

  • Create a directory /etc/nginx/conf.d/ssl
    sudo mkdir /etc/nginx/conf.d/ssl
  • Copy linux-mint.local.key (the original private key that generated the CSR file) and linux-mint.local.crt (the SSL certificate file generated in the previous section) to /etc/nginx/conf.d/ssl on your NGINX server.
  • Create file /etc/nginx/conf.d/ssl.conf with the following content. The first server block allows NGINX to redirect the traffic that comes to the port 80 to port 443 (HTTPS). The second server block actually takes care of the SSL part.
    server {
    listen 80;
    server_name linux-mint.local;
    return 301 https://linux-mint.local;
    }
    server {
    listen 443 ssl;
    server_name linux-mint.local;
    ssl_certificate /etc/nginx/conf.d/ssl/linux-mint.local.crt;
    ssl_certificate_key /etc/nginx/conf.d/ssl/linux-mint.local.key;
    }
  • Restart NGINX.
    sudo systemctl restart nginx

Accessing the SSL Enabled Web Server

Back on your client machine, make sure you can resolve whatever the address can resolve to the IP address. I will talk through my case in my private environment.

  • Edit your /etc/hosts (or C:\Windows\System32\drivers\etc\hosts on Windows) and add an entry in the file just to simulate DNS server. You can alternatively have your internal DNS server but I will omit it. The following line means if the machine tries to access linux-mint.local, it skips the whole DNS query but just resolves it to 192.168.1.22 which is the IP address of the NGINX I already setup.
    192.168.1.22 linux-mint.local
  • Open your browser and enter linux-mint.local and hit enter.
  • Now you can see the website if protected by SSL. If you click on the key icon, you get to see the certificate you created.

Recap

In this blog article, I’ve explained how to create your own CA certificate to protect internal HTTP traffic for free.