2015-09-27

ssh-add complains: Could not open a connection to your authentication agent

When running an ansible playbook a series of ssh connections is used to perform the different playbook actions. Each ssh connection requires the ssh-key passphrase to be entered. The default way to avoid this is to use ssh-agent. The ssh-agent stores your passphrase and send it to ssh if needed. To import a key into ssh-agent you use ssh-add. I try it for my ansible experiments where ansible is running in a docker container (see my previous posts)
docker run --rm -i -t -v $(pwd):/data -w /data thomo/ansible bash -c "ssh-add deploy__2015 && ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=newhosts.keys -i ./deploy__2015' ansible-playbook site.yml -i ./inventories/hosts.baremetal"
Could not open a connection to your authentication agent.
Seams that there is no ssh-agent running. The agent can be start with
eval $(ssh-agent)
But you have to take care, that the evaluation is postponed to shell in the docker - and not be done already in the host shell. So put it in single quotes:
$ ./dr 'eval $(ssh-agent) && ssh-add ./deploy__2015 && ANSIBLE_SSH_ARGS="-o UserKnownHostsFile=newhosts.keys -i ./deploy__2015" ansible-playbook site.yml -i ./inventories/hosts.baremetal'
Agent pid 8
Enter passphrase for ./deploy__2015:
Identity added: ./deploy__2015 (./deploy__2015)

PLAY [apply common configuration to all nodes] *************************
...
dr is a small script which wraps the docker run parameter
docker run --rm -i -t -w /data -v $(pwd):/data thomo/ansible bash -c "$*"

Update 2016-01-06

Improved dr script
SSHPARAM='eval $(ssh-agent) && ssh-add ./deploy__2015 && ANSIBLE_SSH_ARGS="-o UserKnownHostsFile=newhosts.keys -i ./deploy__2015"'
docker run --rm -i -t -w /data -v $(pwd):/data thomo/ansible:20160106 sh -c "${SSHPARAM} $*"
Example usage:
$ ./dr "ansible server -u deploy -m ping -i ./inventories/hosts.baremetal"
Agent pid 9
Enter passphrase for ./deploy__2015:
Identity added: ./deploy__2015 (./deploy__2015)
10.0.0.1 | success >> {
    "changed": false,
    "ping": "pong"
}

2015-09-26

NetCologne DSL Spectrum @Home

In order to document it - and to wallow in self-pity.

Download: 4862 kbit/s
Upload: 639 kbit/s

Ansible: Bootstrap new host - ssh issues

While learning/playing with ansible I want to bootstrap a new host. I found some examples like 5-min-bootstrap and 5minbootstrap and want to try it for my own.

My setup is a Vagrant Box with a CentOS7 image (i use puppetlabs/centos-7.0-64-nocm) and Ansible installed in a docker image (Dockerfile). (see my blog post about the connection issue with this setup problem/solution).

I start with a very minimal bootstrap playbook
My inventory file just contains the IP of the VagrantBox
[newhosts]
172.28.128.3
I run it with
$ docker run --rm -i -t -v $(pwd):/data -w /data thomo/ansible bash -c 
"ansible-playbook bootstrap.yml -i ./inventories/newhosts --ask-pass"
SSH password:

PLAY [Bootstraping new servers] ****************************************

GATHERING FACTS ********************************************************
fatal: [172.28.128.3] => Using a SSH password instead of a key is not possible because Host Key checking is enabled and sshpass does not support this.  Please add this host's fingerprint to your known_hosts file to manage this host.

PLAY RECAP *************************************************************
           to retry, use: --limit @/root/bootstrap.retry

172.28.128.3               : ok=0    changed=0    unreachable=1    
failed=0

$
To publish the host's fingerprint to the ssh client running in the docker container I decided to make it availible via the docker host. Using the option key UserKnownHostsFile the ssh client will use this to check for known_hosts.
$ touch newhosts.keys

$ docker run --rm -i -t -v $(pwd):/data -w /data thomo/ansible bash -c 
"ssh -o UserKnownHostsFile=newhosts.keys 172.28.128.3"
The authenticity of host '172.28.128.3 (172.28.128.3)' can't be established.
ECDSA key fingerprint is 39:e5:9b:0d:8b:bd:74:0a:12:e8:c6:37:cb:cf:17:c3.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '172.28.128.3' (ECDSA) to the list of known hosts.
root@172.28.128.3's password:
Last login: Sat Sep 26 10:03:12 2015 from 172.28.128.1
[root@localhost ~]# exit
logout
Connection to 172.28.128.3 closed.

$ docker run --rm -i -t -v $(pwd):/data -w /data thomo/ansible bash -c 
"ssh -o UserKnownHostsFile=newhosts.keys 172.28.128.3"
root@172.28.128.3's password:
Last login: Sat Sep 26 10:04:27 2015 from 172.28.128.1
[root@localhost ~]# exit
logout
Connection to 172.28.128.3 closed.

$
To tell ansible to use the alternative known_host file the environment variable ANSIBLE_SSH_ARGS can be used. Further I have to specify that ssh should use scp instead of sftp to copy files. This is done with ANSIBLE_SCP_IF_SSH=1 (issue of the docker image I use).
With both varibles I can run my bootstrap playbook.
$ docker run --rm -i -t -v $(pwd):/data -w /data thomo/ansible bash -c "ANSIBLE_SSH_ARGS='-o UserKnownHostsFile=newhosts.keys' ANSIBLE_SCP_IF_SSH=1 ansible-playbook bootstrap.yml -i ./inventories/newhosts --ask-pass"
SSH password:

PLAY [Bootstraping new servers] **************************************
GATHERING FACTS ******************************************************
ok: [172.28.128.3]

PLAY RECAP ***********************************************************
172.28.128.3               : ok=1    changed=0    unreachable=0    failed=0

$

Connect to Vagrant box from docker container running on same host

As described in my last blog post, I could not access my Vagrant box from the Docker container. The reason was that the Vagrant Box had only a NAT interface. This NAT interface is required by Vagrant and you can not change it, e.g. to a Bridged interface. But you can add another interface.

I have added the following entry to the Vagrantfile:
config.vm.network "private_network", type: "dhcp"
After restart the Vagrant Box I determine the assigned IP
$ vagrant ssh -c "ip address"
1: lo:  mtu 65536 qdisc noqueue state UNKNOWN
    link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
    inet 127.0.0.1/8 scope host lo
       valid_lft forever preferred_lft forever
    inet6 ::1/128 scope host
       valid_lft forever preferred_lft forever
2: enp0s3:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 08:00:27:39:18:3c brd ff:ff:ff:ff:ff:ff
    inet 10.0.2.15/24 brd 10.0.2.255 scope global dynamic enp0s3
       valid_lft 86365sec preferred_lft 86365sec
    inet6 fe80::a00:27ff:fe39:183c/64 scope link
       valid_lft forever preferred_lft forever
3: enp0s8:  mtu 1500 qdisc pfifo_fast state UP qlen 1000
    link/ether 08:00:27:e2:20:14 brd ff:ff:ff:ff:ff:ff
    inet 172.28.128.3/24 brd 172.28.128.255 scope global dynamic enp0s8
       valid_lft 1170sec preferred_lft 1170sec
    inet6 fe80::a00:27ff:fee2:2014/64 scope link
       valid_lft forever preferred_lft forever
Connection to 127.0.0.1 closed.
From the docker container the box is now reachable using 172.28.128.3 .

2015-09-25

Unable to access Vagrant box from docker container running in VirtualBox

I want to setup my home server with a new OS (CentOS 7). I decided to use Ansible to provisioning the server because I want to document the different steps, and to learn Ansible. To be able to play with Ansible and to try out different steps I had the idea to use a virtual box image with CentOS7 as target (simulating my new server) and a docker container with ansible as provisioner. Both systems/boxes was setup in just a few minutes. Since I already have installed Vagrant, VirtualBox and Docker I just need to do the following
  • Download and start the virtualbox
  • Build a docker image with this Dockerfile by execute

    docker build -t thomo/ansible .

    and run it with
    docker run --rm -i -t -v $(pwd):/data -w /data thomo/ansible bash
Afterwards I had the shown structure

Fine - I thought at least ...

Next I tried to ping "new host" from the ansible container.
$ docker run --rm -i -t -v $(pwd):/data -w /data thomo/ansible bash
[root@07091097c2ca data]# ping 10.0.2.15
PING 10.0.2.15 (10.0.2.15) 56(84) bytes of data.
64 bytes from 10.0.2.15: icmp_seq=1 ttl=64 time=0.055 ms
64 bytes from 10.0.2.15: icmp_seq=2 ttl=64 time=0.073 ms
64 bytes from 10.0.2.15: icmp_seq=3 ttl=64 time=0.103 ms
64 bytes from 10.0.2.15: icmp_seq=4 ttl=64 time=0.108 ms
^C
--- 10.0.2.15 ping statistics ---
4 packets transmitted, 4 received, 0% packet loss, time 3000ms
rtt min/avg/max/mdev = 0.055/0.084/0.108/0.024 ms
[root@07091097c2ca data]#
Seams to work ...

Next: Login in with ssh
[root@07091097c2ca data]# ssh 10.0.2.15
The authenticity of host '10.0.2.15 (10.0.2.15)' can't be established.
ECDSA key fingerprint is d2:62:41:e4:a3:d2:40:cf:a0:02:eb:d0:16:ab:49:bc.
Are you sure you want to continue connecting (yes/no)? yes
Warning: Permanently added '10.0.2.15' (ECDSA) to the list of known hosts.
root@10.0.2.15's password:
Permission denied, please try again.
root@10.0.2.15's password:
Permission denied, please try again.
root@10.0.2.15's password:
Permission denied (publickey,password,keyboard-interactive).
[root@07091097c2ca data]#
Huh, what the heck ...?

After some investigations (mainly by comparing the host key fingerprints) I realize that I did not communicate with my "new host" box but with the docker host. In fact the "new host" box and the docker host both use the VirtualBox NAT interface but the boxes can not reach each other.
In VirtualBox this router [the NAT interface] is placed between each virtual machine and the host. This separation maximizes security since by default virtual machines cannot talk to each other. (source)
You wonder why the ping trial worked? The reason is the docker host has the same ip address (10.0.2.15) as the "new host". So I did ping the docker host instead of "new host".

I hope my documentation helps other to avoid similar errors or at least facilitates troubleshooting in a similar situation.

2015-09-13

Generate a glossary with Jekyll

With Jekyll you can generate page content out of data files. Below is shown how this can be used to generate a glossary from a simple glossary file given as term - description list (in a json file).

Example data file:
Given a glossary file (e.g. glossary.json) with this content is in the _data folder of a jekyll site directory. To generate a page with the content where the glossary is sorted by term.