Commit f9f1fc48 authored by Guus der Kinderen's avatar Guus der Kinderen

OF-1244: New DNS SRV evaluation

This commit adds an admin console page that allows an administrator to evaluate
the DNS SRV configuration in context of the XMPP domain name and fully qualified
domain name of the sever that is running Openfire.
parent 4ba3e173
......@@ -1036,6 +1036,7 @@ index.uptime=Server Uptime:
index.version=Version:
index.home=Server Directory:
index.certificate-warning=Found RSA certificate that is not valid for the server domain.
index.dns-warning={0}DNS configuration appears to be missing or incorrect.{1}
index.certificate-error=Unable to access certificate store. The keystore may be corrupt.
index.server_name=XMPP Domain Name:
index.host_name=Server Host Name (FQDN):
......@@ -3181,3 +3182,19 @@ connection-type.unspecified=unspecified
connection-mode.plain=plain text (with STARTSSL)
connection-mode.legacy=encrypted (legacy-mode)
connection-mode.unspecified=unspecified
# DNS SRV Records check
system.dns.srv.check.title=DNS SRV Record verification
system.dns.srv.check.name=Current DNS Configuration Evaluation
system.dns.srv.check.info=To compose the information on this page, a DNS SRV query has been made, using the value of <tt>{0}</tt>, which is the XMPP domain name that is configured for Openfire. Any resulting records are inspected for a match against the value of <tt>{1}</tt>, which is the fully qualified domain name of the server that is running Openfire, as {2}configured here{3}.
system.dns.srv.check.rationale=Without a valid DNS SRV record, clients are likely to have trouble connecting to your server. Even when clients provide a manual connect host, it is likely that they provide a different value than the fully qualified domain name that is configured for your server, which will cause problems with certain authentication mechanisms. It is recommended to have a DNS SRV record for this XMPP domain that matches the fully qualified domain name of the server on which you are running this instance of Openfire.
system.dns.srv.check.example=It is expected that your DNS configuration has at least two SRV records, which are similar to this (values like TTL, priority and weight are examples, and might be different in your configuration):
system.dns.srv.check.disclaimer=Note that changes that have been applied to DNS configuration might take a while to propagate. It might take some time for Openfire to be able to see the changes that were made to your DNS configuration.
system.dns.srv.check.detected_matching_records.one-liner=Successfully identified A DNS SRV record for this host.
system.dns.srv.check.detected_matching_records.description=The DNS SRV records for this XMPP domain contain one that matches this server.
system.dns.srv.check.xmppdomain_equals_hostname.one-liner=No DNS SRV records for this host are found, but none are needed.
system.dns.srv.check.xmppdomain_equals_hostname.description=There are no DNS SRV records for this XMPP domain, but as the fully qualified domain name of this host is equal to the XMPP domain name, none are needed.
system.dns.srv.check.no-records.one-liner=No DNS SRV records for this host are found.
system.dns.srv.check.no-records.description=There appear to be no DNS SRV records at all for this XMPP domain. With the current configuration of Openfire, it is recommended that DNS SRV records are created for this server.
system.dns.srv.check.no-match.one-liner=DNS SRV records do not include this host.
system.dns.srv.check.no-match.description=DNS SRV records for this XMPP domain are found, but none of them match the fully qualified domain name of this server. This typically occurs when the DNS SRV record uses a different identifier for this host than the fully qualified domain name as configured in Openfire. This should be corrected, as it is known to cause problems with certain authentication mechanisms. Another possibility is that this instance of Openfire is part of a cluster, but has not yet been included in the DNS configuration.
......@@ -34,7 +34,7 @@ import javax.naming.directory.InitialDirContext;
import java.util.*;
/**
* Utilty class to perform DNS lookups for XMPP services.
* Utility class to perform DNS lookups for XMPP services.
*
* @author Matt Tucker
*/
......@@ -98,9 +98,9 @@ public class DNSUtil {
}
// Attempt the SRV lookup.
results.addAll(srvLookup("_xmpp-server._tcp." + domain));
results.addAll(srvLookup("xmpp-server", "tcp", domain ) );
if (results.isEmpty()) {
results.addAll(srvLookup("_jabber._tcp." + domain));
results.addAll(srvLookup( "jabber", "tcp", domain ) );
}
// Use domain and default port as fallback.
......@@ -161,10 +161,47 @@ public class DNSUtil {
return answer;
}
private static List<? extends HostAddress> srvLookup(String lookup) {
if (lookup == null) {
/**
* Performs a DNS SRV lookup. Does not take into account any DNS overrides configured in this class.
*
* The results returned by this method are ordered by priority (ascending), and order of equal priority entries is
* randomized by weight, as defined in the DNS SRV specification.
*
* @param service the symbolic name of the desired service (cannot be null).
* @param proto the transport protocol of the desired service; this is usually either TCP or UDP (cannot be null).
* @param name the domain name for which this record is valid (cannot be null).
* @return An ordered of results (possibly empty, never null).
*/
public static List<WeightedHostAddress> srvLookup(String service, String proto, String name) {
if (service == null || proto == null || name == null) {
throw new NullPointerException("DNS lookup can't be null");
}
if ( !service.startsWith( "_" ) )
{
service = "_" + service;
}
if ( !service.endsWith( "." ) )
{
service = service + ".";
}
if ( !proto.startsWith( "_" ) )
{
proto = "_" + proto;
}
if ( !proto.endsWith( "." ) )
{
proto = proto+ ".";
}
if ( !name.endsWith( "." ) ) {
name = name + ".";
}
// _service._proto.name.
final String lookup = (service + proto + name).toLowerCase();
try {
Attributes dnsLookup =
context.getAttributes(lookup, new String[]{"SRV"});
......
<%@ page import="java.util.*"
errorPage="error.jsp"
%>
<%@ page import="org.jivesoftware.openfire.net.DNSUtil" %>
<%@ page import="org.jivesoftware.openfire.XMPPServer" %>
<%@ taglib uri="admin" prefix="admin" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/functions" prefix="fn" %>
<jsp:useBean id="webManager" class="org.jivesoftware.util.WebManager" />
<% webManager.init(request, response, session, application, out ); %>
<%
final String xmppDomain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
final String hostname = XMPPServer.getInstance().getServerInfo().getHostname();
final List<DNSUtil.WeightedHostAddress> dnsSrvRecords = DNSUtil.srvLookup( "xmpp-client", "tcp", xmppDomain );
boolean detectedRecordForHostname = false;
for ( final DNSUtil.WeightedHostAddress dnsSrvRecord : dnsSrvRecords )
{
if ( hostname.equalsIgnoreCase( dnsSrvRecord.getHost() ) )
{
detectedRecordForHostname = true;
break;
}
}
pageContext.setAttribute( "xmppDomain", xmppDomain );
pageContext.setAttribute( "hostname", hostname );
pageContext.setAttribute( "dnsSrvRecords", dnsSrvRecords );
pageContext.setAttribute( "detectedRecordForHostname", detectedRecordForHostname );
%>
<html>
<head>
<title><fmt:message key="system.dns.srv.check.title"/></title>
<meta name="pageID" content="server-settings"/>
</head>
<body>
<c:choose>
<c:when test="${detectedRecordForHostname}">
<admin:infobox type="info">
<fmt:message key="system.dns.srv.check.detected_matching_records.one-liner" />
</admin:infobox>
<fmt:message key="system.dns.srv.check.detected_matching_records.description" var="plaintextboxcontent"/>
</c:when>
<c:when test="${xmppDomain eq hostname}">
<admin:infobox type="info">
<fmt:message key="system.dns.srv.check.xmppdomain_equals_hostname.one-liner" />
</admin:infobox>
<fmt:message key="system.dns.srv.check.xmppdomain_equals_hostname.description" var="plaintextboxcontent"/>
</c:when>
<c:when test="${empty dnsSrvRecords}">
<admin:infobox type="warning">
<fmt:message key="system.dns.srv.check.no-records.one-liner" />
</admin:infobox>
<fmt:message key="system.dns.srv.check.no-records.description" var="plaintextboxcontent"/>
</c:when>
<c:otherwise>
<admin:infobox type="warning">
<fmt:message key="system.dns.srv.check.no-match.one-liner" />
</admin:infobox>
<fmt:message key="system.dns.srv.check.no-match.description" var="plaintextboxcontent"/>
</c:otherwise>
</c:choose>
<p>
<fmt:message key="system.dns.srv.check.info">
<fmt:param value="${xmppDomain}" />
<fmt:param value="${hostname}" />
<fmt:param value="<a href=\"server-props.jsp\">" />
<fmt:param value="</a>"/>
</fmt:message>
</p>
<fmt:message key="system.dns.srv.check.name" var="plaintextboxtitle"/>
<admin:contentBox title="${plaintextboxtitle}">
<c:out value="${plaintextboxcontent}"/>
</admin:contentBox>
<c:if test="${not empty dnsSrvRecords}">
<div class="jive-table">
<table cellpadding="0" cellspacing="0" border="0" width="100%">
<thead>
<tr>
<th>&nbsp;</th>
<th nowrap><fmt:message key="server.session.label.host" /></th>
<th nowrap colspan="3"><fmt:message key="server.session.label.connection" /></th>
<th nowrap><fmt:message key="server.session.label.creation" /></th>
<th nowrap><fmt:message key="server.session.label.last_active" /></th>
<th nowrap><fmt:message key="server.session.label.close_connect" /></th>
</tr>
</thead>
<tbody>
<c:forEach var="dnsSrvRecord" items="${dnsSrvRecords}" varStatus="varStatus">
<c:choose>
<c:when test="${dnsSrvRecord.host.toLowerCase() eq hostname}">
<c:set var="cssClass" value="jive-highlight"/>
</c:when>
<c:otherwise>
<c:set var="cssClass" value="${varStatus.count % 2 eq 0 ? 'jive-even' : 'jive-odd' }"/>
</c:otherwise>
</c:choose>
<tr class="${cssClass}">
<td width="1%" nowrap><c:out value="${varStatus.count}"/></td>
<td nowrap><c:out value="${dnsSrvRecord.host}"/></td>
<td nowrap><c:out value="${dnsSrvRecord.port}"/></td>
<td nowrap><c:out value="${dnsSrvRecord.priority}"/></td>
<td nowrap><c:out value="${dnsSrvRecord.weight}"/></td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</c:if>
<p>
<fmt:message key="system.dns.srv.check.rationale" />
</p>
<p>
<fmt:message key="system.dns.srv.check.example" />
<!-- TODO verify the port value! -->
<ul>
<li><tt>_xmpp-client._tcp.<c:out value="${xmppDomain}"/>. 86400 IN SRV 0 5 5222 <c:out value="${hostname}"/>.</tt></li>
<li><tt>_xmpp-server._tcp.<c:out value="${xmppDomain}"/>. 86400 IN SRV 0 5 5269 <c:out value="${hostname}"/>.</tt></li>
</ul>
</p>
<p>
<fmt:message key="system.dns.srv.check.disclaimer" />
</p>
</body>
</html>
......@@ -48,6 +48,7 @@
<%@ page import="java.text.DecimalFormat" %>
<%@ page import="java.util.Arrays" %>
<%@ page import="java.util.List" %>
<%@ page import="org.jivesoftware.openfire.net.DNSUtil" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt" %>
......@@ -243,10 +244,10 @@
<% final IdentityStore identityStore = XMPPServer.getInstance().getCertificateStoreManager().getIdentityStore( ConnectionType.SOCKET_C2S ); %>
<% try { %>
<% if (!identityStore.containsDomainCertificate( "RSA" )) {%>
<img src="images/warning-16x16.gif" width="16" height="16" border="0" alt="<fmt:message key="index.certificate-warning" />" title="<fmt:message key="index.certificate-warning" />">&nbsp;
<img src="images/warning-16x16.gif" width="12" height="12" border="0" alt="<fmt:message key="index.certificate-warning" />" title="<fmt:message key="index.certificate-warning" />">&nbsp;
<% } %>
<% } catch (Exception e) { %>
<img src="images/error-16x16.gif" width="16" height="16" border="0" alt="<fmt:message key="index.certificate-error" />" title="<fmt:message key="index.certificate-error" />">&nbsp;
<img src="images/error-16x16.gif" width="12" height="12" border="0" alt="<fmt:message key="index.certificate-error" />" title="<fmt:message key="index.certificate-error" />">&nbsp;
<% } %>
${webManager.serverInfo.XMPPDomain}
</td>
......@@ -286,6 +287,33 @@
</td>
<td class="c2">
${webManager.serverInfo.hostname}
<% // Determine if the DNS configuration for this XMPP domain needs to be evaluated.
final String xmppDomain = XMPPServer.getInstance().getServerInfo().getXMPPDomain();
final String hostname = XMPPServer.getInstance().getServerInfo().getHostname();
boolean dnsIssue = false;
if ( !xmppDomain.equalsIgnoreCase( hostname ) )
{
dnsIssue = true;
final List<DNSUtil.WeightedHostAddress> dnsSrvRecords = DNSUtil.srvLookup( "xmpp-client", "tcp", xmppDomain );
for ( final DNSUtil.WeightedHostAddress dnsSrvRecord : dnsSrvRecords )
{
if ( hostname.equalsIgnoreCase( dnsSrvRecord.getHost() ) )
{
dnsIssue = false;
break;
}
}
}
if ( dnsIssue ) {
%>
<img src="images/warning-16x16.gif" width="12" height="12" border="0">
<fmt:message key="index.dns-warning">
<fmt:param><a href='dns-check.jsp'></fmt:param>
<fmt:param></a></fmt:param>
</fmt:message>
<%
}
%>
</td>
</tr>
<tr>
......
......@@ -587,6 +587,9 @@ pre, tt {
.jive-table .jive-even td {
background-color:#FBFBFB;
}
.jive-table .jive-highlight td {
background-color:#FFFFCC;
}
.jive-table tfoot td {
background-color:#EEEEEE;
border-bottom:1px solid #CCCCCC;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment