Showing posts with label remote. Show all posts
Showing posts with label remote. Show all posts

Monday, December 31, 2018

Finding the IP address for a KVM guest (in bridge mode)

I have a Windows 10 vm guest, testdesktop, in my KVM vm host I want to remote in.

raub@vmhost:~$ virsh list
 Id    Name                           State
----------------------------------------------------
 1     desktop                        running
 16    testdesktop                    running

raub@vmhost:~$

Thing is I did not set it up to support VNC as console, so I cannot just do virsh vncdisplay testdesktop and go from there. Well, I really just want to connect to it using RDP because that is what I want to do. But to do that I need to know its hostname or IP. I thought it was testdesktop.in.example.com, but I am wrong. So, what can I do?

As far as I know (if I am wrong, do let me know!), I cannot get the IP of a guest directly using virsh unless KVM is acting as the DCHP server. In my case, that is not the case; I am using the domain's DHCP and DNS servers since this guest is in bridge mode:

virsh dumpxml testdesktop
[...[
    &linterface type='bridge'>
      &lmac address='c0:ff:ee:83:eb:ed'/>
      &lsource bridge='br0'/>
      <arget dev='vnet1'/>
      &lmodel type='rtl8139'/>
      <alias name='net0'/>

I found an interesting thread that does offer great suggestions for finding the IP address of a KVM Virtual Machine. Let's see if any of them will help me.

  1. Use my ARP table. The idea here is that since I am in the vm host, vmhost (yes, that is its name, really) testdesktop is a guest of, vmhost's ARP table should see it from time to time. All I need is the MAC address, which I have thanks to previously runnning virsh dumpxml testdesktop:

    raub@vmhost:~$ arp -n|grep c0:ff:ee:83:eb:ed
    raub@vmhost:~$

    Hmmm, don't know why but it ain't there. Next?

  2. Nmap. Yes, it's main use is network security scanner, but Nmap can be used as a glorified ping, where it returns amongst other things the MAC address. Let me show you what I mean by running it against the vmhost (I will be using desktop for that). First we cheat since we know the IP address:

    raub@desktop:/tmp$ sudo nmap -sn -n 192.168.10.19
    
    Starting Nmap 7.01 ( https://nmap.org ) at 2018-12-31 10:45 EST
    Nmap scan report for 192.168.10.19
    Host is up (0.000082s latency).
    MAC Address: BC:5F:F4:54:D7:8D (ASRock Incorporation)
    Nmap done: 1 IP address (1 host up) scanned in 0.22 seconds
    raub@desktop:/tmp$

    Then we use the MAC address to find the IP.

    raub@desktop:/tmp$ sudo nmap -sn 192.168.10.* | grep -B 3 BC:5F:F4:54:D7:8D
    Nmap scan report for vmhost.in.example.com (192.168.10.19)
    Host is up (-0.10s latency).
    MAC Address: BC:5F:F4:54:D7:8D (ASRock Incorporation)
    raub@desktop:/tmp$
    Note: If you want to look cooler, use 192.168.10.0/24 instead of 192.168.10.*. Also, grep -i helps to cover our asses.

    Looks like we have a plan here. So, let's look for testdesktop:

    raub@desktop:~$ nmap -sn 192.168.10.0/24 | grep -i c0:ff:ee:83:eb:ed -B 3
    raub@desktop:~$

    All I got back was was a nice cup of nothing. Next!

  3. What about virsh domifaddr?. Not holding my breath. Remember testdesktop is in bridge mode. But, just to be sure:

    raub@vmhost:~$ virsh domifaddr testdesktop
     Name       MAC address          Protocol     Address
    -------------------------------------------------------------------------------
    raub@vmhost:~$
  4. Well, the arp command should have been deprecated. What about ip neighbour? Er, nope.

    raub@vmhost:~$ ip neighbour | grep -i c0:ff:ee:83:eb:ed
    raub@vmhost:~$
  5. You are making this too complicated! Why not login to the console of this guest and then type ipconfig since it is a Windows box? What if vmhost is run headless? So, I would have to install a windows manager in vmhost, then VNC with all the extra cruft that GUIs require just so I can vnc into it and then use the gui console thingie to get into testdesktop to type one command? Can you say overkill and convoluted? I think we can do better thank you verymuch.

  6. OK, smartguy. Since you are making an article you have found a solution. What did you do?. Well, I like solutions that do not require me to keep installing extra programs; note I used nmap in desktop as it is where I run it. Here is my thought process: vmhost should have the network traffic for testdesktop since it is his guest. Why not then look for traffic matching its MAC address? We can do that using tcpdump which is there to begin with.

    Since I am lazy, I told it to probulate tcpdump the physical network interface, eno1 instead of the bridge I created for KVM. This way I cannot use the excuse that I missed something.

    raub@vmhost:~$ sudo tcpdump -i eno1 | grep c0:ff:ee:83:eb:ed
    [sudo] password for raub:
    tcpdump: verbose output suppressed, use -v or -vv for full protocol decode
    listening on eno1, link-type EN10MB (Ethernet), capture size 262144 bytes
    23:19:09.835884 IP testdesktop.dhcp.example.com.68 > ns.in.example.com.67: BOOTP/DHCP, Request from c0:ff:ee:83:eb:ed (oui Unknown), length 327
    00:49:09.846907 IP testdesktop.dhcp.example.com.68 > ns.in.example.com.67: BOOTP/DHCP, Request from c0:ff:ee:83:eb:ed (oui Unknown), length 327
    02:19:09.857254 IP testdesktop.dhcp.example.com.68 > ns.in.example.com.67: BOOTP/DHCP, Request from c0:ff:ee:83:eb:ed (oui Unknown), length 327
    03:49:09.868670 IP testdesktop.dhcp.example.com.68 > ns.in.example.com.67: BOOTP/DHCP, Request from c0:ff:ee:83:eb:ed (oui Unknown), length 327
    05:19:09.882066 IP testdesktop.dhcp.example.com.68 > ns.in.example.com.67: BOOTP/DHCP, Request from c0:ff:ee:83:eb:ed (oui Unknown), length 327
    06:49:09.895852 IP testdesktop.dhcp.example.com.68 > ns.in.example.com.67: BOOTP/DHCP, Request from c0:ff:ee:83:eb:ed (oui Unknown), length 327
    ^C2384605 packets captured
    2385524 packets received by filter
    914 packets dropped by kernel
    186 packets dropped by interface
    
    raub@vmhost:~$

    So the guest is seen by DNS as testdesktop.dhcp.example.com (I don't want to know why), from which I can get the IP if I so want.

The beauty of the above solution is that it did not really require any special feature from KVM, meaning it should work with VirtualBox, ESXi, HyperV, or even Xen. I like generic solutions.

Monday, October 31, 2016

Creating and uploading ssh keypair using ansible

As some of you have noticed from previous posts, I do use docker. However, I also use Ansible to install packages on and configure hosts. I am not going to talk about when to use each one. Instead, I will try to make another (hopefully) quick post, this time ansible-related.

Let's say I have an user abd I want to set said user up so I can later on ssh to the target machine as that user using ssh key pair. From the machine I am running ansible from. To do so I need to create a ssh keypair and then put the public key to the user's authorized_keys file. And that might require creating said file and ~/.ssh if not available. Now that is a mouthfull so let's do it in steps.

  1. Decide where to put the key pair: This is rather important since we not only need to have it somewhere we can find when we aare ready to copy it to the target server but also we might want not to recreate the key pair every time we run this task.

    For now I will put it in roles/files/, which is not ideal for many reasons. Just to name one, we might want to put it somewhere encrypted or with minimum access by suspicious individuals. But for this discussion it is a great starting place for the key pair. So, let's see how I would verify if the pair already exists or not:

    - name: Check if user_name will need a ssh keypair
        local_action: stat path="roles/files/{{user_name}}.ssh"
        register: sshkeypath
        ignore_errors: True

    The first line (name) writes something to the screen to show where we are in the list of steps. It is rather useful because when we run ansible we then can see which task we are doing. The local_action uses stat to determine if the file roles/files/{{user_name}}.ssh exists. The return/result code is then written to the variable ssheypath. Finally I do not care about any other messages from stat.

  2. Create key pair: there are probably really clever ways that are purely ansible native to do it, but I am not that bright so I will use openssh instead, specifically ssh-keygen. Ansible has a way to call shell scripts, the shell module. Now, I want to run tt>ssh-keygen on the machine I am running ansible from, the local machine in ansible parlance. And for that reason we have the local_action command. Here is one way to do the deed:

    - name: Create user_name ssh keypair if non-existent
        local_action: shell ssh-keygen -f roles/files/"{{user_name}}".ssh -t rsa -b 4096 -N "" -C "{{user_name}}"
        when: not sshkeypath.stat.exists

    As you guessed, we are using ssh-keygen to create the key pair roles/files/user_name.ssh and roles/files/user_name.ssh.pub. The other ssh-keygen are just because I would like my keys to be a bit more secure than those created by the average bear. So far so good: we are here using local_action and shell like in the previous step.

    The interesting part if the when statement. What it is saying is to only create the key pair if it does not already exist. And that is why we did check if roles/files/user_name.ssh existed in the previous step. Of course that meant we assumed that if roles/files/user_name.ssh existed, roles/files/user_name.ssh.pub must also exist. There are ways to check for both but I will leave that to you.

  3. Copy public key to remote server: Now we know we have a ssh key pair, we do need to copy it. You can do it using the file module, which will require a few extra tasks (create the ~/.ssh directory with the proper permissions comes to mind). Or, you can use the appropriately named authorized_key module. I am lazy so I will pick the last option.

    - name: Copy the public key to the user's authorized_keys
        # It also creates the dir as needed
        authorized_key:
          user: "{{user_name}}"
          key: '{{ lookup("file", "roles/files/" ~ user_name ~ ".ssh.pub") }}'
          state: present

    What this does is create ~/.ssh and ~/.ssh/authorized_keys and then copy the key to ~/.ssh/authorized_keys. The most important statement here is the key one. Since it expects us to pass the key as a string, we need to read the contents of the public key. And that is why we called upon the lookup command; think of it here as using cat. And, why we have all the crazy escaping in "roles/files/" ~ user_name ~ ".ssh.pub").

And that is pretty much it. If you want to be fancy you could use a few local_actions to copy the private key to your (local) ~/.ssh and then create/edit ~/.ssh/config to make your life easier. I did not do it here because this user will be used as is by ansible; I need no access to this user as me outside ansible.