Microsoft, in their infinite wisdom, designed Lync in such a way that if members of two organizations deploy Lync and try to schedule meetings with each other, Lync will use federation in order negotiate authentication between the two domains. This is great if you have a federated relationship with all your partners that you want to hold meetings with. But what if you want to do ad hoc meetings with unauthenticated guests? Microsoft gives you two choices. One is to allow automatic discovery of federated partners, where the Lync servers will negotiate with each other based on published DNS and other settings, and the other is to log into the meeting using the Silverlight client.
There’s just one problem.
If you have the Lync desktop client on your PC and you try to visit an external meeting link, such as https://lync.contoso.com/meet/username/EJHFSN and you are not a part of the Contoso organization and you do not have federation set up or do not allow automatic discovery of federated partners, it will fail with a useless numeric error code that means absolutely nothing. Since the desktop client does not allow you log on anonymously, it will never fallback to guest logon, even if the meeting organizer has it enabled for the meeting.
TechNet to the rescue! All you have to do is append “sl=1” to the end of the query string of the URL, so that you visit https://lync.contoso.com/meet/username/EJHFSN?sl=1 and then it will force the Silverlight client, which will allow you to log on anonymously. In this scenario, Lync meetings then behave basically like WebEx or GotoMeeting, where external participants need a browser plugin to connect to the meeting. Perfect. That’s exactly what I want.
Again, one problem. Imagine trying to get your entire staff to always remember to append that to the meeting link when they set up external meetings. Despite best efforts, it’s just not going to happen. Your CFO has better things to do and she will forget, because that is human nature. And, really, this is Microsoft’s shortsightedness here. You can read my comment at the TechNet article linked above.
Thanks for the "?sl=1" trick. That did the trick for me. But explaining this to my users is going to be a pain. Imagine me in the CFO's office after months of extolling the virtues of Lync and how we even got rid of our WebEx subscription because, heck, Lync does meetings too! But suddenly, a meeting participant is also using Lync at his company but we have no federated relationship with each other, so when we click on each other's meeting links it just fails with a terrible numerical error. "I thought this thing could replace WebEx," the CFO bellows, scowling at me in disdain. "Oh, it can," I reply, "just make sure you modify every meeting invitation so that the URL has ?sl=1 at the end of it!" Yea, that will go over well.
Thankfully, there is a workaround. And due to the way Lync is designed, it’s really not difficult to set up.
When you set up your Lync websites, it creates an internal and external site. The external site by default uses the non-standard ports 8080 and 4443. The Lync best practice is to use a Reverse Proxy or firewall port forwarding rules to send traffic destined for the normal web ports to the Lync alternate ports. Your internal users, on the other hand, use ports 80 and 443 as normal, directly communicating with the Lync server.
Reverse proxies can also be set up to modify URLs before the connection is sent to the backend. This is known as URL Rewriting. In this case, you want a URL rewrite rule that will modify connections to /meet/ such that ?sl=1 is always added to the end. I found from trial and error that you get the best results by only modifying the /meet/ part of the above URL (assuming you are using Simple URLs like that). So I set up my topology so that 8080 and 4443 were exposed directly to the outside so I have an option to bypass the reverse proxy once the connection is established. This is all completely secure and transparent to the end user. We’re not bypassing the firewall, just the reverse proxy’s URL rewriting when it is not needed.
So the final topology looks like this. (The Lync Front End is either your Edge server or your single server depending on the size of your deployment.)
From outside my firewall, ports 80, 443, 8080, and 4443 are all open. If you connect to 80 or 443, you are sent to the reverse proxy. If you go to 8080 or 4443, you are sent directly to Lync.
To prepare Lync for this configuration, I first edited the topology so that the published ports are assigned the same as the internal (8080 and 4443) as this will allow us to bypass the reverse proxy when it is not needed.
Whenever you publish your topology, remember to rerun the Lync setup wizard.
The reverse proxy can be easily created using IIS. In fact, you can set it up on your Lync edge server if you want. It depends on your workload. For the purposes of this post, we’ll assume you are setting it up on the same server. Note: Lync will stop any non-Lync website in IIS whenever you publish your topology and rerun setup, so be prepared for this!
In order to configure the reverse proxy, you need to install the Application Request Routing and URL Rewrite extensions for IIS. These both should already be installed if you are using your Lync server.
Enable the Application Request Routing. This is done at the server level. Click on your IIS server in the IIS manager, double click Application Request Routing Cache, then click on Server Proxy Settings. Check Enable proxy and keep everything else at defaults.
Create a new website. Give it a folder path that is not shared with any other site (i.e., don’t reuse C:\Inetpub\wwwroot). The bindings should be whatever the external IP address is mapped to through your firewall. Bind HTTP and HTTPS on the default ports. Make sure you use a different internal IP address than your Lync internal website so there isn’t a collision. You don’t want internal users going through the reverse proxy.
Go into the site’s URL Rewrite section and create a dummy rule. We are going to overwrite this later, so it doesn’t matter what it is. We just want to create a web.config that we can edit by hand.
Edit the web.config “rules” section for the reverse proxy site. Now here is where the fun begins. We are going to modify any request that goes to /meet/ so that it has sl=1 at the end. I created a rule for both HTTP and HTTPS since I am using default Lync ports (non-standard web ports). There is also a condition that if the query string already contains sl=, it will not modify it. Underneath the /meet/ rewrites are the default rules that just pass the request through unmodified to the correct ports. Obviously, URLs, RegEx, ports, and so on, will all need to be modified to match your environment.
<rules>
<rule name="ReverseProxyInboundRule1" stopProcessing="true">
<match url="^meet/(.*)" />
<conditions>
<add input="{QUERY_STRING}" pattern="(.*)sl=(.*)" negate="true" />
<add input="{CACHE_URL}" pattern="^(https)://" />
</conditions>
<action type="Rewrite" url="{C:1}://lync.contoso.com:4443/{R:0}?sl=1&{QUERY_STRING}" appendQueryString="false" logRewrittenUrl="true" />
</rule>
<rule name="ReverseProxyInboundRule2" stopProcessing="true">
<match url="^meet/(.*)" />
<conditions>
<add input="{QUERY_STRING}" pattern="(.*)sl=(.*)" negate="true" />
<add input="{CACHE_URL}" pattern="^(http)://" />
</conditions>
<action type="Rewrite" url="{C:1}://lync.contoso.com:8080/{R:0}?sl=1&{QUERY_STRING}" appendQueryString="false" logRewrittenUrl="true" />
</rule>
<rule name="ReverseProxyInboundRule3" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(https)://" />
</conditions>
<action type="Rewrite" url="{C:1}://lync.contoso.com:4443/{R:1}" appendQueryString="true" logRewrittenUrl="true" />
</rule>
<rule name="ReverseProxyInboundRule4" stopProcessing="true">
<match url="(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(http)://" />
</conditions>
<action type="Rewrite" url="{C:1}://lync.contoso.com:8080/{R:1}" appendQueryString="true" logRewrittenUrl="true" />
</rule>
</rules>
If you attempt to connect to a meeting externally now, this is what happens.
- Browser initiates connection to https://lync.contoso.com/meet/username/EJHFSN.
- Reverse Proxy receives the request, adds sl=1 to the query string, and passes the request to the external Lync website at https://lync.contoso.com:4443/meet/username/EJHFSN?sl=1.
- Lync server replies and tells the browser to load the Silverlight Lync client which then attempts to connect directly to the lync web services (bypassing the Reverse Proxy) at https://pool1.lync.contoso.com:4443/Reach/Client/WebPages/ReachClient.aspx.
- The external user can join as an anonymous guest, or log on using the domain credentials of the organizer’s meeting, if they have that. The desktop Lync client will never launch!
Hopefully in the future Microsoft will fix the desktop client to allow it to log on anonymously to external meetings and also give us a checkbox in the Lync Server Control Panel that allows us to force all external connections to the Silverlight client (for legacy organizations that might connect to ours).