Using Apache/mod_ssl certificate and private key with Tomcat/keytool
Hi, I have an Apache+mod_ssl+Tomcat configuration that's been working fine for several years. I have an SSL certificate from Verisign, and my httpd.conf file contains: SSLCertificateFile /path/to/server.crt SSLCertificateKeyFile /path/to/server.key The private key is unencrypted so that the server can restart automatically. Now I'd like to use the same certificate and private key in a Tomcat-only configuration, but I can't quite figure out how to get these two pieces of information into keytool for tomcat to use! It's easy enough to import the certificate: keytool -import -alias tomcat -file /path/to/server.crt but I know that the private key needs to be in the keystore too, and I haven't been able to figure out how to get it in there! Simply trying to import it: keytool -import -alias tomcat -file /path/to/server.key gives me the message: keytool error: java.lang.Exception: Input not an X.509 certificate which doesn't really surprise me because the private key is not an X.509 certificate! But how can I tell keytool about my private key? Can I do this? If so, how? Can I do it with just keytool? Do I need to use openssl to tweak something? I saw some comments in the httpd.conf file (comments added by mod_ssl) that suggest the certificate and the private key can be combined somehow. Is this what I need to do? If so, how do I do this? Or do I have to toss my old keys and generate a new CSR with keytool? The Tomcat tutorial on how to do that seems reasonably straightforward. But I would much prefer to use my existing key and certificate! I actually tried this for the first time two years ago. After trying everything I could think of, and posting to tomcat-user and getting no replies, I gave up and left things the way they were. Now, two years later, I *still* can't figure out, or find a recipe, to explain how to migrate from an Apache/mod_ssl/Tomcat configuration to a plain Tomcat configuration! Thanks for any help. Scott - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Followup to Valve As Custom Authenticator
I just wanted to follow up on my earlier post, and thank Tim Funk and Bill Barker for leading me in the right direction. It turned out that my Valve code was in fact mostly correct; I was just installing it in the wrong place! I had it installed at the Engine level, where all sorts of weird things happened. Once I moved it to the Host level, everything seemed to start working a whole lot better. For the benefit of anyone who ends up trying to take the same custom valve approach to authentication and authorization that we are using, here's a short list of things I wish I'd known when starting out that would have made things go more quickly: - Write a Valve that extends ValveBase - implement Authenticator and Lifecycle in your valve - implement invoke() - if the request needs to be logged in, do a redirect hres.sendRedirect(hres.encodeRedirectURL(loginPage)) or, alternately, you can output the login page html code directly from the valve - when the request should be allowed, call hrequest.setUserPrincipal(new YourPrincipal(username)) hrequest.setAuthType(BASIC) Other authenticators will see this and not request redundant logins. - Write your own implementation of Principal. - To handle authorization, write a Realm. - To allow a Valve and a Realm to exchange information, use the Principal. If both your Realm and your Valve are installed, the Principal seen by the Realm will be the one attached to the request by your Valve. - Install both the Valve and the Realm at the top of the Host section of server.xml. - Tomcat uses introspection to handle Valve properties, so you can create arbitrary bean-style getters and setters to configure your Valve via server.xml. - Tip: the start() method of the Lifecycle interface is a good place to check to see if your Valve has been properly configured. If not, you can throw LifecycleException and keep the server from starting up. Please note that this is NOT a tutorial! You will have to look at the Tomcat docs and source code to figure out how to do all this. But the above list might make it clearer where to start looking. (And before writing to ask me to explain how to do this; please be aware that pretty much everything *I* know is in the list above, and even that may not be entirely correct! Everything else is in the Tomcat source...) And this list only considers how to talk to Tomcat, not how you should be actually authenticating and authorizing requests-- that's a separate issue. Also, for anyone considering this approach, be aware that it's reasonably straightforward once you understand Tomcat's architecture, but the learning curve is somewhat steep at first! At least that's been our experience. Hope this helps someone, Scott - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Valve as Custom Authenticator
We've had an Apache/Tomcat configuration deployed for a couple years now. Authentication is handled by a custom Apache plugin written in C. Everything works great and has been quite reliable. Now we would like to move to a standalone Tomcat configuration and have been investigating writing a Valve/Authenticator to replace our existing Apache plugin. I've written a prototype Valve and it does almost everything we need. This gives us the ability to require a server-wide login independent of how the individual servlet contexts are configured. This ends up being Tomcat-specific, but we're ok with that. The only problem with the current prototype is that if a user hits a servlet or JSP in a Context that's configured for basic authentication, they still get the browser-generated basic login dialog, even after being logged in with our Valve. In my code, I check for a particular cookie, and if I find it, I set the user principal in the request to the appropriate user, something like this: // Has connection already been authenticated // (i.e. do we have the login cookie?) Cookie lcookie=ValveUtils.findCookie(hreq,LOGIN_COOKIE_NAME); // If the request has the login cookie, let it pass through if (lcookie!=null) { log(Found login cookie, validating); if (validLoginCookie(lcookie,hreq)) { log(cookie is valid, allowing request); // See AuthenticatorBase.invoke(), which also sets authType and userPrincipal // See SignleSignOn.invoke(), which also set authType and userPrincipal hrequest.getRequest().setUserPrincipal(new TempPrincipal(bob_temp_user)); hrequest.getRequest().setAuthType(BASIC); context.invokeNext(request,response); } else { log(cookie not valid, going to error page); hres.sendRedirect(hres.encodeRedirectURL(ERROR_PAGE_URI)); } return; } I had thought that this would work, because later in the pipeline the request hits BasicAuthenticator, which does this: public boolean authenticate(HttpRequest request, HttpResponse response, LoginConfig config) throws IOException { // Have we already authenticated someone? Principal principal = ((HttpServletRequest) request.getRequest()).getUserPrincipal(); if (principal != null) { if (debug = 1) log(Already authenticated ' + principal.getName() + '); return (true); } And since I've set the principal to something besides null, I had assumed that the basic authentication would just be skipped. Which is not the behavior that I'm seeing. Instead, I end up asking the user to log in twice: once for my custom Valve (which is a web form/redirect thing), and a second time when they hit a servlet in a context with basic authentication (which causes the browser to put up the basic authentication dialog). So, what am I missing? Thanks, Scott - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Valve as Custom Authenticator
SingleSignOn has much the same code as BasicAuthenticator: in org.apache.catalina.authenticator.SingleSignOn.java: // Look up the cached Principal associated with this cookie value if (debug = 1) log( Checking for cached principal for + cookie.getValue()); SingleSignOnEntry entry = lookup(cookie.getValue()); if (entry != null) { if (debug = 1) log( Found cached principal ' + entry.principal.getName() + ' with auth type ' + entry.authType + '); request.setNote(Constants.REQ_SSOID_NOTE, cookie.getValue()); ((HttpRequest) request).setAuthType(entry.authType); ((HttpRequest) request).setUserPrincipal(entry.principal); } else { if (debug = 1) log( No cached principal found, erasing SSO cookie); cookie.setMaxAge(0); hres.addCookie(cookie); } // Invoke the next Valve in our pipeline context.invokeNext(request, response); The only piece of that I'm not doing is the setNote() part; I *think* that's only used for the form-based login stuff, but I'm not entirely sure-- that's probably the part of the puzzle I still don't understand. The basic authenicator doesn't seem to use it. Scott At 3:10 PM -0400 6/11/03, Tim Funk wrote: I don't have time to see whats wrong, but check the SingleSignOnValve as a reference to compare your code. -Tim Scott Kelley wrote: We've had an Apache/Tomcat configuration deployed for a couple years now. Authentication is handled by a custom Apache plugin written in C. Everything works great and has been quite reliable. Now we would like to move to a standalone Tomcat configuration and have been investigating writing a Valve/Authenticator to replace our existing Apache plugin. I've written a prototype Valve and it does almost everything we need. This gives us the ability to require a server-wide login independent of how the individual servlet contexts are configured. This ends up being Tomcat-specific, but we're ok with that. The only problem with the current prototype is that if a user hits a servlet or JSP in a Context that's configured for basic authentication, they still get the browser-generated basic login dialog, even after being logged in with our Valve. In my code, I check for a particular cookie, and if I find it, I set the user principal in the request to the appropriate user, something like this: // Has connection already been authenticated // (i.e. do we have the login cookie?) Cookie lcookie=ValveUtils.findCookie(hreq,LOGIN_COOKIE_NAME); // If the request has the login cookie, let it pass through if (lcookie!=null) { log(Found login cookie, validating); if (validLoginCookie(lcookie,hreq)) { log(cookie is valid, allowing request); // See AuthenticatorBase.invoke(), which also sets authType and userPrincipal // See SignleSignOn.invoke(), which also set authType and userPrincipal hrequest.getRequest().setUserPrincipal(new TempPrincipal(bob_temp_user)); hrequest.getRequest().setAuthType(BASIC); context.invokeNext(request,response); } else { log(cookie not valid, going to error page); hres.sendRedirect(hres.encodeRedirectURL(ERROR_PAGE_URI)); } return; } I had thought that this would work, because later in the pipeline the request hits BasicAuthenticator, which does this: public boolean authenticate(HttpRequest request, HttpResponse response, LoginConfig config) throws IOException { // Have we already authenticated someone? Principal principal = ((HttpServletRequest) request.getRequest()).getUserPrincipal(); if (principal != null) { if (debug = 1) log(Already authenticated ' + principal.getName() + '); return (true); } And since I've set the principal to something besides null, I had assumed that the basic authentication would just be skipped. Which is not the behavior that I'm seeing. Instead, I end up asking the user to log in twice: once for my custom Valve (which is a web form/redirect thing), and a second time when they hit a servlet in a context with basic authentication (which causes the browser to put up the basic authentication dialog). So, what am I missing? Thanks, Scott - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Re: Valve as Custom Authenticator
Aha, you're right! First problem, I didn't have the right logging enabled; I hadn't noticed that I didn't see any of the log messages from BasicAuthenticator.java. Second problem, with logging cranked up appropriately, is that BasicAuthenticator gets called before my code. Which leads to my next question: how can I get called before BasicAuthenticator? I had the Valve installed as the first item in the Engine; when it's there it apparently gets called after BasicAuthenticator. I tried moving my valve up a level into the Service, but my code didn't seem to get called at all when I installed the valve there. Let me ask a related question: If I have a valve that's setting the principal and authType, do I need to set a Realm in server.xml, and if so, what should I set it to? Do I need to write a Realm that somehow talks to my Valve (since the request info isn't available in Tomcat 4)? This is, I think, the piece of the puzzle that I don't completely understand. Scott At 5:03 PM -0400 6/11/03, Tim Funk wrote: I took a little longer look. All looks ok to me. Turn up debugging. Are you sure that your valve is being executed before the BasicAuthenticator valve? -Tim Scott Kelley wrote: SingleSignOn has much the same code as BasicAuthenticator: in org.apache.catalina.authenticator.SingleSignOn.java: // Look up the cached Principal associated with this cookie value if (debug = 1) log( Checking for cached principal for + cookie.getValue()); SingleSignOnEntry entry = lookup(cookie.getValue()); if (entry != null) { if (debug = 1) log( Found cached principal ' + entry.principal.getName() + ' with auth type ' + entry.authType + '); request.setNote(Constants.REQ_SSOID_NOTE, cookie.getValue()); ((HttpRequest) request).setAuthType(entry.authType); ((HttpRequest) request).setUserPrincipal(entry.principal); } else { if (debug = 1) log( No cached principal found, erasing SSO cookie); cookie.setMaxAge(0); hres.addCookie(cookie); } // Invoke the next Valve in our pipeline context.invokeNext(request, response); The only piece of that I'm not doing is the setNote() part; I *think* that's only used for the form-based login stuff, but I'm not entirely sure-- that's probably the part of the puzzle I still don't understand. The basic authenicator doesn't seem to use it. Scott At 3:10 PM -0400 6/11/03, Tim Funk wrote: I don't have time to see whats wrong, but check the SingleSignOnValve as a reference to compare your code. -Tim Scott Kelley wrote: We've had an Apache/Tomcat configuration deployed for a couple years now. Authentication is handled by a custom Apache plugin written in C. Everything works great and has been quite reliable. Now we would like to move to a standalone Tomcat configuration and have been investigating writing a Valve/Authenticator to replace our existing Apache plugin. I've written a prototype Valve and it does almost everything we need. This gives us the ability to require a server-wide login independent of how the individual servlet contexts are configured. This ends up being Tomcat-specific, but we're ok with that. The only problem with the current prototype is that if a user hits a servlet or JSP in a Context that's configured for basic authentication, they still get the browser-generated basic login dialog, even after being logged in with our Valve. In my code, I check for a particular cookie, and if I find it, I set the user principal in the request to the appropriate user, something like this: // Has connection already been authenticated // (i.e. do we have the login cookie?) Cookie lcookie=ValveUtils.findCookie(hreq,LOGIN_COOKIE_NAME); // If the request has the login cookie, let it pass through if (lcookie!=null) { log(Found login cookie, validating); if (validLoginCookie(lcookie,hreq)) { log(cookie is valid, allowing request); // See AuthenticatorBase.invoke(), which also sets authType and userPrincipal // See SignleSignOn.invoke(), which also set authType and userPrincipal hrequest.getRequest().setUserPrincipal(new TempPrincipal(bob_temp_user)); hrequest.getRequest().setAuthType(BASIC); context.invokeNext(request,response); } else { log(cookie not valid, going to error page); hres.sendRedirect(hres.encodeRedirectURL(ERROR_PAGE_URI)); } return; } I had thought that this would work, because later in the pipeline the request hits BasicAuthenticator, which does this: public boolean authenticate(HttpRequest request, HttpResponse response, LoginConfig config) throws IOException
Changing *Default* allowLinking Behavior
Hi Tomcat Users, I'm having a problem with one of my server configurations (which has come up repeatedly on this list) where various items in my servlet contexts are unix symbolic links (on Solaris, RedHat, or MacOS X), and since Tomcat 4.1.18 by default disallows following these symlinks for security reasons, the configuration no longer works under 4.1.18. Previously we ran this way with Tomcat 4.0.x and 3.x with no problems, but the symlink behavior apparently changed in 4.1.x. The solution (which has also come up repeatedly on the list) is apparently to use the allowLinking flag, like this: Context ... some valid context ... Resources className=org.apache.naming.resources.FileDirContext allowLinking=true / /Context Now this is all well and good, and I'm pretty sure it will solve our problem. It only has one drawback... we have many servlet contexts, and we have never bothered to list them all in server.xml; we just let the default context apply to everything. So, is there a way to configure server.xml such that the *default* behavior will be allowLinking=true, or do we have to manually list every servlet context in server.xml? From reading the documentation, I thought that something like this might work: Engine name=Standalone defaultHost=localhost debug=0 DefaultContext Resources className=org.apache.naming.resources.FileDirContext allowLinking=true / /DefaultContext /Engine but I tried it and it didn't seem to work using 4.1.18. Also, I couldn't find any mention of this on the list; does anybody use a configuration like this? Oh, and in case anyone's curious, the configuration we use with all the symlinks is one of our development configurations; our deployment builds do not use symlinks at all, so the problem (and the security issue) doesn't come up. Any tips appreciated. Thanks, Scott Kelley P.S. No, we don't want to change the default value in FileDirContext.java and recompile tomcat, but thanks for the suggestion! :-) - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]
Adding security constraints on a per-server basis
Hi, I'm using Tomcat 3.2.1 on Solaris. Is it possible to add a security-constraint tag somewhere so that it will apply to the entire server at once? I want to lock down my whole development server at once, instead of having to write separate web.xml files for each application. I tried adding a security constraint to the web.xml file in Tomcat's conf/web.xml file, in the hope that adding to the default servlet would make it apply to all servlets, but it didn't seem to work. Is it supposed to? If this does work, does anyone have an example? Thanks, Scott -- -- Scott Kelley, [EMAIL PROTECTED] Biology Computing Services, UC San Diego --
Importing OpenSSL Private Key with keytool
Is it simply impossible to import a private key from openssl into a keystore using keytool? And, if so... then am I missing something? Doesn't this make it impossible to use Tomcat's built-in SSL support with anything other than a self-signed key, or a key generated by keytool itself? If it's not impossible, can somebody please explain to me how to do it? If it is impossible, is this a limitation of keytool, or the java.security API? Could I do it with a short piece of code? Thanks, Scott -- Scott Kelley, [EMAIL PROTECTED] Biology Computing Services, UC San Diego -- - To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, email: [EMAIL PROTECTED]