Thursday, February 17, 2011

How to create a reverse HTTP(s) failover web proxy using nginx & heartbeat

Update: There is a Virtual Machine Image available at the bottom of this post
Currently we use a pair of very expensive F5 load balancers to manage our highly available SaaS application, providing SSL offloading and round robin load balanacing with failover in the event one of the SaaS nodes fails.  I decided I wanted to see if I could replace all of the functionality we use from those F5's with a custom built (and free) solution.  I spoke to my coworker JW who works on the SaaS app and has recently been working with other vendors such as Barracuda to find a cheaper device (Our F5's are aging) to replace the F5's and got a list of requirements from him so I would know exactly what we needed.  I spent some time researching and came across nginx which seemed like it would be a suitable package as it provided web proxying, SSL offloading, failover, and sticky sessions (but only in more recent builds).  It took me about half an hour to create a proof of concept with simple web HTTP traffic and another hour to get SSL working and refine/test the configuration.  These are instructions on how to create a set of failover load balancer devices using heartbeat and nginx on Debian Lenny.  If you don't need the heartbeat failover cluster then skip all of the instructions that involve heartbeat.  Configuration for different linux platforms should be similar as well.

Start with a base install of Debian (two if you want clustering)
Configure the network interface with a static IP Address
create a static ip address:
/etc/network/interfaces:
allow-hotplug eth0
iface eth0 inet static
address 192.168.11.250
netamsk 255.255.255.0
gateway 192.168.11.1




Configure Apt Package Manager

The version of nginx we need is in the testing tree, so we need to configure apt by adding these lines to sources.list.
/etc/apt/sources.list:
deb http://debian.osuosl.org/debian testing main contrib non-free
deb-src http://debian.osuosl.org/debian testing main contrib non-free

In order to tell apt to install nginx from the testing tree we hard code the system to use the testing tree for nginx.
/etc/apt/preferences:
Package: *
Pin: release a=stable
Pin-Priority: 900

Package: nginx
Pin: release a=testing
Pin-Priority : 1000

Install Packages

apt-get update
apt-get install heartbeat nginx

Configure Heartbeat
On each server you need to modify ha.cf to look like this.  On node one you put the IP of node two in OtherNodeStaticIP, and on node two you use node 1's ip for OtherNodeStaticIP.  


/etc/ha.d/ha.cf
udpport 694
bcast eth0
mcast eth0 225.0.0.1 694 1 0
ucast eth0 <OtherNodeStaticIP>
node NODENAME1
node NODENAME2
auto_failback off
logfacility local0

/etc/ha.d/haresources
NODENAME1 CLUSTERIP nginx
cp /etc/init.d/nginx /etc/ha.d/resource.d/nginx




Certificates
If you need reverse SSL support to provide SSL encryption for a web service that is not SSL, or you would like to offload the SSL process from your webserver to another device, you will need to configure SSL certificates.  I created a new folder, /etc/nginx/sslcerts and did all of my work in there.

We use the entrust certification authority, so we got the certificate and key file for the certificate we wanted and put it in the /etc/nginx/sslcerts directory.  Additionally, you need to concatenate the middle certificate from your Certificate Signing Authority to teh end of your .crt file.  

copy your crt and key to /etc/nginx/sslcerts/CERTIFICATE.key/crt
cat EntrustMiddleCert.crt >> /etc/nginx/sslcerts/CERTIFICATE.crt


Modify /etc/ha.d/authkeys on both boxes:

auth 2
2 sha1 SOMETHING_UNIQUE


Configure nginx
In our configuration we have 2 clusters, one for the main site, one for reporting, and a final server that hosts a simple page saying the site is down.  This configuration provides all of these features, and in the event that both nodes of the cluster are down the user will get the error page defined instead of a 404 error or something similar.  This configuration example includes HTTP & HTTPS reverse proxy configurations, although you may or may not want to provide both.  Edit to your liking.

/etc/nginx/sites-enabled/default
upstream  SITENAME_cluster  {
  ip_hash; #Sticky sessions
  server   10.32.41.230:20780          max_fails=3  fail_timeout=31s;
  server   10.32.41.231:20780          max_fails=3  fail_timeout=31s;
}
upstream  inetsoft  {
  ip_hash; #Sticky sessions
  server   10.32.41.113:8080          max_fails=3  fail_timeout=31s;
  server   10.32.41.114:8080          max_fails=3  fail_timeout=31s;
}

#the server that servers our notice that the application is unavailable
upstream  errorpage  {
  server   status.localdomain.com          max_fails=3  fail_timeout=31s;
}


#If you want to redirect all HTTP traffic to the HTTPS site, use this instead of  following the port 80 declaration below
#server {
#        listen   80;
#        server_name  SITENAME;
#        rewrite ^(.*) https://$host$1 permanent;
#}


server {
        listen   80;
        server_name  SITENAME;

        location /Reporting {
                proxy_pass http://inetsoft$request_uri;
        }

        location / {
                proxy_pass  http://SITENAME_cluster$request_uri;
                proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

                ### Set headers ####
                proxy_set_header X-Real-IP $remote_addr;
        }

        error_page 502 /error.html;
        location /error.html
        {
                proxy_pass http://errorpage/error.php?Novell;
                proxy_set_header X-Real-IP $remote_addr;
        }
}

server {
        listen   443;
        server_name  SITENAME;

        ssl  on;
        ssl_certificate  /etc/nginx/sslcerts/CERTIFICATE.crt;
        ssl_certificate_key  /etc/nginx/sslcerts/ CERTIFICATE.key;

        ssl_session_timeout  90m;

        ssl_protocols  SSLv2 SSLv3 TLSv1;
        ssl_ciphers  ALL:!ADH:!EXPORT56:RC4+RSA:+HIGH:+MEDIUM:+LOW:+SSLv2:+EXP;
        ssl_prefer_server_ciphers   on;

        location /Reporting {
                proxy_pass http://inetsoft$request_uri;
        }

        location / {
                proxy_pass  http://SITENAME_cluster$request_uri;
                proxy_next_upstream error timeout invalid_header http_500 http_502 http_503;

                ### Set headers ####
                proxy_set_header X-Real-IP $remote_addr;
        }

        error_page 502 /error.html;
        location /error.html
        {
                proxy_pass http://errorpage/error.php?SITENAME;
                proxy_set_header X-Real-IP $remote_addr;
        }
}





You will need to make sure you keep your configurations the same on both servers, using a tool like rsync can automate this process for you but for the purposes of this tutorial I'm going to leave it up to you to decide how to keep your nodes in sync.

Test your nginx configuration for errors, /etc/init.d/nginx start.  If you get any error messages be sure to resolve those, the http://wiki.nginx.org site was very useful for me.  If everything works properly you are good.  If you are using clustering, now you need to start and test your Linux HA cluster.
/etc/init.d/heartbeat start

Congratuations, you have a Reverse HTTP(s) web proxy with failover clustering, and it didn't cost you any body parts.

Wednesday, February 9, 2011

Asterisk with Exchange 2010 SP1 Unified Messaging

Today I decided I wanted to play with using Microsoft Exchange Server Unified Messaging for voicemail in our Digium Asterisk 1.6.2 deployment.  Already having an Exchange 2010 Deployment all I needed to do was install the UM Component in our Exchange infrastructure and configure everything.

Unified messaging provides voicemail transcription support and automatically delivers the voicemail to the users mailbox with as much information about the caller as possible.  If it is another Unified Messaging enabled user it will include all of their contact info based on their callerid. For example when I call somebody from extension 1593 in asterisk and leave another UM user a voicemail, and I have 1593 configured in UM, they will get my cell phone number, email address, and title along with the transcription of the email and an MP3 of the voicemail.  Slick.

One thing to note is the transcription uses a fair amount of CPU power, it took about 30 seconds for it to transcribe a 30 second voicemail, while fully utilizing a core of a Xeon E5430 processor and 250 megabytes of memory.  If you have a lot of voicemail messages being recorded you may need to watch your system utilization closely.

Install Unified Messaging Component
  • UM Requires the desktop experience pack in Windows Server 2008 R2, so I installed that and rebooted
  • Next, you need to have the Microsoft Server Speech Platform Runtime (X64) installed.  If you run the installer it will tell you to install it, and provides you this link to download and install it.  You must use this link, if you just search for and download the platform run time you will not get the right version and the check will still fail.
  • Finally, if the Windows Firewall service is disabled the install will fail with the following message so make sure the service is set to automatic or manual start.  Thanks to this blog post for helping me figure this one out:
    • The following error was generated when "$error.Clear(); install-UMService " was run: "There are no more endpoints available from the endpoint mapper. (Exception from HRESULT: 0x800706D9)".

    • There are no more endpoints available from the endpoint mapper. (Exception from HRESULT: 0x800706D9)
    • Click here for help... http://technet.microsoft.com/en-US/library/ms.exch.err.default(EXCHG.141).aspx?v=14.1.267.0&e=ms.exch.err.Ex88D115&l=0&cl=cp
  • Once you have the prerequisites installed you can install the UM component using your installation media.
  • Once the installation is complete you need to configure UM.
Configure Unified Messaging
  • First you need to create a Dial Plan
  • We use 4 digit extensions and we are in the United States, so I configured the settings accordingly.

  • Now create a UM IP Gateway to point at your Asterisk server, entering the IP Address of your Asterisk box and selecting the Dial Plan you just created.
  • Next I configured the UM Mailbox Policies to use 4 digit pin length instead of the default 6.
  • Finally I configured my Auto Attendant with an extension to provide directory lookups, etc.
Configure Asterisk
  • Next configure your Asterisk server.  You need to add a SIP Peer to sip.conf:
    • [exchangeum]
    • host=192.168.11.31 <-- IP of your exchange server w/ UM Installed
    • type=friend
    • insecure=very
    • transport=tcp
    • port=5065
    • context=from-ocs <-- Context you want calls that come from exchange to go to.  I pointed it at the same context we use for our OCS/Lync deployment
  • Configure your dial plan to use Unified Messaging for voicemail instead of Asterisk's Voicemail() App.
  • This dialplan will call my phone whenever somebody dials 1593, and if I don't answer on my desk phone, or on Lync (OCS_TRUNK_R2) it will send the call to my unified messaging mailbox.
    • exten => 1593,1,Dial(SIP/1593&SIP/OCS_TRUNK_R2/+2593,25)
    • exten => 1593,n,SipAddHeader(Diversion:<tel:1593>)
    • exten => 1593,n,Dial(SIP/1593@exchangeum)
    • exten => 1593,n,Hangup
Configure Users
  • You can manually configure one user at a time in the Exchange Console, or you can use a powershell script to do it in bulk. I found a useful tool available here to allow you to bulk add users to Unified Messaging.  
  • I modified his tool a little bit to make it simplify deployment, making it take username,extension in a CSV delimited file.  
  • You can see the sample CSV here, and download the modified powershell script here

Tuesday, January 18, 2011

Microsoft Lync Server 2010 Integration with Digium Asterisk

We have been using Microsoft Office Communications Server for a long time now, and have recently begun testing Lync.  It took mere minutes for me to get Lync working with Asterisk 1.6.2.  The same steps work for Asterisk 1.8 as well, and for the most part are the same in trixbox, freepbx, elastix, etc.

Asterisk Configuration
First, I configured my Asterisk installation, you have to enabled TCP SIP, and create a peer.  I added the following to the sip.conf general section:

[general]
tcpbindaddr=0.0.0.0
tcpenable=yes

and then I added my peer for Lync


[LYNC_TRUNK]
type=peer
host=192.168.11.4
qualify=no
transport=tcp,udp
canreinvite=no
port=5068
disallow=all
allow=ulaw
context=from-ocs

I used the context from-ocs, so I set up a basic context in extensions.conf, cutting off the + that comes through and sending the call to the normal internal call context for normal call handling:
[from-ocs]
exten => _+1XXX,1,Goto(internal-call,${EXTEN:1},1)
exten => _+1XXXXXXXXXX,1,Dial(SIP/${EXTEN}@OUTBOUND_PROVIDER,30)

You will need to make a test extension in your normal dial context to test calling in to Lync, something like this should work
exten => 1000,1,Dial(SIP/+2593@LYNC_TRUNK,30)

After restarting asterisk that side of things should be configured, now just to make sure you have Lync configured.

Lync Configuration (Standard Edition)
  1. Open the Lync 2010 Topology Builder
  2. Edit the properties of your standard edition pool
  3. Install the mediation server, I used the Collocated option because load is low enough it doesn't need a dedicated server.
  4. Under mediation server take note of the TCP Listening port, as that is the port you need to specify in sip.conf of Asterisk.  It defaults to 5068 so that's what I used.
  5. At the bottom find the section "The following gateways are associated with this mediation server." 
  6. Click New, and enter the IP address of your Asterisk server, and the port you use for Asterisk TCP SIP (5060 by default).
  7. Note that mine has a red X saying i already have this address configured.  This is because I was redoing the steps for this tutorial.  Also note I have two gateways added as my Asterisk server has multiple IP Addresses and you must make sure to enter all addresses in the gateway list.

  8. Once this is done make sure to publish your topology and re-run setup if you did not previously have the mediation service installed.  I restarted my entire Lync server at this point but if you'd prefer you should be able to restart the Mediation service to apply the new settings.
  9. Open your Lync control panel, go to users, edit your test user and enable Telephony for Enterprise Voice.
  10. We use 2XXX range extensions in Lync, and 1XXX range extensions in Asterisk, so you will see tel:+2593, and ext=1593.  The ext=1593 is for PSTN Conferencing support so Lync can see me
    call from 1593 and know I am Andrew, and automatically authenticate me in to my own conferences..
  11. Configure Voice Routing -> Dial Plan.  These are the rules I wrote in OCS 2007 R2 and imported using import-cslegacyconfiguration.  You will need to tweak them for your requirements.
  12. At this point you should be able to make/receive calls to and from Lync/Asterisk.

Exchange 2010 Backup Failure with Data Protection Manager 2010

About a month ago we created a new mailbox store on our Exchange 2010 SP1 server, and last night I started getting alarms that the server was running low on disk space on the drive that contains the transaction logs.  This happened because the new store was not automatically picked up by DPM 2010, and the transaction logs had built up over time eating away at the available disk space.  My fault for forgetting to verify DPM had the store being backed up.

I went to add it in DPM and got the following error upon expanding the exchange server to add the new database store:
DPM could not enumerate application component Microsoft Exchange Server\Microsoft Information Store\csgtacex1\17a465cc-90ca-4abd-927f-9aed49f33b5e on protected computer csgtacex1.domain.com. (ID: 964)

Please make sure that writer is in good state.


I discovered a VSS error on my exchange server which I fixed by cleaning up the ProfileList entry that had .bak attached to the end of it.  This is something i've seen break on Windows Vista machines recently, so I found it interesting that it started causing problems on an Exchange server.
To clean up the entries with .bak open regedit, and navigate to HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\ProfileList\ and remove any keys that have .bak appended to them.  For good measure I made a backup before deleting the key.  After rebooting the VSS errors were gone, but DPM still didn't work.

I googled the DPM specific error and found this post on technet about the same error.  My system had everything working in vssadmin list writers, so further down Frans posts about missing registry keys for powershell.  This got me curious because I had noticed since upgrading to Exchange 2010 Service Pack 1 that the power shell had disappeared.  I immediately became suspicious.  His solution was to export the missing keys from another server that had them, and then import them on the missing server.  I had another server with the keys there, made an export, imported it, and DPM immediately worked.  If you would like, this is the .reg file I exported.  Save this to a .reg and import it and you should be good to go.



Windows Registry Editor Version 5.00


[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellSnapIns]
@=""


[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellSnapIns\Microsoft.Exchange.Management.PowerShell.E2010]
"CustomPSSnapInType"="Microsoft.Exchange.Management.PowerShell.AdminPSSnapIn"
"ApplicationBase"="C:\\Program Files\\Microsoft\\Exchange Server\\V14\\bin"
"AssemblyName"="Microsoft.Exchange.PowerShell.Configuration, Version=14.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
"Description"="Admin Tasks for the Exchange Server"
"ModuleName"="C:\\Program Files\\Microsoft\\Exchange Server\\V14\\bin\\Microsoft.Exchange.PowerShell.Configuration.dll"
"PowerShellVersion"="1.0"
"Vendor"="Microsoft Corporation"
"Version"="14.0.0.0"


[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellSnapIns\Microsoft.Exchange.Management.PowerShell.Setup]
"CustomPSSnapInType"="Microsoft.Exchange.Management.PowerShell.SetupPSSnapIn"
"ApplicationBase"="C:\\Program Files\\Microsoft\\Exchange Server\\V14\\bin"
"AssemblyName"="Microsoft.Exchange.PowerShell.Configuration, Version=14.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
"Description"="Setup Tasks for the Exchange Server"
"ModuleName"="C:\\Program Files\\Microsoft\\Exchange Server\\V14\\bin\\Microsoft.Exchange.PowerShell.configuration.dll"
"PowerShellVersion"="1.0"
"Vendor"="Microsoft"
"Version"="14.0.0.0"


[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\PowerShell\1\PowerShellSnapIns\Microsoft.Exchange.Management.Powershell.Support]
"CustomPSSnapInType"="Microsoft.Exchange.Management.Powershell.Support.SupportPSSnapIn"
"ApplicationBase"="C:\\Program Files\\Microsoft\\Exchange Server\\V14\\bin"
"AssemblyName"="Microsoft.Exchange.Management.Powershell.Support, Version=14.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"
"Description"="Support Tasks for the Exchange Server"
"ModuleName"="C:\\Program Files\\Microsoft\\Exchange Server\\V14\\bin\\Microsoft.Exchange.Management.Powershell.Support.dll"
"PowerShellVersion"="1.0"
"Vendor"="Microsoft Corporation"
"Version"="14.0.0.0"

Wednesday, December 22, 2010

Exchange 2010 SP1 Removing Automatically Discovered Mailbox

Not too long ago we upgraded our Microsoft Exchange 2010 server to Service Pack 1. A few days later I noticed a mailbox I had granted myself full access permission to automatically appeared in Outlook on all of my computers, without my doing anything.  A quick search and I found this article saying it's a new feature which is pretty awesome as far as I'm concerned. I then noticed it was impossible to remove this from Outlook.  Even after removing myself from the permissions list in the Exchange Management console the mailbox continued to show up. The article shows where it adds the delegate, so I went to the record and lo and behold the link to myself is still there. I had to manually delete the delegate in ADSI Edit, close and re-open Outlook and then the mailbox disappeared. Hopefully Microsoft will fix this bug in the near future.

How to remove the link:

  1. Open ADSI Edit.
  2. Navigate to the user object under Default Naming Context.
  3. Open the properties of the user and scroll down to MsExchDelegateListLink.
  4. Remove the value for yourself.  In this case I removed Andrew.
  5. Wait for any replication to happen in your AD infrastructure.
  6. Close & re open Outlook.  Within a few seconds the mailbox should disappear.


Notes:
* Mailboxes you had granted yourself access too before you installed SP1 will not auto discover
* You will have to manually remove this entry for each mailbox you want to remove from Outlook.  Annoying.

Wednesday, November 10, 2010

Asterisk and SIP 911/E911 Support

The Plan:
In our company we have 4 locations, and we have to provide VoIP/SIP E911 support to 3 of them; the 4th is in the Philippines and there is no regional 911 type service there.  I'm sharing what/how I implemented E911 because it may be useful.

The Theory:
First off, for each office we had our ITSP configure a DID for each location with our address and company name.  Then, if somebody from that office calls 911 we must send that DID as the callerid for the call so the 911 responder gets the right address.  To do this, we have different extension ranges for each office so I filtered based on extension.  A 10xx series extension is in San Mateo, 11xx in Makati, 15xx in Redmond, 16xx  in Redmond, and 18xx in Lacey. If you wanted you could probably use another way to decide which location a caller was coming from, maybe look at the SIP Client's IP address, but this method worked for us so we used it.

Additionally, we want to alert some of the people in our office if 911 is ever dialed so we know what is going on.  The receptionist used to get calls saying "we just got a 911 call from you" and had no idea what was going on.   So when anybody dials 911 an email is sent to HR, myself, and the receptionist saying who dialed 911 from what phone, what DID was sent to tell the 911 center where to call, etc.

The final thing we did was added a handy feature that sends the call to the receptionist so they know what is going on.  This has been a problem when somebody in another office called 911 and nobody there was responding to messages, we had no idea what was going on.  Now the receptionist will be able to know what is going on in the event of a 911 call.

The Code:
[from-staff]
exten => 911,1,GotoIf($["${CDR(SRC):0:2}"="10"]?sanmateo911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="11"]?makati911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="15"]?redmond911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="16"]?redmond911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="18"]?lacey911)
exten => 911,n,GotoIf($["${CDR(SRC):0:2}"="19"]?lacey911)
exten => 911,n(makati911),Playback(ss-noservice)
exten => 911,n,Hangup()
exten => 911,n(sanmateo911),System(/etc/asterisk/scripts/911-alert.pl "${CDR(SRC)}" "6505551212" "San Mateo")
exten => 911,n,Set(SPYGROUP=E911)
exten => 911,n,Set(CALLERID(all)=6505551212)
exten => 911,n,Dial(SIP/911@bandwidth-primary)
exten => 911,n,Hangup()
exten => 911,n(redmond911),System(/etc/asterisk/scripts/911-alert.pl "${CDR(SRC)}" "4255551212" "Redmond")
exten => 911,n,Set(SPYGROUP=E911)
exten => 911,n,Set(CALLERID(all)=4255551212)
exten => 911,n,Dial(SIP/911@bandwidth-primary)
exten => 911,n,Hangup()
exten => 911,n(lacey911),System(/etc/asterisk/scripts/911-alert.pl "${CDR(SRC)}" "2535551212" "Lacey")
exten => 911,n,Set(SPYGROUP=E911)
exten => 911,n,Set(CALLERID(all)=2535551212)
exten => 911,n,Dial(SIP/911@bandwidth-primary)
exten => 911,n,Hangup()

;the context that the call will be sent to to listen to the 911 call
[campon911]
exten => _1XXX,1,ChanSpy(SIP/${EXTEN},b)

And you can get the source for 911-alert.pl here, please note I added .txt to the extension so the server wouldn't try and parse it.

Testing:
After you implement your 911 code it is very important that you make a test phone call from each location, tell the responder that the call is a non emergency test call and you would like to verify the address and phone number they received.  This way you know what you've done works properly before something that really needs 911 happens.

Tuesday, November 9, 2010

Using PHP + LDAP to Configure Office Communications Server Users

We have automated the vast majority of the processes in our company, and having our users automatically updated saves a lot of time and missed updates.  Because this is more of a telephony blog I'm mostly covering how this relates to configuring users for Microsoft Office Communications Server, but I use this same code to update user titles, managers, locations and quite a few other settings.

First, you need PHP and LDAP set up and working.  If you do, the steps I've come up with are very simple and straightforward to follow.  If not, i'd recommend you get it installed and loaded up.  There is a good tutorial available here for the basics of what i'm doing.

Microsoft Office Communications Server 2007 R2 Settings:
  1. First I set up a user with the options I wanted using the Office Communications Server User management tools. 
    1.  For Asterisk integration you want to enable Enterprise Voice (but not PBX Integration). 
    2. Set the Line URI to tel:+ and an extension.  Mine is 2593 in OCS.  
    3. You may notice I have ext=1593 added.  This is to add support for the new teleconferencing added to OCS R2; if I dial in to an OCS R2 conference from my desk phone the call will come from 1593, and OCS will know (by looking at ext=) that I am Andrew Parisio. This bypasses the need to authenticate myself from my own phone.
  2. The finished product:
  3. Now to see what this looks like in LDAP I opened adsiedit, navigated to my users properties, and found the settings that pertain to OCS, this is what I found:
  4. Now we've found the settings, using this document you can find what optionflags value you want, or you can set the flags on a user and see what it ends up as in adsiedit, either way works.  In order to get the telephony settings set up like they are above, you need to set OptionFlags to 448.  If you set it to 449 you will enable Public IM Connectivity on the Other Options tab.
  5. Please note that you need to get the msRTCSIP-PrimaryHomeServer value, and use it in place of mine.  
  6. These are the settings I ended up with that are necessary for OCS & Telephony to work (and how I set them in my PHP script):
    1. $datam['msrtcsip-userenabled'] = "TRUE";
    2. $datam['msrtcsip-optionflags'] = "449";
    3. $datam['msrtcsip-primaryhomeserver'] = "CN=LC Services,CN=Microsoft,CN=ocs,CN=Pools,CN=RTC Service,CN=Microsoft,CN=System,DC=mydomain,DC=com";
    4. $datam['msrtcsip-primaryuseraddress'] = "sip:andrewp@mydomain.com";
    5. $datam['msrtcsip-line'] = "tel:+2593;ext=1593";
  7. If you have a user you would like to configure for OCS but aren't worried about the Telephony integration you can use these settings instead:
    1. $datam['msrtcsip-userenabled'] = "TRUE";
    2. $datam['msrtcsip-optionflags'] = "321";
    3. $datam['msrtcsip-primaryhomeserver'] = "CN=LC Services,CN=Microsoft,CN=ocs,CN=Pools,CN=RTC Service,CN=Microsoft,CN=System,DC=mydomain,DC=com";
    4. $datam['msrtcsip-primaryuseraddress'] = "sip:andrewp@mydomain.com";
  8. Once you have the settings, you need to get them in to a script that will allow you to run them easily.  I've stripped down the script I use in my office to just the OCS settings I've talked about above.  If you would like to use this as a base for updating all of the information about users in your company I have some examples at the bottom of my sample code for how to set some other information.

Generic LDAP Entry information and source code is available here:
The example source for what I use is available here, please note that I've renamed it to .inc because my server would parse it if it were named .php