Knife EC2 Server Create Error: Authentication failed

Sometimes with all of the rush and trying to keep track of a 1000 moving parts you might get stumped by a fairly simple issue. Here are a few things to check if you get hung up with an “Authentication failed for user” error when running a “Knife ec2 server create” command.

Waiting for sshd access to become availabledone
Connecting to 52.5.159.42
Failed to authenticate ec2-user - trying password auth
Enter your password:
ERROR: Net::SSH::AuthenticationFailed: Authentication failed for user ec2-user@52.5.159.42@52.5.159.42

Do you have your .pem file downloaded and installed with the correct permissions on your workstation you are running the command from?

It should be in the EC2-USER’s .ssh dir -> /home/ec2-user/.ssh

Make sure its chmodded 400

Make sure you have the knife.rb file set correctly to reference the .pem file for you…otherwise you will have a lot of typing for your command.

knife[:identity_file] = "/home/ec2-user/.ssh/aws-seth.pem"

Make sure you are using the correct user. Unless you have specifically changed something in your configurations by default you will be connecting as the “ec2-user”. So make sure thats what is trying to connect in your error output.

Hopefully these tips will help you narrow down the issue. You have to think about whats really happening and from where with Chef some sometimes these simple issues can really drive you nuts.

Advertisements

Setting up an Apache server using AWS CloudFormation

This a is a brief and simple guide to spinning up an instance, installing Apache, and writing output to a webpage.

Note that by following this guide you can incur charges. Im not responsible for them and will not be picking up your bill.

Setup

1. Have an AWS account. You can signup for one here: http://aws.amazon.com/
2. Have a computer you can work from. This guide assumes you are running Linux. What other OS is there anyways? For me I have an instance on AWS that I startup whenever I want to work on stuff. On this computer you need to install the AWS CLI tool. You can install and setup the CLI tool by going here: http://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-set-up.html
3. Basic understanding of JSON…not a requirement but it helps to understand what you are reading in the template.

The Template

First here is a link that will guide you further than im going to here on the ins and outs of templates and requirements for CloudFormation: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-guide.html

So lets break down the template…

{
 "AWSTemplateFormatVersion" : "2010-09-09",

This is an optional but lets AWS know what version of the template you are using. This is NOT today’s date or something.

"Description" : "Create an Apache webserver with a webpage.",

Gives a description of what your template is for.

"Parameters" : {
    "InstanceType" : {
      "Description" : "Type of EC2 instance to launch",
      "Type" : "String",
      "Default" : "t1.micro"
    },
    "WebServerPort" : {
      "Description" : "TCP/IP port of the web server",
      "Type" : "String",
      "Default" : "80"
    },
    "KeyName" : {
      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances",
      "Type" : "AWS::EC2::KeyPair::KeyName",
      "ConstraintDescription" : "must be the name of an existing EC2 KeyPair."
    },
    "SSHLocation": {
            "Description": " The IP address range that can be used to SSH to the EC2 instances",
            "Type": "String",
            "MinLength": "9",
            "MaxLength": "18",
            "Default": "0.0.0.0/0",
            "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
            "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x."
        }
  },

Breaking down the parameters directive…
*If you dont give your parameter a “Default” you will need to pass in the option via the command line. This will be explained further but keep that in mind in this section.
InstanceType – Tells what kind you want to launch. The default here is t1.micro.
WebServerPort – You are telling what port your webserver will accept connections on.
KeyName – This is the name of your AWS key that you use to authenticate with. Use a key that has permissions to setup new instances and connect to them.
SSHLocation – The IP address range that can be used to SSH to the EC2 instances

"Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "64" }
    },
    "AWSRegionArch2AMI" : {
      "us-east-1"          : { "64" : "ami-246ed34c" }
    }
  },

Mappings…tells what region and what AMI you want to use. For brevity I already knew what I wanted to use and that I wanted it to fail if not available so my listing of types and regions is VERY short. You can see further info here on mappings: http://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/mappings-section-structure.html

Now we will talk about the Resources section. Its where all the real magic happens. Ill break it down some and then provide the full section at the bottom of this page.

"Resources" : {     
      
    "WebServerInstance": {  
      "Type": "AWS::EC2::Instance",
      "Metadata" : {
        "AWS::CloudFormation::Init" : {
          "configSets" : {
            "InstallAndRun" : [ "Install" ]
          },

          "Install" : {
            "packages" : {
              "yum" : {
                "httpd"        : []              
              }
            },

Here, we are saying that we want to initialize and install something on our instance. Notice that we are using “yum”. The AMI I use is an Amazon AMI which is RHEL based. It comes with some tools already setup and installed specific to AWS so its easier to use this AMI base. Here all we are installing is Apache, or “httpd”.

"files" : {
              "/var/www/html/index.html" : {
              "source" : "https://s3.amazonaws.com/BUCKET_NAME/index.html",  
              "mode"  : "000600",
              "owner" : "apache",
              "group" : "apache"
              },

              "/etc/cfn/cfn-hup.conf" : {
                "content" : { "Fn::Join" : ["", [
                  "[main]\n",
                  "stack=", { "Ref" : "AWS::StackId" }, "\n",
                  "region=", { "Ref" : "AWS::Region" }, "\n"
                ]]},
                "mode"    : "000400",
                "owner"   : "root",
                "group"   : "root"
              },

Here we are pulling down our simple html file from our S3 bucket. I could have easily added code here that would have written to the file the HTML code. Its just cleaner looking code wise to simply pull down the file from your bucket. We are also setting the conf file for cfn-hup. That’s a helper daemon that detects changes and reacts when you are making updates to your stack.

"/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
                "content": { "Fn::Join" : ["", [
                  "[cfn-auto-reloader-hook]\n",
                  "triggers=post.update\n",
                  "path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init\n",
                  "action=/opt/aws/bin/cfn-init -v ",
                  "         --stack ", { "Ref" : "AWS::StackName" },
                  "         --resource WebServerInstance ",
                  "         --configsets InstallAndRun ",
                  "         --region ", { "Ref" : "AWS::Region" }, "\n",
                  "runas=root\n"
                ]]}
              }
            },
            "services" : {
              "sysvinit" : {  
                "httpd"   : { "enabled" : "true", "ensureRunning" : "true" },
                "cfn-hup" : { "enabled" : "true", "ensureRunning" : "true",
                              "files" : ["/etc/cfn/cfn-hup.conf", "/etc/cfn/hooks.d/cfn-auto-reloader.conf"]}}}}}
},

More cfn helper to manage changes. Also see that we are enabling and making sure that httpd and cfn-hup are running on startup. This could also be done through scripting placed into the template.

"Properties": {
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                          { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
        "InstanceType"   : { "Ref" : "InstanceType" },
        "SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
        "KeyName"        : { "Ref" : "KeyName" },
        "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
             "#!/bin/bash -xe\n",
             "yum update -y aws-cfn-bootstrap\n",

             "# Install the files and packages from the metadata\n",
             "/opt/aws/bin/cfn-init -v ",
             "         --stack ", { "Ref" : "AWS::StackName" },
             "         --resource WebServerInstance ",
             "         --configsets InstallAndRun ",
             "         --region ", { "Ref" : "AWS::Region" }, "\n",

             "# Signal the status from cfn-init\n",
             "/opt/aws/bin/cfn-signal -e $? ",
             "         --stack ", { "Ref" : "AWS::StackName" },
             "         --resource WebServerInstance ",
             "         --region ", { "Ref" : "AWS::Region" }, "\n"]]}}}
},

Properties for the instance…Calling on the Mappings info and the yet to be mentioned SecurityGroup. This is also where the packages are installed and configured as you can see the scripting here. Also note that this has all been part of “WebServerInstance”. We are just ending that section.

"WebServerSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable HTTP access via port 80",
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"},
          {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}}
        ]
      }
    }
  },

This sets up the security group and allows access to ports 22 and 80 for http and ssh traffic.

"Outputs" : {
    "WebsiteURL" : {
      "Description" : "URL for newly created Apache server",
      "Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "WebServerInstance", "PublicDnsName" ]}]] }
    }
  }
}

This section will output the info you need to know the URL of your new instance. You will look in the Output section of your CloudFormation stack to see the info which will be a link you can click to see your webpage.

AWS Command

So now you have your AWS CLI tool installed and you have your template written…now its time to fire up your stack and see the fruits of your labor.

The command you will want to run is:

aws cloudformation create-stack --stack-name STACK_NAME --template-url https://s3.amazonaws.com/BUCKET_NAME/template.json --parameters  ParameterKey=KeyName,ParameterValue=YOUR_KEY_NAME

This declares that you are using cloudformation to create a new stack, you are giving the stack name, location of where you will download and read the template from, and finally filling in the parameter of your AWS key.
*Remember earlier when I said that if you do not have a “default” for a parameter that you will need to provide it? This is where I was talking about. For the KeyName param we did not provide a default keyname to use. You can pass a number of parameters on the command line as we have here up to a maximum of 60. Really at that point you should be looking at a scripted option to provide those right?

So once you have executed the above command you can look in your CloudFormation and see your stack being created. If it fails it will stop and rollback the creation. Once it completes go to your Output tab and you should see the URL of your webpage. Click it and you should see whatever you put in your index.html file that is in your S3 bucket.

That’s about it. The best way to learn this is to play around with it and read through the CloudFormation documentation. Let me know if you have comments or questions.

 

Full template for you to copy and paste:

Make sure you use a json validator to be sure that the copy and paste didn’t mangle anything.
{
  "AWSTemplateFormatVersion" : "2010-09-09",

  "Description" : "Create an Apache webserver with a webpage.",

  "Parameters" : {
    "InstanceType" : {
      "Description" : "Type of EC2 instance to launch",
      "Type" : "String",
      "Default" : "t1.micro"
    },
    "WebServerPort" : {
      "Description" : "TCP/IP port of the web server",
      "Type" : "String",
      "Default" : "80"
    },
    "KeyName" : {
      "Description" : "Name of an existing EC2 KeyPair to enable SSH access to the instances",
      "Type" : "AWS::EC2::KeyPair::KeyName",
      "ConstraintDescription" : "must be the name of an existing EC2 KeyPair."
    },
    "SSHLocation": {
            "Description": " The IP address range that can be used to SSH to the EC2 instances",
            "Type": "String",
            "MinLength": "9",
            "MaxLength": "18",
            "Default": "0.0.0.0/0",
            "AllowedPattern": "(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})/(\\d{1,2})",
            "ConstraintDescription": "must be a valid IP CIDR range of the form x.x.x.x/x."
        }
  },

  "Mappings" : {
    "AWSInstanceType2Arch" : {
      "t1.micro"    : { "Arch" : "64" }
    },
    "AWSRegionArch2AMI" : {
      "us-east-1"          : { "64" : "ami-246ed34c" }
    }
  },

"Resources" : {     
      
    "WebServerInstance": {  
      "Type": "AWS::EC2::Instance",
      "Metadata" : {
        "AWS::CloudFormation::Init" : {
          "configSets" : {
            "InstallAndRun" : [ "Install" ]
          },

          "Install" : {
            "packages" : {
              "yum" : {
                "httpd"        : []              
              }
            },

            "files" : {
              "/var/www/html/index.html" : {
              "source" : "https://s3.amazonaws.com/BUCKET_NAME/index.html",  
              "mode"  : "000600",
              "owner" : "apache",
              "group" : "apache"
              },

              "/etc/cfn/cfn-hup.conf" : {
                "content" : { "Fn::Join" : ["", [
                  "[main]\n",
                  "stack=", { "Ref" : "AWS::StackId" }, "\n",
                  "region=", { "Ref" : "AWS::Region" }, "\n"
                ]]},
                "mode"    : "000400",
                "owner"   : "root",
                "group"   : "root"
              },

              "/etc/cfn/hooks.d/cfn-auto-reloader.conf" : {
                "content": { "Fn::Join" : ["", [
                  "[cfn-auto-reloader-hook]\n",
                  "triggers=post.update\n",
                  "path=Resources.WebServerInstance.Metadata.AWS::CloudFormation::Init\n",
                  "action=/opt/aws/bin/cfn-init -v ",
                  "         --stack ", { "Ref" : "AWS::StackName" },
                  "         --resource WebServerInstance ",
                  "         --configsets InstallAndRun ",
                  "         --region ", { "Ref" : "AWS::Region" }, "\n",
                  "runas=root\n"
                ]]}
              }
            },

            "services" : {
              "sysvinit" : {  
                "httpd"   : { "enabled" : "true", "ensureRunning" : "true" },
                "cfn-hup" : { "enabled" : "true", "ensureRunning" : "true",
                              "files" : ["/etc/cfn/cfn-hup.conf", "/etc/cfn/hooks.d/cfn-auto-reloader.conf"]}
              }
            }
          }

        }
      },
      "Properties": {
        "ImageId" : { "Fn::FindInMap" : [ "AWSRegionArch2AMI", { "Ref" : "AWS::Region" },
                          { "Fn::FindInMap" : [ "AWSInstanceType2Arch", { "Ref" : "InstanceType" }, "Arch" ] } ] },
        "InstanceType"   : { "Ref" : "InstanceType" },
        "SecurityGroups" : [ {"Ref" : "WebServerSecurityGroup"} ],
        "KeyName"        : { "Ref" : "KeyName" },
        "UserData"       : { "Fn::Base64" : { "Fn::Join" : ["", [
             "#!/bin/bash -xe\n",
             "yum update -y aws-cfn-bootstrap\n",

             "# Install the files and packages from the metadata\n",
             "/opt/aws/bin/cfn-init -v ",
             "         --stack ", { "Ref" : "AWS::StackName" },
             "         --resource WebServerInstance ",
             "         --configsets InstallAndRun ",
             "         --region ", { "Ref" : "AWS::Region" }, "\n",

             "# Signal the status from cfn-init\n",
             "/opt/aws/bin/cfn-signal -e $? ",
             "         --stack ", { "Ref" : "AWS::StackName" },
             "         --resource WebServerInstance ",
             "         --region ", { "Ref" : "AWS::Region" }, "\n"
        ]]}}        
      }
    },
    
    "WebServerSecurityGroup" : {
      "Type" : "AWS::EC2::SecurityGroup",
      "Properties" : {
        "GroupDescription" : "Enable HTTP access via port 80",
        "SecurityGroupIngress" : [
          {"IpProtocol" : "tcp", "FromPort" : "80", "ToPort" : "80", "CidrIp" : "0.0.0.0/0"},
          {"IpProtocol" : "tcp", "FromPort" : "22", "ToPort" : "22", "CidrIp" : { "Ref" : "SSHLocation"}}
        ]
      }
    }
  },
  
  "Outputs" : {
    "WebsiteURL" : {
      "Description" : "URL for newly created Apache server",
      "Value" : { "Fn::Join" : ["", ["http://", { "Fn::GetAtt" : [ "WebServerInstance", "PublicDnsName" ]}]] }
    }
  }
}

AWS Loadbalancing

For previous setup info see my post on setting up instances using Chef and AWS: HERE

AWS Loadbalancing

If you have completed the steps in the previous post you can repeat them a few times to spin up 3 or more instances. For this post lets assume I have spun up 3 instances named web001, web002, and web003.

1. In your AWS control panel navigate to your EC2 instances and on the left hand column click “Load Balancers” under “Network and Security”.

2. Click the “Create Load Balancer” button at the top.

3. Define Load Balancer Enter a name for your LB and for this example use “EC2-Classic”. By default port 80 is being monitored and since we have 3 web servers running httpd this will be all we need. Click Continue.

4. Configure Health Check For this example the only thing that you need to change is PING PATH. Change it to just “/” dropping the index.html that is already there. Leave everything else the way it is. Continue

5. Add Instances to Load Balancer Check off your 3 instances. Leave the settings for Availability Zone Dist as they are. Continue

6. Keys Here you can create a key and value pair for your load balancer. For example, you could define a tag with key = Name and value = Webserver. Continue

7. Review This will give you a run down of your load balancer before you create it. Click CREATE and it will be started up.

Once your LB has been started it will take a couple of minutes before your instances are in service. If you click the name of your LB  you can see the description tab below. Wait a couple of minutes and then copy the DNS Name which should be the first line that ends with (A Record) Ex. LB-001-201566711211.us-east-1.elb.amazonaws.com Paste this into your browser and you should see your webpage that you created in your instances.

Tabs

You have 7 tabs that contain information about your loadbalancer.

1. Description DNS Name can be found here along with brief info on the status fo the instances connected to this LB

2. Instances Status of the instances that are connected to the LB. You can also drop the instance from the LB here

3. Health Check The main thing to watch here is “Unhealty Threshhold”  You can also edit the Health Check settings here.

4. Monitoring CloudWatch metrics for the selected resources. You can also create alarms for your instances on this page.

5. Security Displays your security groups that are connected to this LB

6. Listeners The ports and protocols you are listening for on your instances.

7. Tags Tags that you created when setting up the LB and you can also create more from this page as well

 

This is a VERY basic run down of how to create a load balancer in Amazon AWS. If you have any questions or input feel free to use the comment section below.

 

Chef: Spin-up and bootstrap a node on AWS

A few quick instructions for spinning up a node on AWS using Chef and the Knife EC2 plugin.
Some of these steps I culled together from various sites and thought it might be helpful to have them all in one spot.

Base box: This is running from an instance I stood up on AWS (redhat)

Prep work:

DO: yum groupinstall Development tools
DO: Install Chef Client: curl -L https://www.opscode.com/chef/install.sh | bash
DO: Download your starter kit from your Chef Hosted account and unzip in /opt/chef/ (assuming you have a hosted account already setup there)
DO: copy your AWS key file (.pem) to your /home/ec2-user/.ssh/ folder so your workstation can connect back to AWS to spin up new instances. (There may be a different or better way of doing this but it worked for me)

 

Install the Knife EC2 plugin…
sudo /opt/chef/embedded/bin/gem install knife-ec2

Configure the knife.rb for AWS…(knife.rb is located at something like… /opt/chef/chef-repo/.chef/knife.rb)

Knife File
____________________________________________________________________________________________

current_dir = File.dirname(__FILE__)
log_level :info
log_location STDOUT
node_name "YOUR_USERNAME"
client_key "#{current_dir}/YOUR_USERNAME.pem"
validation_client_name "YOUR_ORGNAME-validator"
validation_key "#{current_dir}/YOUR_ORGNAME-validator.pem"
chef_server_url "https://api.opscode.com/organizations/YOUR_ORGNAME"
cache_type 'BasicFile'
cache_options( :path => "#{ENV['HOME']}/.chef/checksums" )
cookbook_path ["#{current_dir}/../cookbooks"]

#A note about where to find this info…
#You will need to log into your AWS IAM (https://console.aws.amazon.com/iam) Go to users, click your #username and you should see an area that says “Access Credentials”. You will likely need to create new #ones if you didnt already know what your keys were at this point. Do so and then make sure you download #them and keep in a safe spot. Fill out the next two lines…the rest is optional at this point but #useful to know that you can have this here.

knife[:aws_access_key_id] = “AWS_ACCESS_KEY_ID”
knife[:aws_secret_access_key] = “AWS_SECRET_ACCESS_KEY”

# Default flavor of server (m1.small, c1.medium, etc).
knife[:flavor] = “m1.small”

# Default AMI identifier, e.g. ami-12345678
#knife[:image] = “ami-b06a98d8”

# AWS Region
#knife[:region] = “us-east-1”

# AWS Availability Zone. Must be in the same Region.
#knife[:availability_zone] = “us-east-1b”

# A file with EC2 User Data to provision the instance.
#knife[:aws_user_data] = “”

# AWS SSH Keypair.
##knife[:aws_ssh_key_id] = “Name of the keypair you want to use in AWS…Instance> Keypair”
___________________________________________________________________________________________

 

Final Command

Finally run this command to connect to AWS and start up your instance and install the recipe:
sudo knife ec2 server create -x ec2-user -S *KEY_NAME* -G default,www -r 'recipe[apache2]' -I ami-b06a98d8

-x = user that we’ll connect with SSH
-S = AWS SSH keypair
-G = Security groups
-r = recipes to run once the instance is up
-I = AMI Identifier e.g. ami-12345678

(This is assuming you already know how to store your cookbooks locally and upload them to your Chef server. )

Once this completes you should have a new instance on AWS with a working apache2 instance and should be able to navigate to the webpage you created for Apache. A separate post will be coming soon to cover these steps.

 

Possible Error

You could possibly run into this:
[fog][WARNING] Unable to load the ‘unf’ gem. Your AWS strings may not be properly encoded.
ERROR: Excon::Errors::SocketError: getaddrinfo: Name or service not known (SocketError)

If so install UNF via gem like so:
sudo /opt/chef/embedded/bin/gem install unf
Try your create command again…

Once you are finished playing around you can use Knife EC2 to actually delete the AWS instance and also remove the node from your hosted Chef management Console:
sudo knife ec2 server delete -y –purge INSTANCE_IDENTIFIER
sudo knife ec2 server delete -y --purge i-e639ca0d

 

Log into your AWS EC2 instance as your own user

Once you have created and stood up your EC2 instance on AWS you will want to create your own user. By default you have to use the “ec2-user” user and you are not allowed to log in as root*. So you create your own…

sudo useradd -m -G wheel seth
passwd seth

Once you have done this and you have setup your keys correctly (Separate post) you will want to log in as your new user you created…but you cant. Until you do this…

cp -r /root/.ssh /home/user
chown -R user /home/user/.ssh

This allowed me to use the keypair.pem file to log in.

Now what you must do is turn on the ability to SSH in with a password. This option is turned off by default in all Linux AMIs.

vi, nano, pico, etc. into the following file with root privileges:

sudo vi /etc/ssh/sshd_config

Set the following to YES

PasswordAuthentication = yes

Finally you must restart SSH

sudo service sshd restart

That’s it. You must still add users with the adduser command and give them passwords with the passwd command for them to be able to login to your AMI.

* – In case you need to login as root you can do it with this… sudo su -