Showing posts with label Windows. Show all posts
Showing posts with label Windows. Show all posts

Wednesday, September 27, 2023

Debugging bash multiline commands in vi

The ancient vi (1976), and its enhanced and slightly more modern (1991) successor vim have become unnapreciated workhorses in recent years. They come with some interesting features you expect to find in modern IDEs. Case in point is they have built-in features that help debug scripts. In today's episode, let's start with following script:

cat > multiline.sh << 'EOF'
#!/bin/bash
# Test multiline commands
# 20230926

is_this_on=true

if [ is_this_on ]; then
        echo "yes"
        ls -l \ 
                --author \
                $(pwd)
fi
EOF

Realistically, it only shows 3 things:

  • if statements respond to true/false statements (the tests we feed them return that in the end of the day)
  • The --author option offerend in modern gnu ls.
iamdeving@desktop:~/dev/scripts/shell$ ./multiline.sh 
yes
total 92
-rwxrwxr-x 1 iamdeving iamdeving iamdeving 1092 Jan 23  2020 argtest.sh
drwxrwxr-x 3 iamdeving iamdeving iamdeving 4096 Jun 28  2021 ca-stuff
-rw-rw-r-- 1 iamdeving iamdeving iamdeving  168 Jan 30  2020 commandlinetest.sh
-rwxrwxr-x 1 iamdeving iamdeving iamdeving  153 Jun 16  2021 countest.sh
[...]
iamdeving@desktop:~/dev/scripts/shell$

What is the third thing the script shows? How to break a command into multiple lines, which is done adding \ to where we want to have a break. In this case, the ls statement was broken up into 3 lines for no reason whatsoever but to show \ in action:

        ls -l \ 
                --author \
                $(pwd)

There is a potential problem in the above: if you add a space after one of the \, it will break the statement because now the \ is making the space special, not the linefeed (a.ka.a \n) character. I intentionally did that and rerun the script, leading to the following error.

iamdeving@desktop:~/dev/scripts/shell$ ./multiline.sh 
yes
ls: cannot access ' ': No such file or directory
./multiline.sh: line 11: --author: command not found
iamdeving@desktop:~/dev/scripts/shell$ 

If you are using vim, and have it configured to highlight the syntax of programming languages (it also does a great job with html, Python, github markup language, Jekyll), and others) and open the file, you will notice one of the \ is red. That indicates something is fishy around it. Moving the cursor will show there is a space after the slash:

That picture is great if

  • You have setup vi to be in "helpful editing mode." But, it is not so great to show you there is an extra space there until you manually move your cursor there (I myself highlight the entire area and see if something that should not exist is hightighted). My trick is really not that helpful unless you know to look for it.
  • You do not have vision impairements; it relies on you being able to see the colours (yes you can configure that), or being able to see at all.

Can we do something for those who do not meed the above requirements? Actually, yes. vi was created in a time where all text was one colour, usually green. So, it has aids that always work no matter the language you are editing or how fancy your terminal session is. In this case, while we have that file open, type :set list. You will not see the same file with some new characters added.

#!/bin/bash$
# Test multiline commands$
# 20230926$
$
is_this_on=true$
$
if [ is_this_on ]; then$
^Iecho "yes"$
^Ils -l \ $
^I^I--author \$ 
^I^I$(pwd)$     
fi$

The ^I represents a tab while the $ stands in for a linefeed character (we are editing a file written in Linux/UNIX/MacOS here, so it does not have carriage return characters Windows likes so much). If you look at the two lines that have a \ in the end, one of them have a space between the \ and the $ characters: we found the culprit.

If you do not want to use vi another humble option is cat:

iamdeving@desktop:~/dev/scripts/shell$ cat -A multiline.sh 
#!/bin/bash$
# Test multiline commands$
# 20230926$
$
is_this_on=true$
$
if [ is_this_on ]; then$
        echo "yes"$
        ls -l \ $
                --author \$
                $(pwd)$
fi$
iamdeving@desktop:~/dev/scripts/shell$ 

The uses for this feature do not end there. You can view special characters (besides tabs) and ensure there is nothing hidden that should not be there, which may not be as easy with helpful GUI-based IDEs.

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.

Wednesday, October 03, 2018

Creating and mounting a fileshare as a file in a KVM Windows guest

Yeah, the title is a mouthfull.

So, let's get it done.

  1. Create the disk, which is henceforth called foretest.iso. I made it to be 1G because, as the name says, it is a test.
    raub@vmhost:~$ dd if=/dev/zero of=foretest.iso bs=1G count=1
    1+0 records in
    1+0 records out
    1073741824 bytes (1.1 GB, 1.0 GiB) copied, 2.29044 s, 469 MB/s
    raub@vmhost:~$
    NOTE: Some people would complain that I am using the .iso extension for my image file instead of, say, .img. Well, this is my article; deal with it.
  2. Now let's feed it to the vm.
    raub@vmhost:~$ virsh attach-disk desktop dev/foretest.iso hda --type raw --mode readwrite                 
    error: No support for readwrite in command 'attach-disk'
    
    raub@vmhost:~$

    Well, I did not know my KVM host does not have attach-disk. I guess I need to use attach-device instead. But, we will need to use a config file to describe how we want the storage to look like. If it looks familiar, we have used attach-disk to mount a USB device, namely an APC UPS.

    So, here is the .xml file:

    cat > dev/foretest.xml << 'EOF'
    <disk type='file' device='disk'>
       <driver name='qemu' type='raw' cache='none'/>
       <source file='/home/raub/dev/foretest.iso'/>
    <target dev='hdc'/>
    </disk >
    EOF

    And here we are feeding it into the guest:

    raub@vmhost:~$ sudo virsh attach-device --config testdesktop dev/foretest.xml
    Device attached successfully
    
    raub@vmhost:~$

    So we should be able to go to the guest and see the drive waving at us, right? Er, not quite. it is being listed here

    <disk type='file' device='disk'>
    
          <source file='/home/raub/dev/foretest.iso'/>
          <target dev='vdd' bus='virtio'/>
          <address type='pci' domain='0x0000' bus='0x00' slot='0x06' function='0x0'/>
       </disk>

    But not in the GUI thingie nor inside the guest. And, yes, I have 2 virtual CD drives in this guest.


On a second thought, maybe I am doing this wrong. You see, the --config option tells it to add the drive to the config file. Only way to enable that is to completely shut down the vm guest (rebooting is not enough) and then restart it. What if I use --live instead, which does the deed in real time, as if I just pop the computer on and add the drive to an unused sata port?

raub@vmhost:~$ sudo virsh attach-device --live testdesktop dev/foretest.xml
error: Failed to attach device from dev/foretest.xml
error: internal error unable to execute QEMU command '__com.redhat_drive_add': Device 'drive-virtio-disk3' could not be initialized

raub@vmhost:~$

I guess it does not like me to add hard drives to a running guest. We need to try something else.

Attempt #2: USB

What if we pretend our disk is really a removable device like a USB drive? We begin by rewriting our .xml file:

cat > dev/foretest.xml << 'EOF'
<disk type='file' device='disk'>
  <driver name='qemu' type='raw'/>
  <source file='/home/raub/dev/foretest.iso'/>
  <target dev='sdd' bus='usb'/>
</disk>
EOF

Now let's mount it:

raub@desktop:~$ sudo virsh attach-device --live testdesktop dev/foretest.xml
Device attached successfully

raub@desktop:~$

So far so good. Can our guest see it this time? When I use Windows Explorer (do notice I disabled one of the CD drives as I was doing something else between the last screen capture and this one) I do not see the drive there; I kinda expected Windows to say something like "Hey! You just connected an unformatted drive! I need to format it!" Oh well; there are more than one way to get this done:

There it is! And we can format it!

And then, we can put things into it.

Now, let's unmount it

raub@desktop:~$ sudo virsh detach-device --live testdesktop dev/foretest.xml
Device detached successfully

raub@desktop:~$

Now let's make it a physical drive!

Stupid question: since we pretend our foretest.iso file is a USB drive, can we make it a real USB drive? We would test that question by first grabbing a USB drive which is at least 1GB in size (I had a 16GB one doing nothing), which was mounted into my KVM host as /dev/sdh.And now we need to copy the file to the drive.

raub@desktop:~$ sudo dd if=dev/foretest.iso of=/dev/sdh
raub@desktop:~$

Then grab USB drive and then mount it in a windows physical desktop. And it will look just like how it looks in the testdesktop guest down to be able to write to it.

The next step will be to see if we can make it a system disk, a disk we can boot a computer from. But, that will be for another article.

Monday, September 24, 2018

Using ldapsearch and ldapmodify to talk to Active Directory

Why?

Great question! Here are a few lame excuses I was able to come with:

  • I like to use command line. This is a lame excuse because Windows have powershell. But,
  • I am more comfortable with Linux than Windows. Lame excuse since
    1. How many posts in this very blog I have made about using Windows?
    2. How many of said posts I have used the GUI when I could take care of business with Powershell?
    3. What is wrong with Powershell, at least of the applications I have used it for here so far?
I do have a couple of not so lame ones though:
  • I like to be able to access the network resources from any machine in the network running any OS. If I have a Linux box in an Active Directory-controlled network, chances are I will need to authenticate the Linux box against Active Directory (AD so I can save some keytaps). AD is Kerberos + ldap + sprinkles, so I better be able to use the usual kerberos/ldap Linux tools as one day I will need to figure out why things are boink.
  • It feels like I get more info using ldapsearch than the Windows tools, which is good when I do not know the name of an attribute, or how many instaces of said attribute are in use.

Using ldapsearch

Before we go mindlessly typing things, we need some data.
We need to know the name of the ldapserver.
Yes, if you have it configured in your ldap.conf file, you should not need it. But I prefer to assume nothing. If the domain was setup properly, we can ask it directly by typing nslookup -type=srv _ldap._tcp.DOMAIN where DOMAIN is the Active Directory domain name, not the DNS one; that caught me off guard. So, if our DNS dmain is example.com and the AD domain (we are very original) is ad.example.com, we have
raub@desktop:/tmp$ nslookup -type=srv _ldap._tcp.ad.example.com
Server:         192.168.0.10
Address:        192.168.0.10#53

Non-authoritative answer:
_ldap._tcp.ad.example.com   service = 0 100 389 ADDC0.ad.example.com.
_ldap._tcp.ad.example.com   service = 0 100 389 ADDC2.ad.example.com.
_ldap._tcp.ad.example.com   service = 0 100 389 ADDC1.ad.example.com.

Authoritative answers can be found from:
ad.example.com      nameserver = addc1.ad.example.com.
ad.example.com      nameserver = ns.example.com.
ad.example.com      nameserver = ns2.example.com.
ad.example.com      nameserver = addc0.ad.example.com.
ad.example.com      nameserver = addc2.ad.example.com.
ADDC0.ad.example.com        internet address = 192.168.1.100
ADDC1.ad.example.com        internet address = 192.168.1.102
ADDC2.ad.example.com        internet address = 192.168.1.101
ns.example.com      internet address = 192.168.0.10
ns2.example.com     internet address = 192.168.0.10

raub@desktop:/tmp$
and we can use any of the ADDCN.ad.example.com (where N=0,1,2). Notice in my setup, just using ad.example.com also worked. I found out by using netcat to see if port 636 was open (I will leave the answer for "why port 636?" as an exercise to the reader)
raub@desktop:~$ nc -v ad.example.com 636
Connection to ad.example.com 636 port [tcp/ldaps] succeeded!
^C
raub@desktop:~$ 
We need to be able to authenticate against AD somehow.
For this discussion I will be using a username and password; we can also do it using a Kerberos TGT ticket.

Fun Fact: I have a user account, my normal one, which can look into some things in LDAP/AD but then I have another ("admin") account I can see more and edit stuff in AD. You will see later on me forgetting completely about that and how it affects me. But we are getting ahead of ourselves.

With that taken care of, we are going to begin by looking for some user: me

raub@desktop:~$ ldapsearch -H "ldaps://addc0.ad.example.com:636" 
-D "raub@ad.example.com" -W -b "dc=ad,dc=example,dc=com" -LLL -s sub "(CN=raub)" 
Enter LDAP Password:
dn: CN=raub,OU=Users,OU=Identity,DC=ad,DC=example,DC=com
objectClass: top
objectClass: posixAccount
objectClass: person
objectClass: organizationalPerson
objectClass: user
cn: Wrong Droid
sn: Droid
title: Entropy Creators
description: Orthodontics
givenName: Wrong
initials: B 
distinguishedName: CN=raub,OU=Users,OU=Identity,DC=ad,DC=example,DC=com
instanceType: 4
whenCreated: 20080109142820.0Z
whenChanged: 20180905201157.0Z
displayName: Droid, Wrong
uSNCreated: 243336
memberOf: CN=cookie_recipes,OU=Distribution Groups,OU=Special Users,DC=ad,DC=example,DC=com
memberOf: CN=servers,OU=Groups,OU=APE,OU=EXAMPLE,DC=ad,DC=example,DC=com
memberOf: CN=third_floor_printers,OU=Groups,OU=APE,OU=EXAMPLE,DC=ad,DC=example,DC=com
[...]
proxyAddresses: sip:raub@ad.example.com
proxyAddresses: smtp:raub@ad.example.com
proxyAddresses: X500:/o=UNC Exchange/ou=Exchange Administrative Group (FYDIBOH
 F23SPDLT)/cn=Recipients/cn=raub
proxyAddresses: SMTP:raub@email.example.com
displayNamePrintable: Wrong Droid
name: Wrong Droid
[...]
sExchPoliciesExcluded: {26491cfc-9e50-4857-861b-0cb8df22b5d7}
msExchUserAccountControl: 0
msExchELCMailboxFlags: 2
msRTCSIP-PrimaryHomeServer:
[...]
msExchOWAPolicy: CN=Default,CN=OWA Mailbox Policies,CN=Exchange,CN=Microsoft Exchange,CN=Services,CN=Configuration,DC=ad,DC=example,DC=com

# refldaps://ForestDnsZones.ad.example.com/DC=ForestDnsZones,DC=ad,DC=example,DC=com

# refldaps://DomainDnsZones.ad.example.com/DC=DomainDnsZones,DC=ad,DC=example,DC=com

# refldaps://ad.example.com/CN=Configuration,DC=ad,DC=example,DC=com

raub@desktop:~$

That is probably more info than you wanted to know about someone, but this has its applications. Since we now know every attribute associated with a given user (I should not be that special, at least as far as AD is concerned), we can build customized queries looking for only a specific bit of info. For instance, let's just get the groups I belong to or am a member of (hint hint):

raub@desktop:~$ ldapsearch -H "ldaps://addc0.ad.example.com:636" 
-D "raub@ad.example.com" -W -b "dc=ad,dc=example,dc=com" -LLL -s sub "(CN=raub)" memberOf 
dn: CN=raub,OU=Users,OU=Identity,DC=ad,DC=example,DC=com
memberOf: CN=cookie_recipes,OU=Distribution Groups,OU=Special Users,DC=ad,DC=example,DC=com
memberOf: CN=servers,OU=Groups,OU=APE,OU=EXAMPLE,DC=ad,DC=example,DC=com
memberOf: CN=third_floor_printers,OU=Groups,OU=APE,OU=EXAMPLE,DC=ad,DC=example,DC=com

Fancy, huh? Now, if instead of doing (CN=raub) we did (CN=*raub*), it would return every entry that has a CN with raub in it. In my case that would mean two entries, raub and raub.admin (if you remember our Fun Fact you will know about it), but it could also have returned a device whose name matches that. And, they would have been printed one after the other (I do wonder if the order depends on the order they were added to LDAP/AD).

Using ldapmodify

Ok, we established we can probulate Active Directory using common household Linux/UNIX query tools. What if we want to change something? Let's say we have an AD group (specifically a distribution list) called moustache_operators (for those who own and operate moustaches) and want to add a member and delete another.

Why I do like ldapmodify to edit LDAP/AD

Main reason is because I can create a file (in the LDIF format) at my leisure (i.e. think about what I want to do) with pretty commands and comments describing what I want to do. If I like what I did, I can then document it and maybe even save the file in a wiki or somewhere that can be fed to Ansible/Puppet/Chef/Docker and reused.

Our little LDIF file, let's call it change.ldif, could look like this:

# Let's define the entity we will be fiddling with
dn: CN=moustache_operators,OU=Distribution Lists,OU=Special Users,DC=ad,DC=example,DC=com
# And then what we will be doing with it
changetype: modify
delete: member
member: CN=baldone,OU=Users,OU=Identity,DC=ad,DC=example,DC=com
# Separator because we will be doing another change
-
add: member
member: CN=raub,OU=Users,OU=Identity,DC=ad,DC=example,DC=com

So let's try it:

raub@desktop:~$ ldapmodify -H "ldaps://addc0.ad.example.com:636" -D "raub@ad.example.com" -x -W -f change.ldif
Enter LDAP Password:
modifying entry "CN=moustache_operators,OU=Distribution Lists,OU=Special Users,DC=ad,DC=example,DC=com"
ldap_modify: Insufficient access (50)
        additional info: 00002098: SecErr: DSID-03150F93, problem 4003 (INSUFF_ACCESS_RIGHTS), data 0

raub@desktop:~$ 

Why is it not working? Well, do you remember the Fun Fact I mentioned earlier in this article? This is how it shows it's ugly head. I should have used my raub.admin@ad.example.com account instead of raub@ad.example.com. If we do it right, it then works. I will not show the output of a successful connection here; what matters is verifying the deed is done, and we can do it using dear ol' ldapsearch:

raub@desktop:~$ ldapsearch -H "ldaps://addc0.ad.example.com:636" 
-D "raub@ad.example.com" -W -b "dc=ad,dc=example,dc=com" -LLL -s sub "(CN=raub) memberOf" dn: CN=raub,OU=Users,OU=Identity,DC=ad,DC=example,DC=com
memberOf: CN=cookie_recipes,OU=Distribution Groups,OU=Special Users,DC=ad,DC=example,DC=com
memberOf: CN=servers,OU=Groups,OU=APE,OU=EXAMPLE,DC=ad,DC=example,DC=com
memberOf: CN=third_floor_printers,OU=Groups,OU=APE,OU=EXAMPLE,DC=ad,DC=example,DC=com
memberOf: CN=moustache_operators,OU=Distribution Lists,OU=Special Users,DC=ad,DC=example,DC=com

What about Mac/OSX?

They too have ldapsearch; just use the terminal and off you go.

Monday, August 27, 2018

VMWare ESXI Gripes: service-control error message not particularly useful

I posted this at the vmware support forum, whose sophisticated text processing interface decided that it knew better than me and reimagineered my pure html post into a rather flat one. I feel like venting and will do so by posting here what it should have looked like there. Note that right now it is just a bunch of WTF-like questions.

We begin this by stating the ESXi cluster in question uses Windows vcenter server; that is not by my choice. I would rather use the appliance

C:\Users\raub> "C:\Program Files\VMware\vCenter Server\bin\service-control" --status --all
Running:
 VMWareAfdService VMWareCertificateService VMWareDirectoryService VMwareComponentManager VMwareDNSService VMwareIdentityMgmtService VMwareSTS rhttpproxy vmon vmonapi vmware-cis-config vmware-license vmwareServiceControlAgent
Stopped:
 EsxAgentManager VMWareCAMService VServiceManager content-library mbcs vPostgres vapiEndpoint vimPBSM vmsyslogcollector vmware-autodeploy-waiter vmware-imagebuilder vmware-network-coredump vmware-perfcharts vpxd vpxd-svcs vsan-health vsphere-ui vspherewebclientsvc

C:\Users\raub>

C:\Users\raub> "C:\Program Files\VMware\vCenter Server\bin\service-control" --start vspherewebclientsvc
Operation not cancellable. Please wait for it to finish...
Performing start operation on service vsphere-client...
Error executing start on service vsphere-client. Details {
    "detail": [
        {  
            "id": "install.ciscommon.service.failstart",
            "translatable": "An error occurred while starting service '%(0)s'",
            "localized": "An error occurred while starting service 'vsphere-client'",
            "args": [
                "vsphere-client"
            ]
        }
    ],
    "resolution": null,
    "problemId": null,
    "componentKey": null
}
Service-control failed. Error: {
    "detail": [
        {
            "id": "install.ciscommon.service.failstart",
            "translatable": "An error occurred while starting service '%(0)s'",
            "localized": "An error occurred while starting service 'vsphere-client'",
            "args": [
                "vsphere-client"
            ]
        }
    ],
    "resolution": null,
    "problemId": null,
    "componentKey": null
}

C:\Users\raub>

I understand that "An error occurred while starting service 'vsphere-client'", but what is it? Maybe the log file is more helpful. https://kb.vmware.com/s/article/2121043 claims log dir is
C:\ProgamData\VMware\vCenterServer\logs\vsphere-client\logs\
But I can see lots of directories in there but the log one:
C:\Users\raub>dir  "C:\Program Files\VMware\vCenter Server\"
 Volume in drive C has no label.
 Volume Serial Number is F020-F58F

 Directory of C:\Program Files\VMware\vCenter Server

05/21/2018  07:36 PM    <DIR>          .
05/21/2018  07:36 PM    <DIR>          ..
05/21/2018  07:11 PM    <DIR>          apachetomcat
05/21/2018  07:15 PM    <DIR>          autodeploy
05/21/2018  07:17 PM    <DIR>          bin
05/21/2018  07:13 PM    <DIR>          cis-license
05/21/2018  07:10 PM    <DIR>          cis_upgrade_runner
05/21/2018  07:13 PM    <DIR>          cm
05/21/2018  07:10 PM    <DIR>          common-jars
05/21/2018  07:10 PM    <DIR>          common-libs
05/21/2018  07:15 PM    <DIR>          content-library
05/21/2018  07:15 PM    <DIR>          eam
05/21/2018  07:36 PM    <DIR>          eula
05/21/2018  07:12 PM    <DIR>          fips
05/21/2018  07:21 PM    <DIR>          firstboot
05/21/2018  07:15 PM    <DIR>          imagebuilder
05/21/2018  07:10 PM    <DIR>          jmemtool
05/21/2018  07:10 PM    <DIR>          jre
05/21/2018  07:12 PM    <DIR>          jre_ext
05/21/2018  07:15 PM    <DIR>          mbcs
05/21/2018  07:13 PM    <DIR>          netdump
05/21/2018  07:10 PM    <DIR>          openSSL
04/09/2018  01:24 PM         7,398,602 open_source_license.txt
05/21/2018  07:16 PM    <DIR>          perfcharts
05/21/2018  07:11 PM    <DIR>          python
05/21/2018  07:16 PM    <DIR>          python-modules
05/21/2018  07:13 PM    <DIR>          rhttpproxy
05/21/2018  07:12 PM    <DIR>          ruby
05/21/2018  07:15 PM    <DIR>          rvc
05/21/2018  07:13 PM    <DIR>          sca
05/21/2018  07:09 PM    <DIR>          TlsReconfigurator
05/21/2018  07:13 PM    <DIR>          vapi
04/09/2018  01:24 PM            25,214 vcs.ico
05/21/2018  07:14 PM    <DIR>          virgo
05/21/2018  07:12 PM    <DIR>          visl-integration
05/21/2018  07:12 PM    <DIR>          vmafdd
05/21/2018  07:12 PM    <DIR>          vmcad
05/21/2018  07:15 PM    <DIR>          vmcamd
05/21/2018  07:12 PM    <DIR>          vmdird
05/21/2018  07:13 PM    <DIR>          vmdns
05/21/2018  07:13 PM    <DIR>          vmon
05/21/2018  07:15 PM    <DIR>          vmsyslogcollector
05/21/2018  07:13 PM    <DIR>          VMware Identity Services
05/21/2018  07:11 PM    <DIR>          vmware-sasl
04/25/2018  01:20 PM    <DIR>          vmware-sps
05/21/2018  07:13 PM    <DIR>          vmware-sso
05/21/2018  07:14 PM    <DIR>          vPostgres
05/21/2018  07:25 PM    <DIR>          vpxd
05/21/2018  07:14 PM    <DIR>          vpxd-svcs
05/21/2018  07:32 PM    <DIR>          vsan-health
05/21/2018  07:34 PM    <DIR>          vsm
05/21/2018  07:16 PM    <DIR>          vsphere-client
05/21/2018  07:17 PM    <DIR>          vsphere-ui
               2 File(s)      7,423,816 bytes
              51 Dir(s)  69,727,727,616 bytes free

C:\Users\raub>

C:\Users\raub>dir  "C:\Program Files\VMware\vCenter Server\logs"
 Volume in drive C has no label.
 Volume Serial Number is F020-F58F

 Directory of C:\Program Files\VMware\vCenter Server

File Not Found

C:\Users\raub>

Where can I find where vcenter thinks the log files are at?

If you want to see how the ticket looks like at vmware, https://communities.vmware.com/message/2796432#2796432. Try to read the version posted at vmware and you will understand why I am frustrated.

Saturday, June 02, 2018

Finding disk space hogs in a Windows server/workstation

If you have any doubts, we will be doing it from the command line. Just want to put that out before we start. Also, it turned out this is a long and boring article; deal with it.

So, where were we? Disk space and what is using it. That is a problem common to all OS: you have a partition without infinite disk space (sorry ZFS, it happens sometimes) and is running out of space:

  • Sometimes it is a careless user; some OS allow you to tell non-system programs, like the ones run by a user, can only use up to 95% of the disk. This way we have some space to fix things.
  • Sometimes it is actually a program being run as a system/root/admin account, which is trouble since it can use up the entire partition.

If the machine in question is a server, a Windows server since that is what we wrote in the name of the article, we might not be able to just ignore it; others will be affected by this. So, how to take care of this problem? The lazy fix is to throw more space at it and move on. The proper solution is to find out who is hoarding all the space and why. I would like to talk about doing the right thing.

The standard Windows approach would be to search for some app online, which must have a graphics interface and ideally from some site with a name like "finddiskusage.com" because such domain names do inspire confidence, right? Specially when the site's text is pretty much "You do not know what is using your disk space? Click here to download the solution!" Any relationship with a phishing email is merely coincidental.

So, after downloading this shady program from the suspicious website, we then install it and make sure to turn off the firewall and run it with admin rights. And after it does what we hope it is supposed to do, we then take a screenshot of the output and paste it to our documentation.

I do not know about you but I really do not like to install programs in any server, be it windows/linux/mac/solaris/aix/whatever. I think they should have only the bare minimum to do their job; you should see my Linux servers. Since I am the one writing this article, I will put my dictator hat and look for something that fits my style.

In Unix in general and Linux as a special case there is a program called du which allows you to check the disk usage at a given location. You can be short and only show, say, how much all the files and directories (folders in Windows) inside a given directory, or go recursively and show detailed views for every single directory inside the original one. Output is text, which means you can feed it to something else like sort or some program that will make a decision based on the data.

It would be really cool if there was something like that in Windows. One can dream...

Thing is, we do not have to wait for unicorns and fairies to come up with a solution. Nor we have to reinvent the wheel. You see,

  1. There is something like that natively for windows. I do like cygwin but that requires installing yet another collection of packages that need to be patched and upgraded. Kinda wasteful if all you want is little du. I believe less is more.
  2. It is called du just like the unix one.
  3. You do not need to look for it in some shady or just compromised site. You can find it right at Microsoft as part of the sysinternals package(s).
  4. You do not even need to install anything. Just put its directory somewhere you want to use it, including a USB or network drive, and run it from there.
Not bad if you ask me. Enough talk, let's use it.

Using du

The most common way I use du in Windows is like I use in Linux:

C:\Documents and Settings\raub>"\Documents and Settings\raub\My Documents\DU\du.exe" -l 1 \windows >> du.log

Du v1.5 - report directory disk usage
Copyright (C) 2005-2013 Mark Russinovich
Sysinternals - www.sysinternals.com


C:\Documents and Settings\raub>

Ok, it is old but it is not like it is getting more and more useless features to make it bloated. Like interfacing with your bluetooth-enabled IoT-based massage chair. Let me show it in action with a real (!) example: at work I have a Windows 10 vm for desktop. And as you can see, it ran out of disk disk space:

That's not much free space left! If you know Windows, it will get really slow when its boot/OS disk is that full. I am going to assume I should first check the Users directory. If I am wrong, I would then check the Windows one, First I would like to make a point I will be running du.exe off a network fileshare, strongbadia

PS C:\Users\raub> ls \\spacemoose\users\raub\bin


    Directory: \\strongbadia\users\raub\bin


Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        5/21/2018   9:32 AM         169072 du.exe
-a----        5/21/2018   9:32 AM         191616 du64.exe
-a----        6/28/2018   4:06 PM            543 GetDirSize4DateRange.ps1
-a----        7/18/2017  10:12 AM         854072 putty.exe


PS C:\Users\raub>

You are now getting to learn a few secret things about me! Yes I have a bin dir. Inside it you can see the du.exe and du64.exe. Both are very tiny compared to the crazy GUI programs you can get off suspicious sites to do the very same thing. And, that is all you need: those two files. Well, I will be running du.exe even though my guest is a 64bit windows vm. Because I can. So, let's see what is in the root dir for the users dir:

PS C:\Users\raub> \\strongbadia\users\raub\bin\du.exe -l 1 c:\users\

DU v1.61 - Directory disk usage reporter
Copyright (C) 2005-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

      31,480  c:\users\bob.adm
       2,665  c:\users\Default
  51,390,386  c:\users\raub
     629,717  c:\users\raub.adm
       2,727  c:\users\raub.tst
       2,768  c:\users\Public
     129,592  c:\users\windows-user
Files:        2429983
Directories:  9581
Size:         54,003,166,071 bytes
Size on disk: 59,917,407,544 bytes

PS C:\Users\raub>

Man! My homedir is full of junk. What is that and where is it? Let's check it:

PS C:\Users\raub> \\strongbadia\users\raub\bin\du.exe -l 1 c:\users\raub\

DU v1.61 - Directory disk usage reporter
Copyright (C) 2005-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

  46,023,020  c:\users\raub\AppData
           0  c:\users\raub\Contacts
           1  c:\users\raub\Desktop
           3  c:\users\raub\dev
   1,836,295  c:\users\raub\Documents
   3,551,535  c:\users\raub\Downloads
           0  c:\users\raub\eqlgroupmgr
           0  c:\users\raub\Favorites
           1  c:\users\raub\Links
           0  c:\users\raub\Music
           0  c:\users\raub\OneDrive
       5,012  c:\users\raub\Pictures
           0  c:\users\raub\Saved Games
           3  c:\users\raub\Searches
           0  c:\users\raub\Videos
Files:        2425559
Directories:  3976
Size:         52,663,166,132 bytes
Size on disk: 58,521,224,472 bytes

PS C:\Users\raub>

AppData! A conveniently normally invisible source of many out of space drives. Not to du it is. Let's keep going in:

PS C:\Users\raub> \\strongbadia\users\raub\bin\du.exe -l 1 c:\users\raub\appdata\

DU v1.61 - Directory disk usage reporter
Copyright (C) 2005-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

  45,780,916  c:\users\raub\appdata\Local
       2,588  c:\users\raub\appdata\LocalLow
     198,212  c:\users\raub\appdata\Roaming
Files:        2424970
Directories:  3898
Size:         47,085,278,621 bytes
Size on disk: 52,935,700,880 bytes

PS C:\Users\raub> \\strongbadia\users\raub\bin\du.exe -l 1 c:\users\raub\appdata\local\
[...]
PS C:\Users\raub> \\strongbadia\users\raub\bin\du.exe -l 1 C:\users\raub\appdata\local\Microsoft\Windows\INetCac
he\\

DU v1.61 - Directory disk usage reporter
Copyright (C) 2005-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

         239  c:\users\raub\appdata\local\microsoft\windows\inetcache\Content.MSO
         878  c:\users\raub\appdata\local\microsoft\windows\inetcache\Content.Outlook
       5,207  c:\users\raub\appdata\local\microsoft\windows\inetcache\Content.Word
      18,852  c:\users\raub\appdata\local\microsoft\windows\inetcache\IE
  39,825,165  c:\users\raub\appdata\local\microsoft\windows\inetcache\Low
           0  c:\users\raub\appdata\local\microsoft\windows\inetcache\Virtualized
           0  c:\users\raub\appdata\local\microsoft\windows\inetcache\WebTempDir
Files:        2410022
Directories:  55
Size:         40,806,752,248 bytes
Size on disk: 46,635,979,024 bytes

PS C:\Users\raub> \\strongbadia\users\raub\bin\du.exe -l 1 C:\users\raub\appdata\local\Microsoft\Windows\INetCache\low\

DU v1.61 - Directory disk usage reporter
Copyright (C) 2005-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

  39,819,877  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\IE
Files:        2408915
Directories:  34
Size:         40,780,969,849 bytes
Size on disk: 46,606,749,696 bytes

PS C:\Users\raub>

That smells like that other nemesis of web browsers: Internet Explorer or Edge. What is inside that dir?

PS C:\Users\raub> ls C:\users\raub\appdata\local\Microsoft\Windows\INetCache\Low\IE\
PS C:\Users\raub> dir C:\users\raub\appdata\local\Microsoft\Windows\INetCache\Low\IE\
PS C:\Users\raub>

WTF? Why can't I see what is inside it? Let me du inside it:

PS C:\Users\raub> \\strongbadia\users\raub\bin\du.exe -l 1 C:\users\raub\appdata\local\Microsoft\Windows\INetCache\low\

DU v1.61 - Directory disk usage reporter
Copyright (C) 2005-2016 Mark Russinovich
Sysinternals - www.sysinternals.com

   1,237,378  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\10EAXOSV
   1,246,204  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\145KIAQM
   1,235,782  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\2F8FLUJD
   1,241,300  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\2VB3GL0K
   1,250,215  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\5BZQD406
   1,244,360  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\5IVFOAV9
[...]
   1,251,347  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\TQ26RPSG
   1,236,991  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\VQH18R7G
   1,240,357  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\VYHW6URF
   1,239,508  c:\users\raub\appdata\local\microsoft\windows\inetcache\low\ie\XYKKE45S
Files:        2408912
Directories:  33
Size:         40,775,554,697 bytes
Size on disk: 46,601,322,496 bytes

PS C:\Users\raub>

Before you ask, I am using a powershell window, where ls and dir behave the same. I came from unix so you can understand which one I prefer. So we have a ton (33) of stupid cache folders that the browser could not be bothered to delete after it quit. Thanks, Microsoft, for not cleaning after itself. Really. And, everything below c:\users\raub\appdata\local\microsoft\windows\inetcache\low is hidden (?). Alright then, off it goes! Command line cares not about hidden paths! Note: get-help can be seen as the powershell equivalent of the Unix/linux man.

PS C:\Users\raub> get-help rm

NAME
    Remove-Item

SYNOPSIS
    Deletes files and folders.


SYNTAX
    Remove-Item [-Confirm] [-Credential ] [-Exclude ] [-Filter ] [-Force] [-Include
    ] -LiteralPath  [-Recurse] [-Stream ] [-UseTransaction] [-WhatIf]
    []

    Remove-Item [-Path]  [-Confirm] [-Credential ] [-Exclude ] [-Filter ]
    [-Force] [-Include ] [-Recurse] [-Stream ] [-UseTransaction] [-WhatIf] []
   
    Remove-Item [-Stream ] []
   

DESCRIPTION
    The Remove-Item cmdlet deletes one or more items. Because it is supported by many providers, it can delete many
    different types of items, including files, folders, registry keys, variables, aliases, and functions.
    In file system drives, the Remove-Item cmdlet deletes files and folders.
   
    If you use the Stream dynamic parameter, it deletes the specified alternate data stream, but does not delete the
    file.
   
    Note: This custom cmdlet help file explains how the Remove-Item cmdlet works in a file system drive. For
    information about the Remove-Item cmdlet in all drives, type "Get-Help Remove-Item -Path $null" or see Remove-Item
    at http://go.microsoft.com/fwlink/?LinkID=113373.
   

RELATED LINKS 
    Online version: http://technet.microsoft.com/library/jj628241(v=wps.630).aspx
    Remove-Item (generic); http://go.microsoft.com/fwlink/?LinkID=113373
    FileSystem Provider
    Clear-Content
    Get-Content
    Get-ChildItem
    Get-Content
    Get-Item
    Remove-Item
    Set-Content
    Test-Path


REMARKS
    To see the examples, type: "get-help Remove-Item -examples".
    For more information, type: "get-help Remove-Item -detailed".
    For technical information, type: "get-help Remove-Item -full".
    For online help, type: "get-help Remove-Item -online"


PS C:\Users\raub> rm -force -recurse C:\users\raub\appdata\local\Microsoft\Windows\INetCache\low\IE

Note that since we are running this from the command line, I did not have to do the usual screenshot Windows blog and articles love so much. I could cut and paste the real output and put it here. I could also have piped it into some other script to use the output for its nefarious uses. FYI, the above command has been running for 38 minutes now and has not finished yet.

So, what I have done above in this real example can be also used with servers since

  1. We are using Microsoft published program.
  2. The program is self-contained and requires no installation.
  3. The program fears no hidden directories.
  4. The program has very small footprint.
  5. The program can be run locally, off a USB, or from a network drive.
If you can put up with not having a cute window with some animation, I think sysinternals' version of du is a nice tiny add-on to a Windows server manager arsenal.

Monday, February 26, 2018

Testing for multiple strings without much clutter using powershell

Yes, this hopefully will be quick, and yes it is powershell, which does not make me feel as dirty as if it was Windows. So hear me out.

I wrote a script a while ago that I needed to look for a pattern inside a string and then do something. In its simplest form, the code could look like this (second line is there to show the entire string):

$the_string = "There are pickles in a jar"
$the_string

if ($the_string -match "pickles")
{
        "Found me pickles"
}
else
{
        "nothing to declare"
}

If you wonder why I am using -match instead of -contains, there is a nice discussion you want to check. At least I learned a lot from it. When we run the script, which is henceforth called switchtest.ps1, we get

PS C:\Users\dalek> powershell -file .\dev\switchtest.ps1
There are pickles in a jar
Found me pickles
PS C:\Users\dalek>

So far so good. But, what if we want to do things based on whether other substrings are in the string? After all, instead of one single string we might be looping over a list of them, or reading a file and doing things based on what we find on a line-by-line-basis. We could add more if statements but that gets nasty quickly:

$the_string = "There are pickles in a jar"
$the_string

if ($the_string -match "pickles")
{
        "Found me pickles"
}

elseif ($the_string -match "there")
{
        "There is here"
}
else
{
        "nothing to declare"
}

Notes:

  • By default powershell is case insensitive; this is consistent with how DOS and Windows behaves, which is exactly the opposite of UNIX in general and Linux specifically.
  • If you have used other programming languages like python or C, you might remember switch statements (switch, then case-this and case-that). And, they are also implemented in powershell without the case word being explicitly used but the behavior is there for all to see.
  • -match looks for the substring anywhere in the string. So, it would find there in:
    • $the_string = "There are pickles in a jar"
    • $the_string = "Are there pickles in a jar?"
    • $the_string = "Pickles are in a jar over there"
Let's now redo switchtest.ps1 using case:
$the_string = "There are pickles in a jar"
$the_string

switch -wildcard ($the_string)
{
        "*pickles*" {"Found me pickles"}
        "there*" {"There is here"}
        default {"nothing to declare"}
}

and then run it

PS C:\Users\dalek> powershell -file .\dev\switchtest.ps1
There are pickles in a jar
Found me pickles
There is here
PS C:\Users\dalek>

The -wildcard option is where the main magic is. It allows me to enter a string as the search pattern, such as "pickles". The more astute of you probably noticed the asterisk (*) surrounding the search string. Without the first, it would only look for a string that starts with "pickles"; Since we only want to look for there when it begins the string we do not need the leading *. There is a thread in the Spiceworks forum showing another example of looking for strings that begin with a, in their case, specific letter using a case statement. The second one is so it will not look only for string ending with that substring.

There is nothing forcing each case statement to be a one-liner. In fact, it would be clearer to write the second one as

// Do something if the string begins with "there" 
        "there*" 
             {
                   "There is here"
             }

specially if we are going to do more than just write out a string.

Another thing you might also have noticed is the behaviour of the output does not match the one we did using the if statement. Reason is unless we specifically tell the switch statement to stop testing once it finds a match, it will keep going down to the list (the default is only done if nothing matches. Sometimes that is exactly what you want to do, but let's assume that is not the case. Just as in other languages, the command we need to give it is break. Let's add them

$the_string = "There are pickles in a jar"
$the_string

switch -wildcard ($the_string)
{
        "*pickles*" {"Found me pickles"; break}
        "there*" {"There is here"; break}
        default {"nothing to declare"}
}

and try it once more

PS C:\Users\dalek> powershell -file .\dev\switchtest.ps1
There are pickles in a jar
Found me pickles 
PS C:\Users\dalek>

Now the output looks just like the original script, but it is much cleaner and easier to expand.

Wednesday, October 25, 2017

Can't find the Windows drive I want to mount a fileshare in

If you have read this blog before, you know I am not a Microsoft Windows fanboy. For those who have not read it before, let's talk about one of its annoying features: the use of drive letters to make fileshares available to the user. I understand the idea of using drive letters... in the 80s personal computers since they had at best two floppy drives or if you were really rich a floppy and a hard drive. During those bad hairstyle times, you only needed some way to differentiate two devices, so why not call them A and B or 0 and 1? But we now live in a time where a lot of people in our planet do not even know what a floppy drive is (hint: it is not a sex toy) and might connect a phone, a portable storage device of some kind, and who knows what will come next? 26 "drives" might run out quickly. Also,
why must we mount a new device/fileshare at the top level ("drive") instead of in a directory inside another fileshare like you do in UNIX (including Linux and OSX)?

The dirty little secret is that you can, but most users who grew up using Windows are so used to drive letters they cannot think of the option. And, there are Windows programs out there which can only handle drive letters. Point is, Microsoft is not to blame. In fact, they have been trying to convince people to use Microsoft (duh!) UNC Paths, which might not follow the path convention used by every other operating system out there but it is a huge improvement from the drive letter thingie. So, credit to where credit is due.

But, this article is not about path conventions and the religion around them. All we want to do is mount a fileshare inside another in windows. Humble goal, yes? Well, as the poem says, "The best laid schemes of mice and men / Often go awry."

I can has mah driv?

There are instructions out there to mount a fileshare without using drive letters in Windows; the one I picked uses the Computer Management GUI thingie, which might be important since coworkers are paralytic fearful of the command line:




Why is it not showing the D: drive? After all, diskpart can see it:

PS C:\Users\raub.adm> diskpart

Microsoft DiskPart version 6.3.9600

Copyright (C) 1999-2013 Microsoft Corporation.
On computer: Srv12R2

DISKPART> list volume

  Volume ###  Ltr  Label        Fs     Type        Size     Status     Info
  ----------  ---  -----------  -----  ----------  -------  ---------  --------
  Volume 0     F   New Volume   NTFS   Simple        99 GB  Healthy
  Volume 1     D   Data         NTFS   Simple      2959 GB  Healthy
  Volume 2     C   Srv12R2      NTFS   Simple        59 GB  Healthy    Boot
  Volume 3         System Rese  NTFS   Simple       350 MB  Healthy    System
  Volume 4     E   Utility      NTFS   Simple        61 GB  Healthy
  Volume 5     Z                       DVD-ROM         0 B  No Media
  Volume 6         Volume       NTFS   Partition   4095 GB  Healthy

DISKPART> 

And I can mount it from diskpart into some folder in the D: drive without a problem.

DISKPART> select volume 6

Volume 6 is the selected volume.

DISKPART> assign mount=d:\tmp

DiskPart successfully assigned the drive letter or mount point.

DISKPART>

I wants 2 c mah driv!

Similar to an issue we talked about in an early article, we have a bad case of access control being too controlling. Specifically, the mount stuff is defined in Drive_Letter:\System Volume Information\spp for each drive and those who need to see it are not. Let me show you what I mean. Here is the E: drive, which we know we can mount a fileshare inside it using the GUI.

PS C:\Users\raub.adm> cacls 'e:\System Volume Information\spp'
e:\System Volume Information\SPP AD\SOD_Domain Admins:(OI)(CI)(ID)F
                                 BUILTIN\Administrators:(OI)(CI)(ID)F
                                 NT AUTHORITY\SYSTEM:(OI)(CI)(ID)F

PS C:\Users\raub.adm>

Now here is the problematic D: drive:

PS C:\Users\raub.adm> cacls 'd:\System Volume Information\spp'
d:\System Volume Information\SPP AD\NETWORK_Domain Admins:(OI)(CI)(ID)F

PS C:\Users\raub.adm>

As you can see, the local admin entities, Administrators and the SYTEM accounts, can't access D:. So let's correct that:

PS C:\Users\raub.adm> cacls 'd:\System Volume Information\spp' /e /g system:f
processed dir: d:\System Volume Information\SPP
PS C:\Users\raub.adm> cacls 'd:\System Volume Information\spp' /e /g administrators:f
processed dir: d:\System Volume Information\SPP
PS C:\Users\raub.adm> cacls 'd:\System Volume Information\spp'
d:\System Volume Information\SPP NT AUTHORITY\SYSTEM:(OI)(CI)F
                                 BUILTIN\Administrators:(OI)(CI)F
                                 AD\NETWORK_Domain Admins:(OI)(CI)(ID)F

PS C:\Users\raub.adm>


Much better! Moral of the story, if you or your computer cannot access a drive in some way or form, do check permissions.

Tuesday, August 15, 2017

Connecting to multiple VPNs using one single Cisco AnyConnect

Like many here, I remote into networks to work. I access organization X's network using Cisco's AnyConnect VPN client because that is what they use. When I first got involved, they told me to login to a given url in their webserver and get the client for my machine (a MacBook Air if you are curious; I do need to get a new Linux laptop but the Mac has been working great so far). Probably if my machine machine was a company-owned laptop they would have pushed the packaged using SCCM/Chocolatey (Windows) or Casper(now called jamf)/Munki (Mac). Or ansible, but that is another bag of cats. In any case, the point is I got their package, which was configured to work on their VPN. And, it works: double-click on the silly link, connect, enter my authentication info, and off I go.

Now also need to access organization B's machines. And they also chose to use AnyConnect. And just like X they also told me to install their package. Thing is if I do that it will wipe the X configuration, which would get annoying very quickly. I did try seeing if there was a way to add another profile from the client's menu but not luck. Maybe each company disabled the option so you can only use it to access their network; I do not know. Now what I could do since this is a Mac is rename Company X's VPN folder to, say, Cisco.Old (the default folder name is Cisco and then install Company B's VPN package.

This way, if I need to go to X, I would open Cisco.Old and then run that vpn client. If I then wanted to go to B, I would quit the client, go to Cisco, and then run that client. I do not know about you, but that looks a bit cumbersome to me. And, if my laptop was running Windows, I think it would not let me install 2 instances of the client that easily. There has to be a better way.

Probulating

First of all, let's assume there is a configuration file somewhere for the AnyConnect VPN client. Since I am using OSX, chances are it has some plist-sounding name. And I found something called com.cisco.Cisco-AnyConnect-Secure-Mobility-Client.plist in my preferences folder, /Users/raub/Library/Preferences, but it does not look particularly legible from the command line (yes, I know there is probably an app to do that but I like to do things from the command line):

bplist00Ñ^A^B]UILogLocation¥^C^D^E^F^G_^PA/Users/raub/.cisco/vpn/log/UIHistory_2017.08.28.23.35.34.010.txt_^PA/Users/dalek/.cisco/vpn/log/UIHistory_2017.08.28.23.53.04.734.txt_^PA/Users/raub/.cisco/vpn/log/UIHistory_2017.08.29.00.10.34.504.txt_^PA/Users/raub/.cisco/vpn/log/UIHistory_2017.08.29.00.28.05.785.txt_^PA/Users/raub/.cisco/vpn/log/UIHistory_2017.10.04.04.44.45.284.txt^@^H^@^K^@^Y^@^_^@c^@§^@ë^A/^@^@^@^@^@^@^B^A^@^@^@^@^@^@^@^H^@^@^@^@^@^@^@^@^@^@^@^@^@^@^As

So we make a copy of it and then run

plutil -convert xml1 com.cisco.Cisco-AnyConnect-Secure-Mobility-Client.plist
to convert it to something more legible, and then look inside it:

boris:~ raub$ cat com.cisco.Cisco-AnyConnect-Secure-Mobility-Client.plist




 UILogLocation
 
  /Users/raub/.cisco/vpn/log/UIHistory_2016.12.12.13.41.31.883.txt
  /Users/raub/.cisco/vpn/log/UIHistory_2016.12.12.13.58.40.264.txt
  /Users/raub/.cisco/vpn/log/UIHistory_2016.12.12.14.15.56.295.txt
  /Users/raub/.cisco/vpn/log/UIHistory_2017.02.14.06.03.40.692.txt
  /Users/raub/.cisco/vpn/log/UIHistory_2017.07.24.21.43.25.742.txt
 


boris:~ raub$

Hmmm, that does not look like what I want. Maybe the AnyConnect client has a global configuration file somewhere. And it does, and it is called glvpn-anyconnect-profile.xml and is located in /opt/cisco/anyconnect/profile/:

boris:~ raub$ ls /opt/cisco/anyconnect/profile/
AnyConnectProfile.xsd  glvpn-anyconnect-profile.xml
boris:~ raub$

If we look into it, this xml file starts as expected with some system-wide config settings

cat /opt/cisco/anyconnect/profile/glvpn-anyconnect-profile.xml
<?xml version="1.0" encoding="UTF-8"?>
<AnyConnectProfile xmlns="http://schemas.xmlsoap.org/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.xmlsoap.org/encoding/ AnyConnectProfile.xsd">
        <ClientInitialization>
                <UseStartBeforeLogon UserControllable="true">false</UseStartBeforeLogon>
                <AutomaticCertSelection UserControllable="true">true</AutomaticCertSelection>
                <ShowPreConnectMessage>false</ShowPreConnectMessage>
                <CertificateStore>All</CertificateStore>
                <CertificateStoreOverride>false</CertificateStoreOverride>
                <ProxySettings>Native</ProxySettings>
                <AllowLocalProxyConnections>true</AllowLocalProxyConnections>
                <AuthenticationTimeout>60</AuthenticationTimeout>
                <AutoConnectOnStart UserControllable="true">false</AutoConnectOnStart>
                <MinimizeOnConnect UserControllable="true">true</MinimizeOnConnect>
                <LocalLanAccess UserControllable="true">true</LocalLanAccess>
                <ClearSmartcardPin UserControllable="true">true</ClearSmartcardPin>
                <IPProtocolSupport>IPv4,IPv6</IPProtocolSupport>
                <AutoReconnect UserControllable="true">true

But then get to the part we have been anxiously waiting for: how to access company X's vpn:

<ServerList>
                <HostEntry>
                        <HostName>Company X VPN</HostName>
                        <HostAddress>vpn.companyx.com</HostAddress>
                </HostEntry>
        </ServerList>
</AnyConnectProfile>

It does not look very complicated to me: we probably could just add a new HostEntry for Company B, as in

<ServerList>
                <HostEntry>
                        <HostName>Company X VPN</HostName>
                        <HostAddress>vpn.companyx.com</HostAddress>
                </HostEntry>
                <HostEntry>
                        <HostName>Company B VPN</HostName>
                        <HostAddress>vpn.b-company.com</HostAddress>
                </HostEntry>
        </ServerList>
</AnyConnectProfile>

and be done. And that will work. But, I think we can do one better; can we avoid cluttering the profile file? Long story short is yes. Just put something like this

cat > B-profile.xml << 'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<AnyConnectProfile xmlns="http://schemas.xmlsoap.org/encoding/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://schemas.xmlsoap.org/encoding/AnyConnectProfile.xsd">
    <!--
        This section contains the list of hosts the user will be able to
        select from.
      -->
    <ServerList>
        <!--
            This is the data needed to attempt a connection to a specific
            host.
          -->
        <HostEntry>
            <!--
                Can be an alias used to refer to the host or an  FQDN or
                IP address.  If an FQDN or IP address is used, a
                HostAddress is not required.
              -->
            <HostName>Company B VPN</HostName>
            <HostAddress>vpn.b-company.com</HostAddress>
        </HostEntry>
    </ServerList>
</AnyConnectProfile>

in /opt/cisco/anyconnect/profile/:

boris:~ raub$ ls /opt/cisco/anyconnect/profile/
AnyConnectProfile.xsd  glvpn-anyconnect-profile.xml
B-profile.xml
boris:~ raub$

Now when we run the client, we can select either company's VPN:

What about Windows

I've never tried but there is a file called (starting at your homedir) .\AppData\Local\Cisco\Cisco AnyConnect Secure Mobility Client\preferences.xml which would be my starting point. The global profile folder is c:\ProgramData\Cisco\Cisco AnyConnect Secure Mobility Client\Profile.

Final thoughts

I do not like that I have to configure the different profiles at the global level; I might share this laptop with other people and would like to have my profiles uncluttered away from theirs. But, at least now I can use multiple profiles to access different networks. Looking at the Windows configuration file, I wonder if I can do that int he Mac too. That will be the subject for another article.

Monday, July 31, 2017

Downloading a single file from github (using ansible perhaps)

I was going to setup Ansible to talk to one of my Windows servers. According to the Ansible page on Windows, the easiest way to install the windows side is to download the powershell script ConfigureRemotingForAnsible.ps1 and blindly run it on the windows server. It supposedly does all the magic to install and set things up. Should I run it? I don't know, but let's try to get it. We can inspect its code once we have the file.

NOTE: Setting up Ansible on windows is not the topic of this thread. All I want to show is a way to download a single file from a github repo.

Since I do not know how to do it, let's do some searching. And then we try each method out and see what's what.

Attempt I

There is a thread in stackoverflow called How to pull a single file from a server repository in Git? which
suggested using the git clone command as in
git clone https://github.com/igniterealtime/Openfire.git \
Openfire/src/java/org/apache/mina/management/MINAStatCollector.java

Let's try it out:

raub@desktop:/tmp/rmoo$ git clone https://github.com/igniterealtime/Openfire.git \
Openfire/src/java/org/apache/mina/management/MINAStatCollector.java
Cloning into 'Openfire/src/java/org/apache/mina/management/MINAStatCollector.java'...
remote: Counting objects: 107450, done.
remote: Compressing objects: 100% (53/53), done.
Receiving objects:  14% (15868/107450), 61.11 MiB | 209.00 KiB/s
[...]
remote: Total 107450 (delta 32), reused 31 (delta 16), pack-reused 107380
Receiving objects: 100% (107450/107450), 802.60 MiB | 8.23 MiB/s, done.
Resolving deltas: 100% (63893/63893), done.
Checking connectivity... done.
raub@desktop:/tmp/rmoo$ ls Openfire/src/java/org/apache/mina/management/MINAStatCollector.java
build/          i18n/     webadmin/     LICENSE.txt  README.md
dbutil/         src/      webadmintld/  Makefile
documentation/  starter/  xmppserver/   pom.xml
raub@desktop:/tmp/rmoo$

Er, does that look like it grabbed the right thing? For some reason I thought the .java file was, well, a file and not a bunch of files and directories. At least I could swear writing .java files in vi so they were text files. Maybe I am wrong, so let's see if we can get the file I really want:

raub@desktop:/tmp/rmoo$ git clone https://github.com/ansible/ansible.git ansible
/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps1
Cloning into 'ansible/blob/devel/examples/scripts/ConfigureRemotingForAnsible.ps
1'...
remote: Counting objects: 236787, done.
remote: Compressing objects: 100% (66/66), done.
remote: Total 236787 (delta 33), reused 25 (delta 6), pack-reused 236712
Receiving objects: 100% (236787/236787), 73.53 MiB | 8.23 MiB/s, done.
Resolving deltas: 100% (152234/152234), done.
Checking connectivity... done.
raub@desktop:/tmp/rmoo$ ls
ansible/
raub@desktop:/tmp/rmoo$ ls ansible/blob/devel/examples/scripts/ConfigureRemotin$ForAnsible.ps1/
ansible-core-sitemap.xml  .gitattributes            RELEASES.txt
bin/                      .github/                  requirements.txt
CHANGELOG.md              .gitignore                ROADMAP.rst
CODING_GUIDELINES.md      .gitmodules               setup.py
contrib/                  hacking/                  shippable.yml
CONTRIBUTING.md           lib/                      test/
COPYING                   .mailmap                  ticket_stubs/
.coveragerc               Makefile                  tox.ini
docs/                     MANIFEST.in               VERSION
docsite_requirements.txt  MODULE_GUIDELINES.md      .yamllint
examples/                 packaging/
.git/                     README.md
raub@desktop:/tmp/rmoo$ ls ansible/blob/devel/examples/scripts/ConfigureRemotin$ForAnsible.ps1/
bin/        test/                     docsite_requirements.txt  ROADMAP.rst
contrib/    ticket_stubs/             Makefile                  setup.py
docs/       ansible-core-sitemap.xml  MANIFEST.in               shippable.yml
examples/   CHANGELOG.md              MODULE_GUIDELINES.md      tox.ini
hacking/    CODING_GUIDELINES.md      README.md                 VERSION
lib/        CONTRIBUTING.md           RELEASES.txt
packaging/  COPYING                   requirements.txt
raub@desktop:/tmp/rmoo$

I do not know about you, but that not look like what I really wanted: a single file. On a second thought, that sure looks like the root for the ansible git repo:

I don't know about you but the files and directories sure look familiar.

I guess it is time to try something else.

Attempt II

Let's try something else: in StackOverflow there is a thread called Retrieve a single file from a repository, which suggests

git clone --no-checkout --depth 1 git@github.com:foo/bar.git && cd bar && git show HEAD:path/to/file.txt

For this attempt, we will try to get the file ConfigureRemotingForAnsible.ps1 I want:

git clone --no-checkout --depth 1 https://github.com/ansible/ansible.git  && cd ansible && \
git show HEAD:examples/scripts/ConfigureRemotingForAnsible.ps1

Thing is that will just spit the file to the screen, literally:

raub@desktop:/tmp/rmoo$ git clone --no-checkout --depth 1 https://github.com/ansible/ansible.git  && \
cd ansible && git show HEAD:examples/scripts/ConfigureRemotingForAnsible.ps1
Cloning into 'ansible'...
remote: Counting objects: 5873, done.
remote: Compressing objects: 100% (4282/4282), done.
remote: Total 5873 (delta 962), reused 3652 (delta 660), pack-reused 0
Receiving objects: 100% (5873/5873), 7.13 MiB | 8.09 MiB/s, done.
Resolving deltas: 100% (962/962), done.
Checking connectivity... done.
#Requires -Version 3.0

# Configure a Windows host for remote management with Ansible
# -----------------------------------------------------------
#
# This script checks the current WinRM (PS Remoting) configuration and makes
# the necessary changes to allow Ansible to connect, authenticate and
# execute PowerShell commands.
#
[...]

We could improve that by saving the file into a file. But how? The quickest solution I can think of is to pipe it to a file:

file="ConfigureRemotingForAnsible.ps1" ; git clone --no-checkout --depth 1 https://github.com/ansible/ansible.git  && cd ansible && $(git show HEAD:examples/scripts/$file > $file)

will put $file inside the ansible dir:

raub@desktop:/tmp/rmoo/ansible$ ls
ConfigureRemotingForAnsible.ps1
raub@desktop:/tmp/rmoo/ansible$

Of course we can do better, like placing it on the original pwd and delete the (now temporary) ansible dir. Something like

file="ConfigureRemotingForAnsible.ps1" ; git clone --no-checkout --depth 1 https://github.com/ansible/ansible.git  && cd ansible && $(git show HEAD:examples/scripts/$file > ../$file) && cd .. && rm -rf ansible

should do just fine. But you do not have to believe on me; here's it in action:

raub@desktop:/tmp/rmoo$ file="ConfigureRemotingForAnsible.ps1" ; \
git clone --no-checkout --depth 1 https://github.com/ansible/ansible.git  && \
cd ansible && $(git show HEAD:examples/scripts/$file > ../$file) && cd .. && rm -rf ansible
Cloning into 'ansible'...
remote: Counting objects: 5873, done.
remote: Compressing objects: 100% (4282/4282), done.
remote: Total 5873 (delta 962), reused 3652 (delta 660), pack-reused 0
Receiving objects: 100% (5873/5873), 7.13 MiB | 758.00 KiB/s, done.
Resolving deltas: 100% (962/962), done.
Checking connectivity... done.
raub@desktop:/tmp/rmoo$ ls
ConfigureRemotingForAnsible.ps1
raub@desktop:/tmp/rmoo$

Just to show this is not an accident, let's validate it by applying it to get the java source file we tried to get earlier.

raub@desktop:/tmp/rmoo$ file="MINAStatCollector.java" ; git clone --no-checkout --depth 1 https://github.com/igniterealtime/Openfire.git  && cd Openfire && $(git show HEAD:src/java/org/apache/mina/management/$file > $file)
Cloning into 'Openfire'...
remote: Counting objects: 5291, done.
remote: Compressing objects: 100% (4433/4433), done.
remote: Total 5291 (delta 807), reused 3394 (delta 479), pack-reused 0
Receiving objects: 100% (5291/5291), 92.80 MiB | 8.24 MiB/s, done.
Resolving deltas: 100% (807/807), done.
Checking connectivity... done.
raub@desktop:/tmp/rmoo/Openfire/Openfire$ head MINAStatCollector.java
package org.apache.mina.management;

import static org.jivesoftware.openfire.spi.ConnectionManagerImpl.EXECUTOR_FILTER_NAME;

import org.apache.mina.core.service.IoService;
import org.apache.mina.core.service.IoServiceListener;
import org.apache.mina.core.session.IdleStatus;
import org.apache.mina.core.session.IoSession;
import org.apache.mina.filter.executor.ExecutorFilter;
import org.apache.mina.filter.executor.OrderedThreadPoolExecutor;
raub@desktop:/tmp/rmoo/Openfire$

I think we can improve it by making it more generic, which might be the subject of another post. Or something; I've been dragging on finishing this article for a few weeks now so I just want it gone.

Now what about something completely Ansible-ly?

Er, this Article is getting way longer than I originally planned. I will put the ansible side on another. How about that for raising your expectations and then expertly crushing them?

Friday, June 30, 2017

Tivoli (TSM) Backup can't backup a drive in Windows

Over here we use IBM's TSM backup system. I do not want to go over its features and setup, but the bottom line is that I get an email listing the backup status for each machine (known as nodes in TSM lingo) I am backing up. And one day one of those nodes barked:

backup7x SERVER02.EXAMPLE           Failed***    12     2017-06-22 00:00:00 2017-06-22 
00:01:06 2017-06-22 00:01:07

If you are curious about the 12, here is what it means right out of that very same email (I copied that session including the wasteful blank lines):

Result:

0 - Success.

1 - See explanation for 'Missed'.

4 - The operation completed successfully, but some files were not
processed.

8 - The operation completed with at least one warning message.

12 - The operation completed with at least one error message
(except for error messages for skipped files).

That does not help me much. You see, I like to have access to logs and not sad face cryptic messages. So I went to C:\Program Files\Tivoli\TSM\baclient to look into dsmsched.log for any funny business. And funny business I found:

06/22/2017 00:01:09 --- SCHEDULEREC OBJECT BEGIN D-0000AM 06/22/2017 00:00:00
06/22/2017 00:01:10 Incremental backup of volume '\\server02\d$'
06/22/2017 00:01:11 ANS1228E Sending of object '\\server02\d$' failed.
06/22/2017 00:01:11 ANS1751E Error processing '\\server02\d$': The file system can not 
be accessed.
06/22/2017 00:01:11 --- SCHEDULEREC STATUS BEGIN
06/22/2017 00:01:11 --- SCHEDULEREC OBJECT END D-0000AM 06/22/2017 00:00:00
06/22/2017 00:01:11 ANS1512E Scheduled event 'D-0000AM' failed.  Return code = 12.
06/22/2017 00:01:11 Sending results for scheduled event 'D-0000AM'.
06/22/2017 00:01:11 Results sent to server for scheduled event 'D-0000AM'.

Ok, what's so special about the D drive? I looked at the config file, C:\Program Files\Tivoli\TSM\baclient\dsm.opt, and it seems to be right. If you do not believe me (I wouldn't and I have to live with me), here are its first few lines:

NODENAME SERVER02.EXAMPLE
TCPSERVERADDRESS backup7x.example.com

DOMAIN "\\server2\d$"
MANAGEDSERVICES WEBCLIENT SCHEDULE
webports 1501 1581

txnbytelimit 25600
schedmode prompted
schedlogretent 30,d
errorlogretent 30,d
passwordaccess generate
quiet
tapeprompt no


EXCLUDE.BACKUP "*:\Thumbs.db"
EXCLUDE.BACKUP "*:\desktop.ini"
EXCLUDE.BACKUP "*:\*.tmp"
EXCLUDE.BACKUP "*:\...\Scans\mpcache-*"
EXCLUDE.BACKUP "*:\microsoft uam volume\...\*"
EXCLUDE.BACKUP "*:\microsoft uam volume\...\*.*"
EXCLUDE.BACKUP "*:\...\EA DATA. SF"
EXCLUDE.BACKUP "*:\IBMBIO.COM"
EXCLUDE.BACKUP "*:\IBMDOS.COM"
EXCLUDE.BACKUP "*:\IO.SYS"
[...]

As you can see, I am telling it to only backup the D drive. Maybe we should take a look at this drive and see who can access it:

C:\Users\raub> icacls d:\
d:\ AD\EXAMPLE_Domain Admins:(OI)(CI)(F)
    AD\EXAMPLE_Users:(RX)

Successfully processed 1 files; Failed processing 0 files
C:\Users\raub>
Where:
  • OI: Object inherit
  • CI: Container inherit
  • F: Full access
  • RX: Read and execute

We can also do that through powershell:

PS C:\Users\raub> get-acl d:\ | fl


Path   : Microsoft.PowerShell.Core\FileSystem::D:\
Owner  : BUILTIN\Administrators
Group  : AD\Domain Users
Access : AD\EXAMPLE_Domain Admins Allow  FullControl
         AD\EXAMPLE_Users Allow  ReadAndExecute, Synchronize
Audit  :
Sddl   : O:BAG:DUD:PAI(A;OICI;0x1200a9;;;SY)(A;OICI;FA;;;S-1-5-21-344340502-4252695000-2390403120-1439459)(A;;0x1200a9;
         ;;S-1-5-21-344340502-4252695000-2390403120-1439468)(A;OICI;FA;;;S-1-5-21-344340502-4252695000-2390403120-14759
         66)



PS C:\Users\raub>

which as you can see is a more verbose way to say the same thing. But what is missing here? You see, by default Windows services are run by the system user (it's full name is NT AUTHORITY\SYSTEM. So let's add it. Does it need to write to the drive as far as TSM is concerned? We are backing up here. Maybe if we need to restore we might need to write but we will cross that bridge when we get to it (hopefully never).

You can add that user and setup the permissions (I did read-execute; but wonder if read only would suffice. Let me know if you find the answer) either using the windows explorer, icacls, or Set-Acl. Pick one; what really matters is that in the end of the day you should have something like this:

C:\Users\raub> icacls d:\
d:\ AD\EXAMPLE_Domain Admins:(OI)(CI)(F)
    AD\EXAMPLE_Users:(RX)
    NT AUTHORITY\SYSTEM:(OI)(CI)(RX)

Successfully processed 1 files; Failed processing 0 files
C:\Users\raub>
or in powershell,

PS C:\Users\raub> get-acl d:\ | fl


Path   : Microsoft.PowerShell.Core\FileSystem::D:\
Owner  : BUILTIN\Administrators
Group  : AD\Domain Users
Access : NT AUTHORITY\SYSTEM Allow  ReadAndExecute, Synchronize
         AD\EXAMPLE_Domain Admins Allow  FullControl
         AD\EXAMPLE_Users Allow  ReadAndExecute, Synchronize
Audit  :
Sddl   : O:BAG:DUD:PAI(A;OICI;0x1200a9;;;SY)(A;OICI;FA;;;S-1-5-21-344340502-4252695000-2390403120-1439459)(A;;0x1200a9;
         ;;S-1-5-21-344340502-4252695000-2390403120-1439468)(A;OICI;FA;;;S-1-5-21-344340502-4252695000-2390403120-14759
         66)



PS C:\Users\raub>

And now I get an email saying all is well:

backup7x SERVER02.EXAMPLE           Completed    0      2017-06-24 00:00:00 2017-06-24 
00:00:54 2017-06-24 01:07:57

Some of you noticed this status email is from 2 days later. The reason was that on the 23rd it was catching up and that took quite a while.

Saturday, April 29, 2017

Network packet capturing in Windows without extra programs


One of the things that separate the Linux from Windows is that

When you want to take a look at what is happening on the network, you want to listen to the wire. In Linux you can run tcpdump, wireshark (GUI) and tshark (console), or even wiping up a script in python or bash. So, it can be done with something that comes with the OS by default (most of distros come with python and bash and a lot also have tcpdump) or can be easily added (wireshark).

Then we have Windows... it better be since the title of this article hints that it might be involved. Common sense and standard practices dictate that if you want to do packet capture in that OS, you should buy or download a program/app such as (surprise!) wireshark or something that was created specifically for Windows. Which is fine... unless you are running in a server. Ask yourself: why should we install and run wireshark in a Web Server? And probably leave it there in case we might need it again, so someone can have it ready to go after breaking into the system (this is related to my pet peeve about developing or at least leaving development software on production servers in general and web servers specifically). Or worse: search the web and download a suspicious packet capture app because it had "EZ" on its name and a cute turtle as its logo? That smells like a security risk besides adding weight to your server; ideally you should only have the packages and programs you need.

That looks like a bit of a drag. It would be really nice if we could do packet capturing in Windows without needing to install yet another program. Perhaps even using what is built-in the host.

One can dream...

Starting the capture

So, let's see how to do it then. The command we want is netsh trace, which will need to be run with escalated privileges because it is accessing the network port. Here is how we would capture everything and save it to the file pickles.etl:

netsh trace start persistent=yes capture=yes tracefile=pickles.etl

There are a few useful options we might want to know:

  • maxsize : max size of log file before it gets overwritten
    • maxsize=250 MB is the default
    • maxsize = 0 unlimited
    NOTE: if you use this option you also need to add the option filemode or it will not work.
    filemode={circular|append|single}
    Ex:
    netsh trace start persistent=yes capture=yes tracefile=stuff maxsize=0  filemode=append
  • persistent : Keep on logging after a reboot
    persistent = no (default)

Stopping capturing

netsh trace stop
Correlating traces ... done
Merging traces ... done
Generating data collection ...

Don't look at me like that; you guessed what the stop command was while I was typing this. Anyway, we end up with two documents:

Mode                LastWriteTime         Length Name
----                -------------         ------ ----
-a----        4/24/2017   3:59 PM         650033 pickles.cab
-a----        4/24/2017   3:58 PM         524288 pickles.etl

But for now we only care about the .etl one.

Converting file to something less proprietary

Note:I deleted my original capture after I posted this and forgot to put screen captures. So I had to add the images later.

Unfortunately, this time we will need to download something called Microsoft Message Analyzer. On my defense, it is (currently) a free Microsoft product. You will need to install it as admin because otherwise it will not allow everyone on the machine to run it as the error message states:

Thing is, I would rather have only me able to run it but I am not given an option as implied in the above image. But I digress.

So do install it and then run it. It will take some time to load everything up and be ready for business.

The way MessageAnayzer shows packets is different than Wireshark, which does not mean it is bad. But all I want is to convert it, so we open the file pickles.etl.

As I said, it does look different than wireshark. But I know wireshark better so let's do some exporting: Hit File->Save As and you then will be able to export it:

Save it as a .pcap file and then wireshark will be happy. Yes I know it required to install an extra program in the end but this can be done in our desktop, not on the machine we did the packet acquisition. Would you agree we have a working solution that met our requirements?

Monday, February 27, 2017

Create output filename based on input filename using powershell

Here's a situation that happened to me many times in Linux: let's say we create a script which expects the user to enter the input and output filenames. Now, what if the user forgets to enter the output filename? Should we bark or come up with a filename based on the input filename? This of course depends on the situation, but when I decided to create the output filename I would tack today's date to the input filename so they would be different.

But that was Linux and bash and python and this is Windows with powershell. And, yes, we could keep on writing in Bash using cygwin, but that owuld be cheating. Give the constraint of only running what came in Windows 7 and above (I am dating myself), let's see what we can do:

  1. Date. The date formats I like are (using Linux here, so focus on the output not the command)

    raub@desktop:~$ date +%F
    2017-01-30
    raub@desktop:~$ 
    and
    raub@desktop:~$ date +%Y%m%d
    20170130
    raub@desktop:~$ 

    Both write year using 4 digits followed by 2 digits for the month and two for the day. I know some people will cry and moan and demand the traditional US format, day/month/year, but the format I like makes sorting in a directory much easier as we are putting what changes the fastest on the end of the filename. But we are talking about powershell, not Bash or Bourne shell. That is true but now we know what we want to accomplish.

    To make it easier, I will pick one of the two formats -- YYYYMMDD -- and run with it; you can later modify the code to use the other one as exercise. The equivalent in powershell is:

    PS C:\Users\raub> get-date -format "yyyyMMdd"
    20170130
    PS C:\Users\raub> 

    Looks just like what we did above in Linux.

  2. Create filename if not given. We are reading the input filename into the script in some way or fashion. How we are doing it depends on the script and whether we should be passing options with or without flags to identify them. For now, we are going to be lazy and do the simplest thing possible: using param() on the beginning of the script.

    param($inputFile, $outputFile)

    If we have just one argument, it shall be the inputfile. If two, the second one is the outputfile. What if no arguments are passed? We can just send an error message and get out.

    function Usage
    {
       Write-Host "Usage:", $MyInvocation.MyCommand.Name, "inputfile [outputfile]"
       exit
    }
    
    if (!$inputfile)
    {
       Usage
    }

    The $MyInvocation.MyCommand.Name is a lazy way to for the script to get its own name by itself.

  3. Do something if there is no $outputFile. This is a variation of the same test we did to see if we had an $inputFile:

    function LazyOutputFilename($ifile)
    {
       $ofile = (Get-Item $ifile ).DirectoryName + '\' +  `
                (Get-Item $ifile ).BaseName + `
                '_' + (get-date -format "yyyyMMdd") + `
                (Get-Item $ifile ).Extension
       return $ofile
    }
    
    function GetOutputFilename($ifile, $ofile)
    {
       # $ofile cannot be $ifile
       # Create a $ofile if one was not given
       if (( [string]::IsNullOrEmpty($ofile) ) -or ( $ofile -eq $ifile ))
       {  
          $ofile = LazyOutputFilename $ifile
       }
    
       return $ofile
    }
    
    $outputFile = GetOutputFilename $inputFile $outputFile
    • In LazyOutputFilename() we are creating the output filename. We are putting it in the same directory as the input filename and then adding the formatted date right before the file extension.

    • The ( [string]::IsNullOrEmpty($ofile) ) checks is the output file, called $ofile inside this function, is empty. The reason we also wants to make sure the output file is not the input file is because we might be reading the input file a chunk at a time (line by line if text) so the script can handle large files without using up all the memory. If we are reading it line by line and then write right back to it, bad things might happen.

    • And, yes, we are overwriting the output filename if it gets changed in GetOutputFilename().

  4. Put everyone together.

    param($inputFile, $outputFile)
    
    function Usage
    {
       Write-Host "Usage:", $MyInvocation.MyCommand.Name, "inputfile [outputfile]"
       exit
    }
    
    <#
     Create output filename based on the full path of the input filename +
     today's date appended somewhere
     #>
    function LazyOutputFilename($ifile)
    {
       $ofile = (Get-Item $ifile ).DirectoryName + '\' +  `
                (Get-Item $ifile ).BaseName + `
                '_' + (get-date -format "yyyyMMdd") + `
                (Get-Item $ifile ).Extension
       return $ofile
    }
    
    function GetOutputFilename($ifile, $ofile)
    {
       # $ofile cannot be $ifile
       # Create a $ofile if one was not given
       if (( [string]::IsNullOrEmpty($ofile) ) -or ( $ofile -eq $ifile ))
       {  
          $ofile = LazyOutputFilename $ifile
       }
    
       return $ofile
    }
    
    if (!$inputfile)
    {
       Usage
    }
    
    $outputFile = GetOutputFilename $inputFile $outputFile