Bashing bash – variable substitution

This is the first post in the “bashing bash” series to highlight problems in bash and convince systems and software engineers to help building a better alternative.

Bash is a very powerful and useful tool, doing a better job than many other shells and programming languages when used for the intended tasks. Still, it’s hard to believe that writing a software decades later can not be done better.

The problem

What does the following command do?

cp $src_file $dst_file

One might think it copies the given file to the specified destination. Looking at the code we can say it was the intention. What would actually happen? It can not be known from the line above. Each $src_file and $dst_file expand to zero to N arguments so unexpected things that could happen. The correct command would be

cp "$src_file" "$dst_file"

Forgetting the quotes or leaving them out assuming  $src_file and $dst_file will always contain one bash word, expanding to exactly one argument each is dangerous.

Keeping quoting everything makes code cluttered.

The suggested solution

In NGS, $var expands to exactly one argument similar to "$var" in bash. The new syntax $*var, consistent with similar syntax in other NGS parts, would expand to zero to N arguments.

Please help building a better alternative

Go to https://github.com/ilyash/ngs/ and contribute some code.

Israeli Banks Web Security Mini Survey – 2016

security-265130_640

Intro

I have used Qualsys HTTPS checker tool to survey Israeli banks and a few reference sites. Main points summarized in the table below.

I did no “hacking” nor “cracking” nor break-in attempts.

I am not a security specialist. I just have some basic understanding of security.

List of banks is from Banking in Israel article on Wikipedia.

Comparison points

  1. SSL3 – insecure, old protocol, should not be used since June 2015
  2. RC4 – unsupported by recent versions of major browsers since January 2016 because it’s considered to be an insecure protocol. Deprecation started in 2015.
  3. SHA256 certificate – as opposed to deprecated SHA1 certificate.
  4. TLS 1.2 – The recommended version of TLS, invented in 2008, plenty of time to implement, one would think… The most important in my opinion (and Qualsys’ too, according to ratings).
  5. The forward secrecy supporting protocols protects your current sessions, which are probably recorded by NSA and others, from being decrypted later, when the server is compromised. A site gets “yes” if there are some protocols one could use to connect to the site that support the forward secrecy feature.
  6. Qualsys overall rating

Note that presence of SSL3 or RC4 is not a problem for up-to-date browsers as they just will not use it. It enables insecure connections for older browsers (in some cases the alternative would be no connection at all).

Results

Web Site SSL3 (bad) RC4 (bad) SHA256 certificate TLS 1.2 Forward secrecy
Qualsys rating
Hapoalim (login.bankhapoalim.co.il) no no yes no no C
Leumi (hb2.bankleumi.co.il) no no yes no no C
Discount (start.telebank.co.il) no no yes yes no A-
Mizrahi Tfahot (www.mizrahi-tefahot.co.il) no no yes yes partial A-
First International Bank of Israel (online.fibi.co.il) no yes no yes no C
Gmail (mail.google.com) yes yes no yes yes B
Yahoo mail (uk-mg42.mail.yahoo.com) no no yes yes yes A
Facebook (www.facebook.com) no yes yes yes yes B
Bank of America (secure.bankofamerica.com) no no yes yes no A-

Opinion / Rant

Banks that do not support TLS 1.2 should close the web site, heads of security along with their bosses should do Seppuku and the banks should be closed. Do you think that banking information security is less important than emails or Facebook? Maybe it’s “duopoly of Hapoalim and Leumi” manifestation?

Banks that do not support forward secrecy – it’s about damn time!

When one of my clients asked me to improve HTTPS security (when it became important), it went from C to A in about half a day of work for several Nginx and ELB endpoints. Yes, a bank has more complex security and more variety in types of clients but it also has a security team, not one part-time operations guy. The security situation is outrageous.

Most JQ you will ever need

I’m using jq (think sed/awk/grep for JSON) for a while now. From time to time, people ask me how to do this or that using jq. Most of the questions can be distilled into these two cases:

Show tabular data

Given array of objects, show it in a tabular, human readable format.

Sample jq input: output of aws cloudformation describe-stacks:

{
    "Stacks": [
        {
            "DisableRollback": false,
            "StackStatus": "CREATE_COMPLETE",
            "Tags": [],
            "CreationTime": "2016-05-10T14:16:06.573Z",
            "StackName": "CENSORED",
            "Description": "CENSORED",
            "NotificationARNs": [],
            "StackId": "arn:aws:cloudformation:CENSORED",
            "Parameters": [
                ...
            ]
        },
        {
            ...
        },
    ...
}

To show this in a human-readable form you could pipe it to

jq -r '.Stacks[] | "\(.StackName) \(.StackStatus) \(.CreationTime) \(.Tags)"'

Explanation:

  1. -r make the output “raw”. Such output does not include enclosing quotes for each of the strings we are generating
  2. .Stacks[] | – for each element in the Stacks array evaluate and output the expression that follows the vertical bar (inexact explanation but it fits this case). When evaluating the expression to the right, the context will be set to one element of the array at a time.
  3. "..." – a string
  4. Inside the string, \(.StackName) – The .StackName attribute of current element from the Stacks array.

The output columns will not be visually aligned. I suggest solving the alignment issue by introducing special columns separator character such as % and the using the columns command to visually align the columns. Full solution suggestion:

aws cloudformation describe-stacks | \
jq -r '.Stacks[] | "\(.StackName)%\(.StackStatus)%\(.CreationTime)%\(.Tags)"' | \
column -t -s '%'

Note: I don’t have a good solution for the case when more than one or two tags are present. The output will not look very good.

More AWS-specific JSON formatting is in my .bashrc_aws.

Do something with each object

Given array of objects, iterate over it accessing fields in each array element.

Continuing the example above, assume you want to process each stack somehow.

aws cloudformation describe-stacks | \
jq -rc '.Stacks[]' | while IFS='' read stack;do
    name=$(echo "$stack" | jq .StackName)
    status=$(echo "$stack" | jq .StackStatus)
    echo "+ $name ($status)"
    # do your processing here
done

Explanation:

The only new thing here is the -c switch, for “compact”. This causes each resulting JSON representing a stack to be output on one line instead of few lines.

UPDATE: AWS tags handling

I was dealing with instances which had several tags and it was very annoying so using jq man and Google I’ve arrived to this:

aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | "\(.InstanceId)%\(if .Tags and ([.Tags[] | select ( .Key == "Name" )] != []) then .Tags[] | select ( .Key == "Name" ) | .Value else "-" end)%\(.KeyName)%\(if .PublicIpAddress then .PublicIpAddress else "-" end)%\(.LaunchTime)%\(.InstanceType)%\(.State.Name)%\(if .Tags then [.Tags[] | select( .Key != "Name") |"\(.Key)=\(.Value)"] | join(",") else "-" end)"' | sed 's/.000Z//g' | column -t -s '%'

Explanation of the new parts:

  1. if COND_EXPR then VAL1 else VAL2 end construct which returns VAL1 or VAL2 depending on whether COND_EXPR evaluates to true or false. Works as expected.
  2. Why if .Tags is needed? I want to display something (-) if there are no tags to keep the columns aligned. If you didn’t want do display anything special there… you just had to have this if .Tags anyway. Why? Thank AWS for the convoluted logic around tags! OK, you made tags (which should be a key-value map) a list but then if the list is empty it’s suddenly not a list anymore, it’s a null! I guess this comes from Java developers trying to save some memory… and causing great suffer for the users. The .Tags[] expression fails if .Tags is null.
  3. [...] builds an array
  4. select(COND_EXPR) filters the elements so that only elements for which the COND_EXPR evaluates to true are present in it’s output.
  5. join("STR") – predictably joins the elements of the array using the given separator STR.

I think that any instances I have that have tags, have the Name tag. I guess you would need to adjust the code above if it’s not the case on your side. If I will have this problem and a fix, I’ll post an update here.

Handling large or complex JSONs

When your JSON is large, sometimes it’s difficult to understand it’s structure. The tool show-struct will show which jq paths exist in the given JSON and summarize which data is present at these paths.

For the example above, the output of aws cloudformation describe-stacks | show_struct.py - is

.Stacks -- (Array of 10 elements)
.Stacks[]
.Stacks[].CreationTime -- 2016-04-18T08:55:34.734Z .. 2016-05-10T14:16:06.573Z (10 unique values)
.Stacks[].Description -- CENSORED1 .. CENSORED2 (7 unique values)
.Stacks[].DisableRollback -- False
.Stacks[].LastUpdatedTime -- 2016-04-24T14:08:22.559Z .. 2016-05-10T10:09:08.779Z (7 unique values)
.Stacks[].NotificationARNs -- (Array of 0 elements)
.Stacks[].Outputs -- (Array of 1 elements)
.Stacks[].Outputs[]
.Stacks[].Outputs[].Description -- URL of the primary instance
.Stacks[].Outputs[].OutputKey -- CENSORED3 .. CENSORED4 (2 unique values)
.Stacks[].Outputs[].OutputValue -- CENSORED5 .. CENSORED6 (2 unique values)
.Stacks[].Parameters -- (Array of 3 elements) .. (Array of 5 elements) (2 unique values)
.Stacks[].Parameters[]
.Stacks[].Parameters[].ParameterKey -- AMI .. VpcId (11 unique values)
.Stacks[].Parameters[].ParameterValue --  .. CENSORED7 (13 unique values)
.Stacks[].StackId -- arn:aws:cloudformation:CENSORED8
.Stacks[].StackName -- CENSORED9 .. CENSORED10 (10 unique values)
.Stacks[].StackStatus -- CREATE_COMPLETE .. UPDATE_COMPLETE (2 unique values)
.Stacks[].Tags -- (Array of 0 elements)

Extras from a friend

AMIs with joined EBS snapshots list and tags

Demonstrates AWS tags values and join for normalization.
Code:

aws ec2 describe-images --owner self --output json | jq -r '.Images[] | "\(if .Tags and ([.Tags[] | select ( .Key == "origin" )] != []) then .Tags[] | select ( .Key == "origin" ) | .Value else "-" end)%\(.ImageId)%\(.Name)%\(if .Tags and ([.Tags[] | select ( .Key == "stamp" )] != []) then .Tags[] | select ( .Key == "stamp" ) | .Value else "-" end)%\(.State)%\(.BlockDeviceMappings | map(select(.Ebs.SnapshotId).Ebs.SnapshotId) | join(",") | if .=="" then "-" else . end)"' | (echo "Source%AMI%Desc%Stamp%State%Snaps"; cat;) | column -s % -t

Output:

Source   AMI           Desc                    Stamp       State      Snaps
serv001  ami-11111111  serv001_20171031054504  1509428704  available  snap-1111111111111111
-        ami-22222222  prd-db002-20170911      -           available  snap-2222222222222222
app333   ami-33333333  app333_20171031054504   1509428704  available  snap-4444444444444444,snap-7777777777777777

Load-balancers with de-normalized instances

Demonstrates vars for de-normalization

Code:

aws elb describe-load-balancers --output json | jq -r '.LoadBalancerDescriptions[] | . as $l | .Instances[] as $i | [$l.LoadBalancerName] + [$i.InstanceId] | @csv' | sed 's#"##g; s#,#\t#g;' | (echo -e "LB\tInstance"; cat;) | column -t

Output:

LB            Instance
wordpress-lb  i-11111111
app-lb        i-11111111
webapp-prd    i-33333333
webapp-prd    i-99999999

External links

  1. StackOverflow – How to convert arbirtrary simple JSON to CSV using jq?

Hope this helps.

Please let me know in comments if there is some another common use case that should be added here.

AWS CLI inhuman output and what I suggest

The problem

If you use AWS you probably use AWS CLI. This tool gives you control over your resources from the command line. AWS CLI can output the results of your commands in several formats. None of the formats seem to be useful for humans.

AWS CLI can output the data in the following formats:

  1. JSON, the default – good for processing with jq or programming languages, definitely not good for a human looking at list of few or more instances.
  2. text – good for processing with command line tools such as awk, very hard to read for a human.
  3. table – WTF? Probably meant for humans. Cluttered and difficult for a human to process. See the screenshot:
awscli-din
AWS CLI. List of instances. –output table. Is it for humans?

You can make it look better but what you see above is in no way reasonable default and making it look prettier should not be as unpleasing as described in the user guide towards the end of the page.

My takes on solving this

Take 1 – old script

I have a script which uses outdated Ruby AWS client which does not work with eu-central regions because it’s old. It was originally built for EC2 classic so it fails to show VPC security groups.

oldscript

If newer regions and VPC security groups issues are solved this is a fine tool for human output.

Take 2 – JQ

While the old script is not fixes I have this temporary solution: jq transformation of AWS CLI output JSON (from my .bashrc_aws). The output is one line per instance.

DESC () 
{ 
    aws ec2 describe-instances | jq -r '.Reservations[].Instances[] | "\(.InstanceId)%\(.KeyName)%\(.PublicIpAddress)%\(.LaunchTime)%\(.InstanceType)%\(.State.Name)%\(.Tags)"' | sed 's/.000Z//g' | column -t -s '%'
}

It works well when there are no tags or small amount of tags per instance. With few tags on an instance, the line becomes too long and wraps. Reformatting sample output here for your convenience. Original is one logical line, wrapped, takes almost two lines in the terminal window.

i-XXXXXXXX  KEY_NAME  52.XXXXXXXXXX   INSTANCE_START_TIME  t2.small
running
[{"Key":"rollback-who","Value":"new"},
 {"Key":"Name","Value":"XXXXXXXXXX/132-1-7"},
 {"Key":"creator","Value":"deploy"},
 {"Key":"env","Value":"prod"},
 {"Key":"role","Value":"XXXXXXXXXX"},
 {"Key":"rollback-id","Value":"XXXXXXXXXXXXXXXXXXXXXXXX"},
 {"Key":"status","Value":"Live"}]

Take 3 – ec2din.ngs

This is one of the demo scripts in the NGS project that I’m working on.

The column after state (“running”) is the key name.

ec2din-ngs
ec2din.ngs – human output

It would be better if actual available screen width would be considered so there would be no need to arbitrary trim security groups’ names and tags’ keys and values (the ... in the screenshot above).

How I think it should be solved

NGS (the new shell I’m working on) implements the Table type which is used to do the layout you see on “ec2din.ngs – human output” screenshot. Table is part of the standard library in NGS. As you see, it’s already useful in it’s current state. There is plenty of room for improvement and the plan is to continue improving Table beyond simply “useful”.

This is the right direction with the following advantages:

  1. The script that needs to output a table (for example ec2din.ngs) does not deal with it’s layout at all, making it smaller and more focused on the data.
  2. The script can set defaults but does not handle which columns are actually displayed and in which order. The Table type handles it using another NGS facility: config which currently gets the configuration from environment variables but should be able to read it from files and maybe other sources in future.
  3. Output of tabular data is a very common task so factoring it out to a library has all the advantages of code deduplication.

I would also like the shell to automatically detect heuristically or otherwise which data comes out of the programs that you run and display accordingly. Most of the data that is manipulated in shells is tabular and could be displayed appropriately, roughly as ec2din.ngs displays it. Maybe ec2din.ngs will become unnecessary one day.

In my dream, the output is semantic so that the shell knows what’s on the screen and allows you to navigate for example to i-xxxxx part, press a key and choose which operation you would like to perform on the instance. When you pick an operation, the shell constructs a command for such operation and executes it exactly as if you typed it in (so it’s in history and all other CLI advantages over GUI apply).

Common Sense Movement – where is it?

brain-605603_960_720

Let’s look at systems and software engineering.

We hear about all kinds of “cool” methodologies, ideas, frameworks: DevOps, Scrum, Agile, Kanban, Continuous Integration, Continuous Delivery, Puppet, Chef, Ansible … and combinations of these.

Why do you hear about all this cool stuff all the time? Why there are hypes all the time? Simple: money. Another hype, another opportunity to sell you something [at higher price]: products, support, certifications, guidance. You don’t necessarily need these things but they definitely would like to sell you all of it.

I’m starting Common Sense Movement

This humorous thought crossed my mind yesterday. You know, just to balance the situation a bit. Then I realized why don’t you hear much about the shiny cool “common sense” methodology: money.

You can’t sell a “common sense” certificate. That’s because if someone really has some common sense, he/she is not going to pay you for this crap and the only certificate you can sell would be “Failed Common Sense Practitioner”. Actually I doubt you can  hardly sell any crap to common sense practitioners. That’s a problem. So, there is no Common Sense Movement for you 😦

I really would like the hype waves to stop so we could just do our work in peace. Meanwhile, when you are approached by a client or a boss with yet another “We need tool X” or “We need technology Y” you should really answer with “Prove that you need it and it’s optimal usage for our money and time considering other alternatives” no matter how new,shiny or cool that thing is.

Prove your tool is the right choice

I see two common approaches at work when it comes to choosing tools.

Photo of different tools
How do you choose?

Bad

I have heard about this new shiny cool framework/library/tool, let’s use it!

It might be a good approach when you want to learn something in your spare time, just don’t do this at work.

Good

We have this problem and found a few alternative solutions (frameworks/libraries/tools) that might solve it. We will investigate and see if any of them fit our needs.

Note that depending on your situation, it might be the case when none of the existing tools will be a good fit for your project.

It might be tempting to look at existing solutions in a positive light thinking “our problem is not unique, there are tools solving it already”. The problem might appear to be similar to the problems that these tools solve but remember to look and focus and what’s unique to your circumstances.

Suggested approach

You should prove that the points below are wrong.  Assume that the tool you are evaluating:

  1. Does not cover your needs.
  2. Has crappy/new/unstable code.
  3. Has no community so getting answers to your questions or help is a nightmare.
  4. Has none or very costly official support.
  5. It will have a steep learning curve.
  6. It will be harder to maintain than any other alternative or your own code.
  7. You will need to modify the tool and it will be tough.
  8. Provides so little that it’s not worth another external dependency.
  9. Will cause lock-in with very high switching costs. (You can affect this one to some degree with your design).
  10. Has big company behind it which
    1. had huge investment and only cares about things like ROI and market share and not you, even if they did at some point in the past.
    2. has more interesting markets than you and optimizes the product for these markets, making it not a good fit for your situation.
    3. generates hype making it all look rosy even when the product has grave flaws.
    4. makes it look like everybody uses this tool.
    5. makes it feel like the next best thing.
  11. Has a big alliance behind it with many interests of different partners with zero intersection with your interests.
  12. Most places that use this tool suffer immensely but either don’t talk about it or you don’t hear that for other reasons. Dig deeper to find such information.

Calling some friends and asking them about the tool is very good but note that your situation is different so most of the points listed above still stand.

I guess that when you start positively when evaluating a tool, you might fall into the confirmation bias trap. Beware!

Winning executives team

0df763b

Brought to you by most trendy positions. You may recognize some of them at your work. Say hello!

  1. VP Downtime
  2. VP Broken Builds
  3. VP Lost Leads
  4. VP Ugly Sites
  5. VP Mediocrity and Disempowering
  6. VP Over-Complication Because it’s Cool
  7. VP of Sales Prevention
  8. VP of Successful Penetration
  9. VP Wrong Solutions
  10. Project Failure Architect
  11. Chief Titles Manager
  12. Chief Hype Evangelist
  13. Chief External Dependencies Promoter
  14. Chief Next Trendy Language Adopter
  15. Chief Instances Killer
  16. Chief Budget Overrun Manager
  17. Chief Miscommunication Advocate
  18. Chief Technical Debt Accumulation Promoter
  19. New shiny technologies Team Leader
  20. Misused Frameworks Manager

Some of the entries were contributed by VPs from beame.io

You are very welcome to extend this list in your comments.

Don’t call me DevOps

… unless you are ready to clarify what exactly do you mean by “DevOps”, I suggest you shut up don’t use that word at all.

The reason not to call a person “DevOps” is that DevOps seems to be a set of principles but not a person nor a job position. None of the amorphous definitions say it’s a person or position so don’t call DevOps anyone please.

Here is why I don’t recommend using this term at all:

  1. There is no exact definition. I’ve asked several people from in the industry and none of them could define the term precisely.
  2. Some people think that using specific tools is being DevOps. Some don’t. I don’t. Specific tools can not be used all the time as situations are different.
  3. Wikipedia has a description which I’m not sure can be used as a definition because some people would agree and some won’t.
  4. New Relic guys write “First, let’s just say there is no definitive answer.” in the article What is DevOps?.
  5. Automation seems to be one of the principles. Does it mean that an unsuspecting person that automates processes is now suddenly practicing DevOps? Are we just naming existing practices with a new fancy term now?

OpenStack is not ready

If you read the news you get the impression that OpenStack is the answer to anything (along with Docker but that’s for another post). I would like to balance this hype a bit. This post is my personal opinion.

tl;dr – TCO much higher than expected

When I first started working with OpenStack few years ago numerous issues caused me to mentally summarize “Not ready for production, re-visit in a year or two”. Unfortunately this still stands in 2016.

If you are considering OpenStack for your private cloud, think twice. Here are some examples of what I experienced few years back till now with two different clients that use OpenStack.

  1. Using API is a suffer
    1. API is inconsistent between components like Nova and Neutron so you will have more code.
    2. Neutron documentation for Python client is ridiculous: create network and list networks. That’s all. This is in contrast to the counterpart Nova documentation which is OK.
  2. Few years ago I have edited instance types (“flavors”) using the web UI. Somehow it screwed up all the types. Unfortunately I don’t know if there was an open issue for that.
  3. About a year ago we had production system workaround with a line of bash that would do internal ping within the message bus that OpenStack uses. Without this it would randomly get stuck and not respond to any management commands (create/delete instance for example). If I remember correctly this bug was fixed in Juno or Kilo, unfortunately I can’t find it.

Be sure to visit the bugs lists before your decision.

The Go language TL;DR review

I took a few hours to overview the Go language to make up my mind. Posting here because someone else might also find this useful.

Please note that I just reviewed the language. I did not write big chunks of code so my experience is fairly limited.

  • Compiled.
  • Easy to use almost as any high-level interpreted language, this is not a fiction, works as advertised. Simplicity and ease of use – goal checked.
  • Good libraries (HTTP client+server, FastCGI server, JSON, RPC, …)
  • VIM support in the code repo (syntax, indentation, …)
  • bash completion support in the code repo
  • The language as a whole makes the impression that the right choices were made.
  • It’s pragmatic.
  • It’s minimalistic for the capabilities it provides.
  • The only “big” innovation I see (besides the ease of use) is simplifying interfaces in a statically typed language. All other features resemble things I’ve seen somewhere else already. Actually “familiarity” of the language was one of the goals.
  • Has low level stuff, see http://golang.org/pkg/syscall/ for example.
  • As of this moment the Go language became my second favorite moving down JS and Python. Sorry, Lisp still remains the first. This of course might be a temporary condition till I start writing some real code. I hope it’s not.