I will skip kerberos configuration because I am certain that it works. I made a test with checking PHP $_SERVER variable, and everything was set.
Apache configuration:
LoadModule auth_kerb_module /usr/lib/apache2/modules/mod_auth_kerb.so
<VirtualHost localhost:443>
SSLEngine on
SSLCertificateFile /opt/keys/localhost.crt
SSLCertificateKeyFile /opt/keys/private.pem
SSLProxyEngine On
SSLProxyVerify none
SSLProxyCheckPeerCN off
SSLProxyCheckPeerName off
SSLProxyCheckPeerExpire off
ProxyPass / ajp://localhost:8009/
<Location / >
SSLRequireSSL
AuthType Kerberos
KrbMethodNegotiate On
KrbMethodK5Passwd Off
KrbServiceName HTTP/localhost#example.com
KrbAuthRealms example.com
Krb5KeyTab /etc/krb5.keytab
require valid-user
</Location>
</VirtualHost>
Tomcat configuration
< Connector port="8443" protocol="org.apache.coyote.http11.Http11Protocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS"
SSLProtocol="TLSv1+TLSv1.1+TLSv1.2"
keystoreFile="/opt/keys/keystore.jks"
keystorePass="changeit" />
Setting creating project with mvn archetype HOOK, named Kerberos-Hook, and then create in src/main/java class in package com.liferay.portal.security.auth.KerberosAutoLogin.java:
public class KerberosAutoLogin implements AutoLogin {
private static Log logger = LogFactoryUtil.getLog(KerberosAutoLogin.class);
public String[] handleException(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response, Exception e)
throws AutoLoginException {
logger.error("1");
return doHandleException(request, response, e);
}
protected String[] doHandleException(
HttpServletRequest request, HttpServletResponse response,
Exception e)
throws AutoLoginException {
logger.info("2");
if (request.getAttribute(AutoLogin.AUTO_LOGIN_REDIRECT) == null) {
throw new AutoLoginException(e);
}
logger.error("doHandleException: " + e);
return null;
}
public String[] login(HttpServletRequest req, HttpServletResponse res)
throws AutoLoginException {
logger.error("3");
try {
return doLogin(req, res);
}
catch (Exception e) {
return handleException(req, res, e);
}
}
protected String[] doLogin(HttpServletRequest req, HttpServletResponse res)
throws AutoLoginException, Exception {
logger.error("4");
String[] credentials = null;
String userName = (String) req.getAttribute("REMOTE_USER");
logger.info("kerberosUserName = " + userName);
userName = userName.replaceAll("#.*", "").replaceAll("/.*", "");
logger.info("userName = " + userName);
long companyID = PortalUtil.getCompanyId(req);
logger.info("CompanyID = " + companyID);
if (userName == null || userName.length() < 1) {
return credentials;
} else {
credentials = new String[3];
User user = UserLocalServiceUtil.getUserByScreenName(companyID, userName);
long userID = user.getUserId();
String userPassword = user.getPassword();
logger.info("userID = " + userID);
credentials[0] = String.valueOf(userID);
credentials[1] = userPassword;
credentials[2] = Boolean.FALSE.toString();
return credentials;
}
}
}
Configuring liferay-hook.xml to point to src/main/resources/ext-portal.properties which contain auto.login.hooks=com.liferay.portal.security.auth.KerberosAutoLogin
Create project with mvn archetype Ext and in project ext-impl create class com.liferay.portal.servlet.filters.autologin.AutoLoginFilter.java
Deployed HOOK and EXT
in the KerberosAutoLogin class I put some logs in order to see somekind of a debug because I deploy application on remote server so I am not able to debug the hook properly.
However logs are not being displayed, so the class is not even used, can anyone point me what Am I missing in order to start integrating my liferay with kerberos?
In apache log I see for example:
localhost:443 192.168.24.73 - mithrand1r#example.com [11/Feb/2016:09:56:57 +0100] "POST /poller/receive HTTP/1.1" 200 1011 "https://localhost/group/control_panel/manage/-/server/log-levels/update-categories?refererPlid=20184" "Mozilla/5.0 (Windows NT 6.1; WOW64; rv:38.0) Gecko/20100101 Firefox/38.0"
Seems you are missing liferay-hook.xml file configuration.
Please see answer for How do I use autologin in liferay?, namely "Registering the autologin class" section.
From the configuration it also looks you introduced a security vulnerability using ProxyPass / https://localhost:8443/.
Try to go to https://yourserver/api/axis, you should not see list of services. If you see the list of services you are vulnerable. Then please use AJP connector ProxyPass / ajp://localhost:8009/. For more info please see http://topolik-at-work.blogspot.cz/2013/02/http-modproxy-liferay-same-server.html
Have you set the value in auto.login.hook property? It is mandatory to indicate Liferay what AutoLogin classes has to use, something like this:
auto.login.hooks=com.liferay.portal.security.auth.KerberosAutoLogin
Anyway, I used a hook like you and it didn't work for me neither. So I tried to use the class in a ext-plugin project and that worked (of course, you have to set the auto.login.hook property with your AutoLogin class anyway).
I'm not sure that proxy forwarding through http/https includes all the information that Apache httpd has received. As #topolik as suggested in his answer to use ajp, this is a better starting point to make sure that all headers and request state is forwarded to tomcat. Monitor the connection between httpd and tomcat and see what goes through the line.
When the proxy creates its own http(s) request, there's nothing that I'm aware of that mandates that all request state must be forwarded. In fact, typically tomcat will receive "localhost" as the hostname unless you add ProxyPreserveHost On to your configuration. This is a lot more trivial than all the Kerberos settings.
I suspect they get lost at your Apache httpd, provided you're not only sure that they're there, but rather extra extra extra sure.
Speaking of extra extra extra sure: You mention ext-portal.properties, while Liferay's standard is portal-ext.properties. Check the logs to make sure your configuration file is picked up by Liferay, and make sure it's named correctly.
It looked that It was my misunderstoonding how Kerberos SSO work in liferay and I was missing including mod_jk.conf inside of apache2.conf
according to http://blog.dbi-services.com/kerberos-sso-with-liferay-61/
[root mod_auth_kerb-5.4]# cd ..
[root opt]# wget http://mirror.switch.ch/mirror/apache/dist/tomcat/tomcat-connectors/jk/tomcat-connectors-1.2.40-src.tar.gz
[root opt]# tar -xvf tomcat-connectors-1.2.40-src.tar.gz
[root opt]# cd tomcat-connectors-1.2.40-src/native
[root native]# ./configure --with-apxs=/usr/local/apache2/bin/apxs --enable-api-compatibility
[root native]# make
[root native]# make install
and
[root opt]# vi /opt/liferay-6.1.1/tomcat/conf/mod_jk.conf
LoadModule jk_module /usr/local/apache2/modules/mod_jk.so
JkWorkersFile /opt/liferay-6.1.1/tomcat-7.0.27/conf/workers.properties
JkLogFile /usr/local/apache2/logs/mod_jk.log
JkLogLevel debug
JkLogStampFormat "[%a %b %d %H:%M:%S %Y]"
# JkOptions indicate to send SSL KEY SIZE,
JkOptions +ForwardKeySize +ForwardURICompat -ForwardDirectories
# JkRequestLogFormat set the request format
JkRequestLogFormat "%w %V %T"
JkMount / ajp13
JkMount /* ajp13
[root opt]# vi /opt/liferay-6.1.1/tomcat/conf/workers.properties
# Define 1 real worker named ajp13
worker.list=ajp13
worker.ajp13.type=ajp13
worker.ajp13.host=localhost
worker.ajp13.port=8009
worker.ajp13.lbfactor=50
worker.ajp13.cachesize=10
worker.ajp13.cache_timeout=600
worker.ajp13.socket_keepalive=1
Related
I'm renaming the cookie and made it to target to a different path, rather than targeting to a default path "/".
Below is the web.config settings:
<sessionState sessionIDManagerType="MyNamespace.MySessionIDManager" cookieName="AppCookie"/>
Below is the backend class used to create the cookie:
public class MySessionIDManager : SessionIDManager, ISessionIDManager
{
void ISessionIDManager.SaveSessionID(HttpContext context, string id, out bool redirected, out bool cookieAdded)
{
base.SaveSessionID(context, id, out redirected, out cookieAdded);
if (cookieAdded)
{
var name = "AppCookie";
var cookie = context.Response.Cookies[name];
cookie.Path = "/Forms";
}
}
}
This fix is working fine for me locally. The cookie is successfully pointing to the given path i.e "/Forms".
But when I deploy my application to IIS, I'm not able to login to the application.
It is not throwing any error, but not allowing me to login to the web application.
If I use to below web.config settings, it is working fine.
<sessionState mode="InProc" timeout="30" cookieName="AppCookie" />
Please let me know what issue it is causing in the IIS.
Any input is much appreciated.
Thank you all in advance.
Thanks and Regards,
Dada.
please try to change the URL from http://www.website/login/ to http://www.website/forms/login/ and then you can see the cookie will send in the request header, and you will auto login.
This is caused by you change the cookie URL to /forms, it means that only the http URL has /forms string will send the session cookie which has created with the aspnet_sessionID.
If you don't change the cookie URL, default path '/' means the cookie can be shared.
You can see this link: https://learn.microsoft.com/en-us/dotnet/api/system.web.httpcookie.path?view=netframework-4.8
I fixed this issue with the below piece of code;
public class CookieManager : SessionIDManager, ISessionIDManager
{
void ISessionIDManager.SaveSessionID(HttpContext context, string id, out bool redirected, out bool cookieAdded)
{
base.SaveSessionID(context, id, out redirected, out cookieAdded);
if (cookieAdded)
{
SessionStateSection sessionStateSection = (System.Web.Configuration.SessionStateSection)ConfigurationManager.GetSection("system.web/sessionState");
var cookie = context.Response.Cookies[sessionStateSection.CookieName];
cookie.Path = context.Request.ApplicationPath;
}
}
}
And update the web.config as follows;
<sessionState sessionIDManagerType="ANJU.Reports.WebUI.Library.CookieManager" timeout="30" cookieName="CookieName"/>
Now when I host my application on the IIS, it'll fetch the directory where I have my build.
All my cookies will point to the root directory of the build.
I have a .NET Core 2.1 application that is running on IIS with Basic Authentication and Windows Authentication configured. Windows Authentication is working as expected. When I disable Windows Authentication and only use Basic Authentication the application does not work as expected. It prompts for credentials when visiting the index.html page and prompts again when you go to the swashbuckle UI of the API. When you try to use any of the API Endpoints it prompts for credentials and continues to prompt for credentials like it doesn't remember the user.
I have this Middleware logging what is happening
public class UserInfoMiddleWare
{
private readonly ILogger<UserInfoMiddleWare> _logger;
private readonly RequestDelegate _next;
public UserInfoMiddleWare(ILogger<UserInfoMiddleWare> logger, RequestDelegate next)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
this._logger.LogError("UserInfoMiddleWare!!!!!!");
try
{
var userIdentity = context.User?.Identity;
this._logger.LogError($"user exists {(userIdentity != null)}");
var loginName = context.User?.LoginName();
this._logger.LogError($"LoginName {loginName}");
var name = userIdentity?.Name;
this._logger.LogError($"name {name}");
var identityIsAuthenticated = userIdentity?.IsAuthenticated;
this._logger.LogError($"identityIsAuthenticated {identityIsAuthenticated}");
var identityAuthenticationType = userIdentity?.AuthenticationType;
this._logger.LogError($"identityAuthenticationType {identityAuthenticationType}");
}
catch (Exception e)
{
this._logger.LogError(e, "bad middleware!");
}
await _next(context);
}
This is a sample output I am getting in my logs
[Error] UserInfoMiddleWare!!!!!!
[Error] user exists True
[Error] LoginName
[Error] name
[Error] identityIsAuthenticated False
[Error] identityAuthenticationType
Edit:
After doing some reading it looks like .NET Core does not support Basic Auth even if IIS is the proxy. If someone could confirm that with some documentation I would appreciate it.
ASP.NET Core Module is designed to only forward Windows authentication token,
https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/aspnet-core-module?view=aspnetcore-2.1
Not sure if in .NET Core 2.2 they plan to change that.
I have a Service Stack service hosted within a SharePoint 2013 site. When attempting to make a cross domain request to one of the services, a preflight OPTIONS request is made, as expected.
The problem is that the response always comes back as 401 Unauthorized, due to the fact that authentication info is not sent across with the request. I have tried putting some request filters via servicestack to try and bypass the authentication, but these filters are not firing - it seems like something prior to service stack is sending the response.
Is there any way of specifying that OPTIONS requests to the sharepoint site do not need to be authenticated? If not, does anyone have a workaround for this scenario?
I tried 'fooling' the browser in to not sending a preflight request by changing the data type from application/json to text/plain in my ajax request, but then the data I send is not being deserialised in to the correct RequestDTO for the service calls on the server side.
Any help would be appreciated.
We ended up having to write our own HTTP module in order to support the options request. We basically add a key specifying which domains to allow the CORS requests from (can support more than one) and then have this HTTP module registered:
public class ECSPreFlightModule : IHttpModule
{
/// <summary>
/// You will need to configure this module in the Web.config file of your
/// web and register it with IIS before being able to use it. For more information
/// see the following link: http://go.microsoft.com/?linkid=8101007
/// </summary>
public void Dispose()
{
//clean-up code here.
}
private const string OptionsHeader = "OPTIONS";
private const string OriginHeader = "ORIGIN";
private const string AccessAllowOrigin = "Access-Control-Allow-Origin";
private string AllowedOriginUrlsArray
{
get
{
return GetWebConfigValue("CORSAllowedOriginUrls");
}
}
private string GetWebConfigValue(string key)
{
var configuration = WebConfigurationManager.OpenWebConfiguration("~");
object o = configuration.GetSection("system.web/httpModules");
var section = o as HttpModulesSection;
return section.CurrentConfiguration.AppSettings.Settings[key].Value;
}
public void Init(HttpApplication context)
{
context.PreSendRequestHeaders += (sender, e) =>
{
var splitUrls = AllowedOriginUrlsArray.Split('|');
var response = context.Response;
var originHeader = context.Request.Headers.Get(OriginHeader);
if (!String.IsNullOrEmpty(originHeader) && splitUrls.Length > 0)
{
foreach (var url in splitUrls)
{
var urlLower = url.ToLower();
var originHeaderLower = originHeader.ToLower();
// if the method being requested is an OPTIONS request and the url is the url specified in the web.config then return an OK response.
if (context.Request.HttpMethod.ToLowerInvariant() == OptionsHeader.ToLowerInvariant() &&
(urlLower == originHeaderLower))
{
response.StatusCode = (int)HttpStatusCode.OK;
}
// If the originating header url is equal to the url specified in the web.config then grant the access control
if (originHeaderLower == urlLower)
{
response.AddHeader(AccessAllowOrigin, originHeader);
break;
}
}
}
};
}
}
}
The above module was wrapped in a sharepoint feature that, when activated, made the appropriate changes to the web.config, namely registering the module and adding the following keys:
<add name='Access-Control-Allow-Credentials' value='true' />
<add name='Access-Control-Allow-Headers' value='Authorization, X-Requested-With, Content-Type, Origin, Accept, X-RequestDigest' />
<add name='Access-Control-Allow-Methods' value='GET,POST,OPTIONS,PUT, DELETE' />
I am setting up facebook authentication with servicestack and have been getting the return type #f=Unknown, I've tracked it down to coming from the authentication block:
try
{
var contents = accessTokenUrl.DownloadUrl();
var authInfo = HttpUtility.ParseQueryString(contents);
tokens.AccessTokenSecret = authInfo["access_token"];
session.IsAuthenticated = true;
authService.SaveSession(session, SessionExpiry);
OnAuthenticated(authService, session, tokens, authInfo.ToDictionary());
//Haz access!
return authService.Redirect(session.ReferrerUrl.AddHashParam("s", "1"));
}
catch (WebException we)
{
var statusCode = ((HttpWebResponse)we.Response).StatusCode;
if (statusCode == HttpStatusCode.BadRequest)
{
return authService.Redirect(session.ReferrerUrl.AddHashParam("f", "AccessTokenFailed"));
}
}
//Shouldn't get here
return authService.Redirect(session.ReferrerUrl.AddHashParam("f", "Unknown"));
The reason for it dropping through is the catch checks the response status code. In my scenario I am receiving 407 Proxy Authentication Required.
I've tracked it down further to the line:
var contents = accessTokenUrl.DownloadUrl();
Can anybody help with how I put in place the required proxy authentication?
For info, my app is running in a windows environment, it is run as an windows authenticated user so has permission access the proxy server, I just need to tell the code to use this - or any - credentials.
Thanks in anticipation
You can try setting the default proxy for .NET applications by setting the <defaultProxy/> in your Web.Config:
<configuration>
<system.net>
<defaultProxy>
<proxy
usesystemdefaults="true"
proxyaddress="http://192.168.1.10:3128"
bypassonlocal="true"
/>
<bypasslist
<add address="[a-z]+\.contoso\.com" />
</bypasslist>
</defaultProxy>
</system.net>
</configuration>
I'm creating unit tests for our application, and I'm stuck. For testing I have a simple HelloWebServlet that I'm protecting via annotations:
#WebServlet(urlPatterns = {"/hello"})
#ServletSecurity(#HttpConstraint(rolesAllowed = {"user"}))
I'm starting the server the way that's always worked OK (see [1]) and creating users (see [2]) seems to be OK because output from CommandRunner calls to list-file-users and list-file-groups are correct, but I'm getting this error when I try to connect using the username and password:
WARNING: WEB9102: Web Login Failed: com.sun.enterprise.security.auth.login.common.LoginException: Login failed: Unable to locate a login configuration
The calling code uses the Jersey client API:
#Test
public void testPingServletLoggedIn() {
Client client = Client.create();
client.addFilter(new HTTPBasicAuthFilter(GlassFishServerHelper.USERNAME, "xx"));
WebResource webResource = client.resource(GlassFishServerHelper.getBaseUri() + "/hello");
ClientResponse clientResponse = webResource
.accept(MediaType.TEXT_PLAIN)
.get(ClientResponse.class); // #GET
assertEquals(ClientResponse.Status.OK, clientResponse.getClientResponseStatus());
}
(Note: I tried to set javax.enterprise.system.core.security.level=FINE but that call failed with: PlainTextActionReporterFAILURENo configuration found for javax.enterprise.system.core.security . Drat!)
I've tried this against both glassfish-embedded-all-3.1.2.jar (our production version) and glassfish-embedded-all-3.2-b06.jar with the same results. What do you think would solve this? I've struggled and succeeded way too many times with GFE to give up without a fight!
==== [1] server startup (excerpt) ====
public static void startServer() {
GlassFishProperties gfProps = new GlassFishProperties();
gfProps.setPort("http-listener", PORT);
GLASSFISH = GlassFishRuntime.bootstrap().newGlassFish(gfProps);
GLASSFISH.start();
enableDefaultPrincipleToRoleMapping();
createUsersAndGroups();
ScatteredArchive archive = new ScatteredArchive(WEB_APP_NAME, ScatteredArchive.Type.WAR);
File classesDir = new File("out/production/simple-security-servlet-test");
archive.addClassPath(classesDir);
DEPLOYER = GLASSFISH.getDeployer();
APP_NAME = DEPLOYER.deploy(archive.toURI());
private static void enableDefaultPrincipleToRoleMapping() throws GlassFishException {
CommandRunner cr = GLASSFISH.getCommandRunner();
CommandResult result = cr.run("set",
"server-config.security-service.activate-default-principal-to-role-mapping=true");
}
==== [2] user creation (excerpt) ====
private static void createUsersAndGroups() throws GlassFishException {
CommandRunner commandRunner = GLASSFISH.getCommandRunner();
File passwordFile = new File("password-file.txt");
CommandResult result = commandRunner.run("create-file-user",
"--passwordfile", passwordFile.getAbsolutePath(),
"--groups", "user",
USERNAME
);
}
I was getting the same error and have managed to work around it, although my situation is slightly different. I'm starting GF 3.1.2 from the "maven-embedded-glassfish-plugin" maven plugin, but this might work for you as well.
Try setting the following system property:
java.security.auth.login.config=target/test-classes/login.conf
This should point to a copy of the login.conf file. You can find this file in the "config" folder in any Glassfish domain folder.