"CLIENT-CERT" based X509 certificate authentication in Grails 3.3 - ssl

I have been trying to mirror an implementation I had in Grails 2 for some time now as I try and upgrade to Grails 3.
I need to support X509 certificate based authentication using the "client-cert" auth method, that is, I only want to be prompted for a certificate once a protected resource has been requested.
See current implementation below in Application.groovy.
#Bean
EmbeddedServletContainerCustomizer containerCustomizer() throws Exception {
return new EmbeddedServletContainerCustomizer() {
#Override
public void customize(ConfigurableEmbeddedServletContainer container) {
TomcatEmbeddedServletContainerFactory tomcat = (TomcatEmbeddedServletContainerFactory) container
tomcat.addConnectorCustomizers(
new TomcatConnectorCustomizer() {
#Override
public void customize(Connector connector) {
connector.setPort(8443)
connector.setSecure(true)
connector.setScheme("https")
Http11NioProtocol proto = (Http11NioProtocol) connector.getProtocolHandler()
proto.setMinSpareThreads(5)
proto.setSSLEnabled(true)
proto.setClientAuth("false")
proto.setKeystoreFile("/tmp/keys/app.jks")
proto.setKeystorePass("changeit")
proto.setKeystoreType("JKS")
proto.setKeyAlias("ssl_server")
proto.setTruststoreFile("/tmp/keys/app.jts")
proto.setTruststoreType("JKS")
proto.setTruststorePass("changeit")
}
})
tomcat.addContextCustomizers(new TomcatContextCustomizer() {
#Override
public void customize(Context context) {
context.setPath("/myapp")
SecurityConstraint sc = new SecurityConstraint()
SecurityCollection securityCollection = new SecurityCollection()
securityCollection.setName("Protected")
securityCollection.addPattern("/*")
sc.addCollection(securityCollection)
sc.addAuthRole("mySecureConnection")
sc.setUserConstraint("CONFIDENTIAL")
context.addConstraint(sc)
context.addSecurityRole("mySecureConnection")
context.setRealm(new MySecurityRealm())
LoginConfig loginConfig = new LoginConfig()
loginConfig.setAuthMethod("CLIENT-CERT")
loginConfig.setRealmName("MySecurityRealm")
context.setLoginConfig(loginConfig)
sc.setAuthConstraint(true)
}
});
}
}
But no matter how many different ways I try and cut it, the application will not request a cert upon access (which it should based on my catch all pattern above).
Note that this mechanism does work as expected when clientAuth is set to true;
proto.setClientAuth("true")
but this means a cert is always requested which is not ultimately what I am looking for (I intend to update the pattern above).
Any help would be much appreciated.

Got this working myself in the after working on a proof of concept at the vanilla tomcat and Spring Boot level before returning to Grails 3.3.x to apply what worked.
I think probably the most important piece of the jigsaw was the addition of a tomcat valve component (using the SSLAuthenticator implementation obviously) which was the only way I could manage to get the browser to prompt for a certificate. This then required me to use a custom realm to retrieve the principal from the certificate (I know of no other way around this at present).
Code is as follows;
#Bean
public EmbeddedServletContainerFactory servletContainer() {
final TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory();
tomcat.addContextValves(new SSLAuthenticator());
tomcat.addContextCustomizers(new TomcatContextCustomizer() {
#Override
public void customize(Context ctx) {
String AUTH_ROLE = "mySecureRole";
ctx.addSecurityRole(AUTH_ROLE);
ctx.setRealm(new MySecurityRealm())
LoginConfig config = new LoginConfig();
config.setAuthMethod("CLIENT-CERT");
config.setRealmName("MySecurityRealm");
ctx.setLoginConfig(config);
SecurityConstraint constraint = new SecurityConstraint();
constraint.addAuthRole(AUTH_ROLE);
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/secure");
constraint.addCollection(collection);
ctx.addConstraint(constraint);
}
})
tomcat.addAdditionalTomcatConnectors(createConnector());
return tomcat;
}
private Connector createConnector() {
Connector connector = new Connector(TomcatEmbeddedServletContainerFactory.DEFAULT_PROTOCOL);
connector.setPort(8443);
connector.setSecure(true);
connector.setScheme("https");
Http11NioProtocol proto = (Http11NioProtocol) connector.getProtocolHandler();
proto.setMinSpareThreads(5);
proto.setSSLEnabled(true);
proto.setClientAuth("false");
proto.setSSLProtocol("all");
proto.setKeystoreFile("/path/store.jks");
proto.setKeystorePass("changeit");
proto.setKeystoreType("JKS");
proto.setKeyAlias("ssl_server");
proto.setTruststoreFile("/path/store.jts");
proto.setTruststoreType("JKS");
proto.setTruststorePass("changeit");
proto.setSSLVerifyDepth(2);
return connector;
}
I'm leaving in the connector details for completeness but of course all of the important stuff is happening in the context customizer.
Now, when I visit this web application I do not get prompted for a certificate. This only happens when I visit the /secure path which is exactly what I required.

Related

Apache Ignite performance problem on Azure Kubernetes Service

I'm using Apache Ignite on Azure Kubernetes as a distributed cache.
Also, I have a web API on Azure based on .NET6
The Ignite service works stable and very well on AKS.
But at first request, the API tries to connect Ignite and it takes around 3 seconds. After that, Ignite responses take around 100 ms which is great. Here are my Web API performance outputs for the GetProduct function.
At first, I've tried adding the Ignite Service to Singleton but it failed sometimes as 'connection closed'. How can I keep open the Ignite connection always? or does anyone has something better idea?
here is my latest GetProduct code,
[HttpGet("getProduct")]
public IActionResult GetProduct(string barcode)
{
Stopwatch _stopWatch = new Stopwatch();
_stopWatch.Start();
Product product;
CacheManager cacheManager = new CacheManager();
cacheManager.ProductCache.TryGet(barcode, out product);
if(product == null)
{
return NotFound(new ApiResponse<Product>(product));
}
cacheManager.DisposeIgnite();
_logger.LogWarning("Loaded in " + _stopWatch.ElapsedMilliseconds + " ms...");
return Ok(new ApiResponse<Product>(product));
}
Also, I add CacheManager class here;
public CacheManager()
{
ConnectIgnite();
InitializeCaches();
}
public void ConnectIgnite()
{
_ignite = Ignition.StartClient(GetIgniteConfiguration());
}
public IgniteClientConfiguration GetIgniteConfiguration()
{
var appSettingsJson = AppSettingsJson.GetAppSettings();
var igniteEndpoints = appSettingsJson["AppSettings:IgniteEndpoint"];
var igniteUser = appSettingsJson["AppSettings:IgniteUser"];
var ignitePassword = appSettingsJson["AppSettings:IgnitePassword"];
var nodeList = igniteEndpoints.Split(",");
var config = new IgniteClientConfiguration
{
Endpoints = nodeList,
UserName = igniteUser,
Password = ignitePassword,
EnablePartitionAwareness = true,
SocketTimeout = TimeSpan.FromMilliseconds(System.Threading.Timeout.Infinite)
};
return config;
}
Make it a singleton. Ignite node, even in client mode, is supposed to be running for the entire lifetime of your application. All Ignite APIs are thread-safe. If you get a connection error, please provide more details (exception stack trace, how do you create the singleton, etc).
You can also try the Ignite thin client which consumes fewer resources and connects instantly: https://ignite.apache.org/docs/latest/thin-clients/dotnet-thin-client.

Register Hibernate 5 Event Listeners

I am working on a legacy non-Spring application, and it is being migrated from Hibernate 3 to Hibernate 5.6.0.Final (latest at this time). I have generally never used Hibernate Event Listeners in my work, so this is quite new to me, and I am studying these in Hibernate 5.
Currently in some test class we have defined the code this way for Hibernate 3:
protected static Configuration createSecuredDatabaseConfig() {
Configuration config = createUnrestrictedDatabaseConfig();
config.setListener("pre-insert", "com.app.server.services.db.eventlisteners.MySecurityHibernateEventListener");
config.setListener("pre-update", "com.app.server.services.db.eventlisteners.MySecurityHibernateEventListener");
config.setListener("pre-delete", "com.app.server.services.db.eventlisteners.MySecurityHibernateEventListener");
config.setListener("pre-load", "com.app.server.services.db.eventlisteners.EkoSecurityHibernateEventListener");
return config;
}
This is obviously no longer valid, and I believe I need to create a Hibernate Integrator, which I have done.
public class MyEventListenerIntegrator implements Integrator {
#Override
public void integrate(Metadata metadata, SessionFactoryImplementor sessionFactory,
SessionFactoryServiceRegistry serviceRegistry) {
EventListenerRegistry eventListenerRegistry = serviceRegistry.getService(EventListenerRegistry.class);
eventListenerRegistry.getEventListenerGroup(EventType.PRE_INSERT).appendListener(new MySecurityHibernateEventListener());
eventListenerRegistry.getEventListenerGroup(EventType.PRE_UPDATE).appendListener(new MySecurityHibernateEventListener());
eventListenerRegistry.getEventListenerGroup(EventType.PRE_DELETE).appendListener(new MySecurityHibernateEventListener());
eventListenerRegistry.getEventListenerGroup(EventType.PRE_LOAD).appendListener(new MySecurityHibernateEventListener());
}
So, now I believe the next step is to add this to the session via the registry builder. I am using this website to help me:
https://www.boraji.com/hibernate-5-event-listener-example
Because we were using older Hibernate 3, we had code to create our session factory as follows:
protected static SessionFactory buildSessionFactory(Database db)
{
if (db == null) {
throw new NullPointerException("Database specifier cannot be null");
}
try {
Configuration config = createSessionFactoryConfiguration(db);
String url = config.getProperty("connection.url");
String user = config.getProperty("connection.username");
String password = config.getProperty("connection.password");
try {
String dbDriver = config.getProperty("hibernate.connection.driver_class");
Class.forName(dbDriver);
Connection conn = DriverManager.getConnection(url, user, password);
}
catch (SQLException error) {
logger.info("Didn't find driver, on QA or production, so it's okay to assume we have DB connection");
error.printStackTrace();
}
SessionFactory sessionFactory = config.buildSessionFactory();
sessionFactoryConfigs.put(sessionFactory, config); // Cannot recover config from factory instance, must be stored.
return sessionFactory;
}
catch (Throwable ex) {
// Make sure you log the exception, as it might be swallowed
logger.error("Initial SessionFactory creation failed.", ex);
throw new ExceptionInInitializerError(ex);
}
}
The link that I referred to above has a much different way of creating the sessionfactory. So, I'll be testing that out to see if it works in our app.
Without Spring handling our sessions and transactions, in this app it is coded by hand the way it was done before Spring, and I haven't seen that kind of code in years.
I solved this issue with the help from the link I provided above. However, I didn't copy exactly what they did, but some of it helped. My solution is as follows:
protected static SessionFactory createSecuredDatabaseConfig() {
Configuration config = createUnrestrictedDatabaseConfig();
BootstrapServiceRegistry bootstrapRegistry =
new BootstrapServiceRegistryBuilder()
.applyIntegrator(new EkoEventListenerIntegrator())
.build();
ServiceRegistry serviceRegistry = new StandardServiceRegistryBuilder(bootstrapRegistry).applySettings(config.getProperties()).build();
SessionFactory sessionFactory = config.buildSessionFactory(serviceRegistry);
return sessionFactory;
}
This was it. I tried multiple different ways to register the events without the BootstrapServiceRegistry, but none of those worked. I did have to create the integrator. What I did NOT include was the following:
MetadataSources sources = new MetadataSources(serviceRegistry )
.addPackage("com.myproject.server.model");
Metadata metadata = sources.getMetadataBuilder().build();
// did not create the sessionFactory this way
sessionFactory = metadata.getSessionFactoryBuilder().build();
If I had gone further and use this method to create the sessionFactory, then all of my queries would have been complaining about not being able to find the parameterName, which is something else.
The Hibernate Integrator and this method to create the sessionFactory is all for the unit tests. Without registering these events, one unit test would fail, and now it doesn't. So, this solves my problem for now.

How i can sing up with restcomm correctly?

Already few weeks i try sing up on SIP service with restcomm for android. I check connection with third-party application (cSipSimple) and everything works correctly. But when i try coonection with restcomm demo app, connection falls everytime after 4 seconds. Whats wrong with my sdk or how i can sing up right?
SipProfile sipProfile = new SipProfile();
HashMap<String, Object> params = new HashMap<String, Object>();
params.put("pref_proxy_ip", "my.server.ip");
params.put("pref_proxy_port", "5060");
params.put("pref_sip_user", "7879114");
params.put("pref_sip_password", "EeFei2Fa");
for (String key : params.keySet()) {
if (key.equals("pref_proxy_ip")) {
sipProfile.setRemoteIp((String) params.get(key));
} else if (key.equals("pref_proxy_port")) {
sipProfile.setRemotePort(Integer.parseInt((String) params.get(key)));
} else if (key.equals("pref_sip_user")) {
sipProfile.setSipUserName((String) params.get(key));
} else if (key.equals("pref_sip_password")) {
sipProfile.setSipPassword((String) params.get(key));
}
}
final SipManager sipManager = new SipManager(sipProfile, true);
Register registerRequest = new Register();
final Request r = registerRequest.MakeRequest(sipManager, 100000, null);
// Send the request statefully, through the client transaction.
Thread thread = new Thread() {
public void run() {
try {
final ClientTransaction transaction = sipManager.sipProvider.getNewClientTransaction(r);
transaction.sendRequest();
} catch (SipException e) {
e.printStackTrace();
}
}
};
thread.start();
#Vladislav, you are using the low level facilities of the SDK which are obsolete and not meant to be used directly. I would suggest that you use the RestCommClient API as exposed by the SDK directly. It is much easier to use and provides the same functionality and more.
For an example on how to use it please check:
https://github.com/RestComm/restcomm-android-sdk/blob/master/Examples/restcomm-helloworld/app/src/main/java/com/telestax/restcomm_helloworld/MainActivity.java
You need to change the SIP server settings from:
https://github.com/RestComm/restcomm-android-sdk/blob/master/Examples/restcomm-helloworld/app/src/main/java/com/telestax/restcomm_helloworld/MainActivity.java#L99
And the called party from:
https://github.com/RestComm/restcomm-android-sdk/blob/master/Examples/restcomm-helloworld/app/src/main/java/com/telestax/restcomm_helloworld/MainActivity.java#L174
Just keep in mind that for media Webrtc is used so that NATs can be handled properly. This means that the receiving party needs to be able to handle Webrtc as well. Unless there's a server in the middle handling the mediation, like Restcomm-Connect.
For more information please check RestComm Client Android SDK Quick Start

How to list JBoss AS 7 datasource properties in Java code?

I'm running JBoss AS 7.1.0.CR1b. I've got several datasources defined in my standalone.xml e.g.
<subsystem xmlns="urn:jboss:domain:datasources:1.0">
<datasources>
<datasource jndi-name="java:/MyDS" pool-name="MyDS_Pool" enabled="true" use-java-context="true" use-ccm="true">
<connection-url>some-url</connection-url>
<driver>the-driver</driver>
[etc]
Everything works fine.
I'm trying to access the information contained here within my code - specifically the connection-url and driver properties.
I've tried getting the Datasource from JNDI, as normal, but it doesn't appear to provide access to these properties:
// catches removed
InitialContext context;
DataSource dataSource = null;
context = new InitialContext();
dataSource = (DataSource) context.lookup(jndi);
ClientInfo and DatabaseMetadata from a Connection object from this Datasource also don't contain these granular, JBoss properties either.
My code will be running inside the container with the datasource specfied, so all should be available. I've looked at the IronJacamar interface org.jboss.jca.common.api.metadata.ds.DataSource, and its implementing class, and these seem to have accessible hooks to the information I require, but I can't find any information on how to create such objects with these already deployed resources within the container (only constructor on impl involves inputting all properties manually).
JBoss AS 7's Command-Line Interface allows you to navigate and list the datasources as a directory system. http://www.paykin.info/java/add-datasource-programaticaly-cli-jboss-7/ provides an excellent post on how to use what I believe is the Java Management API to interact with the subsystem, but this appears to involve connecting to the target JBoss server. My code is already running within that server, so surely there must be an easier way to do this?
Hope somebody can help. Many thanks.
What you're really trying to do is a management action. The best way to is to use the management API's that are available.
Here is a simple standalone example:
public class Main {
public static void main(final String[] args) throws Exception {
final List<ModelNode> dataSources = getDataSources();
for (ModelNode dataSource : dataSources) {
System.out.printf("Datasource: %s%n", dataSource.asString());
}
}
public static List<ModelNode> getDataSources() throws IOException {
final ModelNode request = new ModelNode();
request.get(ClientConstants.OP).set("read-resource");
request.get("recursive").set(true);
request.get(ClientConstants.OP_ADDR).add("subsystem", "datasources");
ModelControllerClient client = null;
try {
client = ModelControllerClient.Factory.create(InetAddress.getByName("127.0.0.1"), 9999);
final ModelNode response = client.execute(new OperationBuilder(request).build());
reportFailure(response);
return response.get(ClientConstants.RESULT).get("data-source").asList();
} finally {
safeClose(client);
}
}
public static void safeClose(final Closeable closeable) {
if (closeable != null) try {
closeable.close();
} catch (Exception e) {
// no-op
}
}
private static void reportFailure(final ModelNode node) {
if (!node.get(ClientConstants.OUTCOME).asString().equals(ClientConstants.SUCCESS)) {
final String msg;
if (node.hasDefined(ClientConstants.FAILURE_DESCRIPTION)) {
if (node.hasDefined(ClientConstants.OP)) {
msg = String.format("Operation '%s' at address '%s' failed: %s", node.get(ClientConstants.OP), node.get(ClientConstants.OP_ADDR), node.get(ClientConstants.FAILURE_DESCRIPTION));
} else {
msg = String.format("Operation failed: %s", node.get(ClientConstants.FAILURE_DESCRIPTION));
}
} else {
msg = String.format("Operation failed: %s", node);
}
throw new RuntimeException(msg);
}
}
}
The only other way I can think of is to add module that relies on servers internals. It could be done, but I would probably use the management API first.

Providing workflow extensions to a workflow service - WF 4.0

Greetings one and all!
I'm new to WF 4.0 and WWF in general so forgive me if this seems like a newbie type of question, but believe me I've scoured the depths of the Internet for a solution to this problem, but to no avail.
I have created a sample WF application with a custom CodeActivity that requires an extension be provided, as per below:
public sealed class PreparePizza : CodeActivity
{
public InArgument<Order> Order { get; set; }
protected override void CacheMetadata(CodeActivityMetadata metadata)
{
base.CacheMetadata(metadata);
if (this.Order == null)
metadata.AddValidationError("You must supply an Order.");
metadata.RequireExtension<IPreparePizzaExtension>();
}
// If your activity returns a value, derive from CodeActivity<TResult>
// and return the value from the Execute method.
protected override void Execute(CodeActivityContext context)
{
// Obtain the runtime value of the Text input argument
Order order = context.GetValue(this.Order);
var extension = context.GetExtension<IPreparePizzaExtension>();
extension.Prepare(order);
}
}
public interface IPreparePizzaExtension
{
void Prepare(Order order);
}
I then slot this activity into a workflow service and attempt to consume via my web app by adding a service reference. However, when I add the reference I get:
System.Activities.ValidationException: An extension of type 'PizzaMan.ActivityLibrary.IPreparePizzaExtension' must be configured in order to run this workflow.
Fair enough - of course my activity requires that I pass it an implementation of IPreparePizzaExtension - after all, I've told it to!
So my question is, how on earth do I pass this to the service? I can manage this easily enough in a console app scenario, using the WorkflowInvoker, but I cannot see any obvious way to do this via the service approach. I would assume that obviously a programmatic approach to adding the reference is what's needed, but again I'm at a loss as to precisely how to go about this.
Any help would be greatly appreciated.
Best regards
Ian
The WorkflowServiceHost has a WorkflowExtensions property where you can add the workflow extenstion. There are several ways you can do that. If you are self hosting this is easy as you create the WorkflowServiceHost. If you are usign IIS you need to create a ServiceHostFactory to configure you WorkflowServiceHost. Finally there is an option to add the workflow extension in the CacheMetadata of your activity using the metadata.AddDefaultExtensionProvider() function.
Solved it as follows, self-hosting style:
static void Main(string[] args)
{
Workflow1 workflow = new Workflow1();
// Provide some default values; note: these will be overriden once method on the service is called.
workflow.productID = -1;
Uri address = new Uri("http://localhost:1234/WorkflowService1");
WorkflowServiceHost host = new WorkflowServiceHost(workflow, address);
// Behaviours
host.Description.Behaviors.Add(new ServiceMetadataBehavior { HttpGetEnabled = true });
host.Description.Behaviors.Remove(typeof(ServiceDebugBehavior));
host.Description.Behaviors.Add(new ServiceDebugBehavior { IncludeExceptionDetailInFaults = true });
// Persistence
var connStr = #"";
var behavior = new SqlWorkflowInstanceStoreBehavior(connStr);
behavior.InstanceCompletionAction = InstanceCompletionAction.DeleteNothing;
behavior.InstanceLockedExceptionAction = InstanceLockedExceptionAction.AggressiveRetry;
behavior.InstanceEncodingOption = InstanceEncodingOption.None;
host.Description.Behaviors.Add(behavior);
// Add extension implementations
if (!TEST_MODE)
{
host.WorkflowExtensions.Add(new MyExtension());
}
else
{
host.WorkflowExtensions.Add(new MyExtensionTest());
}
host.Faulted += new EventHandler(host_Faulted);
host.Open();
foreach (System.ServiceModel.Description.ServiceEndpoint endpoint in host.Description.Endpoints)
{
Console.WriteLine(endpoint.Address);
}
Console.WriteLine("Listening...");
Console.ReadLine();
host.Close();
}
My toolkit has configuration support for this. See http://neovolve.codeplex.com/wikipage?title=Neovolve.Toolkit.Workflow.dll%20-%201.1
There is also this method of doing things:
http://wf.codeplex.com/wikipage?title=How%20do%20I%20add%20an%20extension%20to%20a%20WCF%20Workflow%20Service?