This is the part 5 of application development series. Refer to part 4 for the previous information. On our strength application, we wanted to enable https certificate. As it is for learning purpose, we wanted to keep it low cost.
Here were our options:
- Enable AWS provided https option.
- Get a free https certificate via letsencrypt and enable it on AWS. For our Sprint Boot application, we needed to generate a keystore.p12 file. We decided to opt for option2: get a free https certificate via letsencrypt website.
Our next challenge is to access the generated certificate into Spring Boot application in a way that is scalable in the future and does not go away if we terminate our EC2 instance on the ECS cluster. Here are options for us:
- Manually copy https certificate to EC2 instance. We did not opt for this option. Reason is, if we terminate our ECS instance (attached to the ECS cluster), the https certificate will be deleted with the termination of the EC2 instance.
- Keep the certificate at Amazon S3. Then, copy it to EC2 instance manually. We did not opt this option because every time we have a need to recreate an EC2 instance, we will have to manually copy the certificate.
- When creating an EC2 instance within ECS cluster, add commands in user data option, to copy the certificate from AWS S3. We think this is an optimum option. But we couldn’t enable it. Free version of ECS enabled EC2 instance did not allow adding user data properly. To allow running user data into EC2 instance, we had to run an EC2 agent configuration. Running these configurations were either not easily available or too complicated within the free tier EC2 instance. So, we did not opt this option.
- Add the https certificate within the Spring Boot application via S3 copy using SSL configuration. This could have been a considerable option. Within Spring Boot code, we can add SSL configuration bean to copy the certificate from AWS S3 and recreate a certificate file within the Spring Boot application. Below is a sample code to do it:
import java.io.File;
import org.apache.catalina.Context;
import org.apache.catalina.connector.Connector;
import org.apache.tomcat.util.descriptor.web.SecurityCollection;
import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
import org.springframework.boot.web.servlet.server.ServletWebServerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//@Configuration
public class SslConfiguration {
@Bean
public ServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
@Override
protected void postProcessContext(Context context) {
SecurityConstraint securityConstraint = new SecurityConstraint();
securityConstraint.setUserConstraint("CONFIDENTIAL");
SecurityCollection collection = new SecurityCollection();
collection.addPattern("/*");
securityConstraint.addCollection(collection);
context.addConstraint(securityConstraint);
}
};
tomcat.addAdditionalTomcatConnectors(redirectConnector());
return tomcat;
}
private Connector redirectConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setPort(8443);
connector.setSecure(true);
connector.setScheme("https");
connector.setAttribute("keyAlias", "tomcat");
connector.setAttribute("keystorePass", "<hidden>");
connector.setAttribute("keyStoreType", "PKCS12");
Object keystoreFile;
File file = new File("");// ADD PATH
String absoluteKeystoreFile = file.getAbsolutePath();
connector.setAttribute("keystoreFile", absoluteKeystoreFile);
connector.setAttribute("clientAuth", "false");
connector.setAttribute("sslProtocol", "TLS");
connector.setAttribute("SSLEnabled", true);
return connector;
}
}
- Add the https certificate within the Spring Boot application via S3 using properties file. To use this option, we need to read the https certificate file from application.properties. Below is a sample code to do it:
private static void copySSLCertificateFromS3() {
try {
Properties props = readPropertiesFile("src/main/resources/application.properties");
String clientRegion = props.getProperty("clientRegion");
String bucketName = props.getProperty("bucketName");
String sslFileNameWithPath = props.getProperty("sslFileNameWithPath");
String keyStoreFileName = props.getProperty("server.ssl.key-store");
AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(clientRegion)
.withCredentials(new ProfileCredentialsProvider()).build();
S3Object object = s3Client.getObject(new GetObjectRequest(bucketName, sslFileNameWithPath));
InputStream objectData = object.getObjectContent();
// Process the objectData stream.
File file = new File(keyStoreFileName);
try (OutputStream outputStream = new FileOutputStream(file)) {
IOUtils.copy(objectData, outputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
// handle exception here
} catch (IOException e) {
e.printStackTrace();
// handle exception here
}
objectData.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Properties readPropertiesFile(String fileName) throws IOException {
FileInputStream fis = null;
Properties prop = null;
try {
fis = new FileInputStream(fileName);
prop = new Properties();
prop.load(fis);
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
fis.close();
}
return prop;
}
private static void copySSLCertificateFromS3() {
try {
Properties props = readPropertiesFile("src/main/resources/application.properties");
String clientRegion = props.getProperty("clientRegion");
String bucketName = props.getProperty("bucketName");
String sslFileNameWithPath = props.getProperty("sslFileNameWithPath");
String keyStoreFileName = props.getProperty("server.ssl.key-store");
AmazonS3 s3Client = AmazonS3ClientBuilder.standard().withRegion(clientRegion)
.withCredentials(new ProfileCredentialsProvider()).build();
S3Object object = s3Client.getObject(new GetObjectRequest(bucketName, sslFileNameWithPath));
InputStream objectData = object.getObjectContent();
// Process the objectData stream.
File file = new File(keyStoreFileName);
try (OutputStream outputStream = new FileOutputStream(file)) {
IOUtils.copy(objectData, outputStream);
} catch (FileNotFoundException e) {
e.printStackTrace();
// handle exception here
} catch (IOException e) {
e.printStackTrace();
// handle exception here
}
objectData.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static Properties readPropertiesFile(String fileName) throws IOException {
FileInputStream fis = null;
Properties prop = null;
try {
fis = new FileInputStream(fileName);
prop = new Properties();
prop.load(fis);
} catch (FileNotFoundException fnfe) {
fnfe.printStackTrace();
} catch (IOException ioe) {
ioe.printStackTrace();
} finally {
fis.close();
}
return prop;
}
- Use a docker container to copy https certificate form S3 to EC2 instance. Every time we have a new EC2 instance, we can copy the https certificate file from S3 to EC2 instance using a very light weight docker container task. So far, this seems to be the best possible approach within the free tier EC2 instance of ECS type. We’re exploring this option.
If anyone has suggestions to us for a better approach, feel free to share your comments.
One thought on “Strength app part 5: Enable https on AWS”