My Azure Web App (running on Tomcat9) successfully connects to the Azure SQL database by having the following in META-INF/context.xml
<Resource name="jdbc/myDB" type="javax.sql.DataSource" auth="Container"
factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver" initialSize="30"
maxActive="100" validationQuery="SELECT 1"
validationQueryTimeout="1000"
testOnBorrow="true"
url="jdbc:sqlserver://myserver.database.windows.net:1433;database=myDatabase;encrypt=true;trustServerCertificate=true;loginTimeout=10;user=myuser@myserver;password=${dbPassword}"
/>
And having the following CATALINA_OPTS
application setting in the WebApps Configuration
-Ddbpassword=<my password>
I want to upgrade my security, so I added a secret in the Key Vault called DB-PASSWORD
and changed the CATALINA_OPTS
application setting in the WebApps Configuration as follows
-Ddbpassword=@Microsoft.KeyVault(SecretUri=https://thekeyvaulttest.vault.azure.net/secrets/DB-PASSWORD/)
I used this guide from Microsoft.
However the server failed to start, saying
/usr/local/tomcat/bin/catalina.sh: eval: line 1: syntax error: unexpected "("
The WebApp has permission to read secrets from the Key Vault. This does not seem to be an authentication issue such as in this question
The Java code to read context.xml is as follows:
public final class DBConnection {
private static DataSource ds = null;
private static DBConnection instance = null;
private DBConnection() throws NamingException {
InitialContext ic = new InitialContext();
ds = (DataSource) ic.lookup("jdbc/mydb");
}
public static synchronized DBConnection getInstance() {
if (instance == null) {
try {
instance = new DBConnection();
}
} catch (Exception e2) {
}
}
return instance;
}
public Connection getConnection() {
Connection con = null;
try {
con = ds.getConnection();
} catch (SQLException e) {
System.out.println(e.toString());
}
return con;
}
public void returnConnection(Connection con) {
try {
con.close();
} catch (SQLException e) {
System.out.println(e.toString());
} finally {
try {
con.close();
} catch (SQLException e) {
System.out.println(e.toString());
}
}
return;
}
}
One workaround that works is to use BasicDataSource
instead of DataSource
and load the parameters in Java. This bypasses the need for defining the connection in context.xml completely.
String keyVaultUrl = "https://thekeyvaulttest.vault.azure.net/";
SecretClient secretClient = new SecretClientBuilder()
.vaultUrl(keyVaultUrl)
.credential(new DefaultAzureCredentialBuilder().build())
.buildClient();
ds=new BasicDataSource();
String url = "jdbc:sqlserver://myserver.database.windows.net:1433;database=myDatabase;encrypt=true;trustServerCertificate=true;loginTimeout=10;";
String username = "myuser@myserver";
String password = secretClient.getSecret("DB-PASSWORD").getValue();
ds.setDriverClassName("com.microsoft.sqlserver.jdbc.SQLServerDriver");
ds.setUrl(url);
ds.setMaxTotal(100);
ds.setUsername(username);
ds.setPassword(password);
ds.setInitialSize(30);
ds.setValidationQuery("SELECT 1");
ds.setTestOnBorrow(true);