7

I don't know, how to approach a solution for the following scenario.

We have a new requirement to remove DB Password from properties even though it's encrypted with Jasypt library or some other algorithms.

Instead of storing the password in properties or LDAP, we need to fetch it dynamically from Cyberark.

Password may expire in a day or two or in a week or in a month. It totally depends on Password expiration policy.

We have multiple projects. Some are web-based and some are standalone. We want to write a generic solution.

How to override getConnection method of any data source like Spring data source, Apache Basic data source (it support extending class), C3P0, DBCP or HikariCP without impacting their behavior and setting the password before hitting super.getConnection()?

super.getConnection(); // Here max attempt  will be 3

Spring supports method replacement, but I don't know what will be the impact on the connection pooling framework.

Let me know if you need more details.

Pavel Molchanov
  • 2,299
  • 1
  • 19
  • 24
Viraj
  • 1,360
  • 3
  • 18
  • 38
  • First of all, the requirement is a good one - "encrypting" passwords in source code is a naive solution that adds nothing to security - your encryption password must in in plaintext to decrypt; so well done to your security team. – Boris the Spider Jun 25 '18 at 06:36
  • 1
    As to your current problem, Wrap It!. Create a `PasswordRenewalDatasource` and wrap the data database's native `Datasource` - when the password expries (ideally before it does) renew the underlying `Datasrouce`. Pass the `PasswordRenewalDatasource` to your connection pool. – Boris the Spider Jun 25 '18 at 06:38
  • Thanks Boris, Well that's what I am doing currently, But if I want to use above mentioned data connection pooling library then i don't know, how to achieve this or rather what will be impact. – Viraj Jun 25 '18 at 06:44
  • As I say, simply pass your pool to that pool! For Hikari CP use `dataSourceClassName` - then configure as normal, you will all need to pass the renewal properties. – Boris the Spider Jun 25 '18 at 06:45
  • Okay Boris, Let me check, will get back to you on this. – Viraj Jun 25 '18 at 06:48
  • 1
    If you load datasource properties from properties file you could use AOP and create aspect for `Properties.load()`. Aspect will check if property name has `password` in it (or you could use any other more suitable check) and then go to Cyberark to get actual password – Ivan Jun 26 '18 at 18:00

1 Answers1

0

To solve your problem you can use spring-cloud-context library and its @RefreshScope annotation. Also, it will be needed for you to develop a bit.

1) You need a special watcher bean which will monitor if the password was changed. It will be smth like this:

@Service
public class Watcher {
    private final ContextRefresher refresher;

    public Watcher(ContextRefresher refresher) {
        this.refresher = refresher;
    }

    @Scheduled(fixedDelay = 10000L)
    public void monitor() {
        if (/* smth changed*/) {
            refresher.refresh();
        }
    }
}

So, when you call refresher.refresh(); all beans annotated with @RefreshContext will be disposed and recreated after the first access to them.

2) Annotate your datasource bean with @RefreshContext annotation. 3) You have to provide password to be accessed using @ConfigurationProperties annotation. You will need to create SourceLocator. It will be smth like this

@Order(0)
public class SourceLocator implements PropertySourceLocator {
    @Override
    public PropertySource<?> locate(Environment environment) {
        //Load properties to hash map
        return new MapPropertySource("props", new HashMap<>());
    }
}

Also, create a file spring.factories and put the following data there:

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
com.test.YourSourceLocator

4) Create properties class where your db pass will be held and refreshed.

@RefreshScope
@ConfigurationProperties(prefix="your.prefix")
public class Properties {
    private String dbPassword;
}

Autowire this bean to the configuration where you create your datasource and use password from it.

D. Krauchanka
  • 264
  • 3
  • 15
  • 1
    If the question is specific to Spring framework you shouldn't start bringing in Cloud, and by transitive dependencies, boot dependencies. IT will screw you over in the long run. Additionally refresh scope was made for spring cloud config servers. A better solution is wrapping the datasource in another datasource impl to refresh if the call fails and re-attempting with refreshed credentials. – Darren Forsythe Jun 27 '18 at 22:52
  • `@RefreshScope` wasn't made only for config servers. Spring-cloud-config just uses the same approach. It is not a good idea to wrap a logic of getting configuration to datasource. Instead it will be better to have the same Watcher, which will monitor changes and will set new password and `softReset()` the datasource. But you will have to use `com.mchange.v2.c3p0.ComboPooledDatasource` in this case. – D. Krauchanka Jun 28 '18 at 05:37
  • Thanks for answer Krauchanka, But I wan to fetch password only when new get Connection fails to load connection, then want to try with new password and max attempt 3, so can't go in loop. – Viraj Jun 28 '18 at 11:58