How to add self signed SSL certificate to jHipster sample app? - spring-boot

I have create sample jHipster app. Now I want to add self signed SSL certificate and test in local to have a access to https. How to achieve this?

These instructions are applicable for all Spring Boot applications, on which JHipster is based. I have tested this on a newly generated JHipster 2.7 project.
You need to complete these steps when starting from scratch:
Generate a self-signed certificate
Add the SSL properties to your application.properties or application.yml as mentioned in the Spring Boot documentation
(Optional) Redirect HTTP to HTTPS
Generating a self-signed certificate
First you need to generate your self-signed certificate in your project directory, this can be done with keytool, which is utility script provided by Java:
keytool -genkey -alias tomcat -storetype PKCS12 -keyalg RSA -keysize 2048 -keystore keystore.p12 -validity 3650
Enter keystore password:
Re-enter new password:
What is your first and last name?
[Unknown]:
What is the name of your organizational unit?
[Unknown]:
What is the name of your organization?
[Unknown]:
What is the name of your City or Locality?
[Unknown]:
What is the name of your State or Province?
[Unknown]:
What is the two-letter country code for this unit?
[Unknown]:
Is CN=Unknown, OU=Unknown, O=Unknown, L=Unknown, ST=Unknown, C=Unknown correct?
[no]: yes
I have chosen password mypassword so this is the one I will use in the next step. When you have done this, you will see a keystore.p12 in your current directory.
Add the SSL properties to your application.properties or application.yml as mentioned in the Spring Boot documentation
Now you need to add the HTTPS connector properties for Tomcat. You can find the property (yml) files in src/main/resources/ and you need to update the application.yml (or if it is only for development in application-dev.yml with the following properties:
server:
ssl:
key-store: keystore.p12
key-store-password: mypassword
keyStoreType: PKCS12
keyAlias: tomcat
Now you can package your application with Maven (or Gradle if you chose that for your JHipster application) using mvn clean package and run the application using mvn spring-boot:run. You can now access your application on https://localhost:8080
For simplicity I did not change the port, but ideally you should change it as well in the properties files, but I left it out since they are already defined in application-dev.yml and application-prod.yml so you would have to change it in there or remove it and put it in the general application.yml
(Optional) Add redirect HTTP to HTTPS
You can only enable one protocol through the application.properties, so when you do this like above only HTTPS will work. If you want HTTP to work too, and redirect to HTTPS you have to add a #Configuration class like below
#Bean
public EmbeddedServletContainerFactory servletContainer() {
TomcatEmbeddedServletContainerFactory tomcat = new TomcatEmbeddedServletContainerFactory() {
#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(initiateHttpConnector());
return tomcat;
}
private Connector initiateHttpConnector() {
Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
connector.setScheme("http");
connector.setPort(8080);
connector.setSecure(false);
connector.setRedirectPort(8443);
return connector;
}
This response is basically a copy of my blog post on the same subject: http://www.drissamri.be/blog/java/enable-https-in-spring-boot/

To extend the Driss Amri brilliant answer on how to re-enable BrowserSync.
If you choose not to support http, or if http is redirected to https, BrowserSync will not work. To make it work again, few changes are necessary in:
gulp/config.js, apiPort and uri to:
apiPort: 8443,
uri: 'https://localhost:',
gulp/serve.js: add options.rejectUnauthorized = false; into proxyRoutes so that node does not complain about self signed certificate:
proxyRoutes.map(function (r) {
var options = url.parse(baseUri + r);
options.route = r;
options.preserveHost = true;
options.rejectUnauthorized = false;
return proxy(options);
}));
optionally let BrowserSync serve content over https too. I recommend it with Spring Social to save some trouble. Just add https: true into browserSync call in gulp/serve.js:
browserSync({
open: true,
port: config.port,
server: {
baseDir: config.app,
middleware: proxies
},
https: true
});
Now BrowserSync will serve content with self signed certificate shipped with it. It is possible to reuse the one created for Spring Boot, more on BrowserSync homepage.

For those using webpack instead of gulp you can complete Driss Amri's answer with two changes:
modify the proxy.conf.json:
{
"*": {
"target": "https://localhost:8443",
"secure": true
}
}
this will redirect API requests to the new https address.
Then alter also webpack file for instance here a webpack.dev.js modified example:
module.exports = webpackMerge(commonConfig({ env: ENV }), {
devtool: 'eval-source-map',
devServer: {
contentBase: './target/www',
proxy: [{
context: [
/* jhipster-needle-add-entity-to-webpack - JHipster will add entity api paths here */
'/api',
'/management', ...
'/auth'
],
target: 'https://127.0.0.1:8443',
/* set secure to false here, otherwise self-signed certification cause DEPTH_ZERO_SELF_SIGNED_CERT proxy errors */
secure: false
}]
},

Related

Configuring Swagger UI for OAuth 2.0 in Spring Boot with Kotlin

I am trying to configure OpenAPI 3 for OAuth 2.0 with a configuration class in Spring Boot with Kotlin.
Even though I set oauth2RedirectUrl in application.yml, when I click authorize in swagger UI to get new token to send a request, redirect url doesn't work as expected and I get the default redirect url called something like that(I believe it's a default redirectUrl): &redirect_uri=http://localhost:8080/oauth2-redirect.html instead of (what i configured in application.yaml)
Access the Swagger-UI at http://localhost:8080/swagger-ui/index.html?queryConfigEnabled=true&url=/v3/api-docs
Then click the authorize button and use the preconfigured values.
The IdentityProviderController prints then the configured values, e.g. redirect_uri.
The redirect_uri looks like http://localhost:8080/swagger-ui/oauth2-redirect.html and the swagger-ui:oauth2RedirectUrl path is missing. Even when it is configured in the application.yaml.
I added the following dependencies:
implementation("org.springdoc:springdoc-openapi-ui:1.6.14")
implementation("org.springdoc:springdoc-openapi-kotlin:1.6.14")
implementation("org.springdoc:springdoc-openapi-security:1.6.14")
and this is my application.yml
springdoc:
api-docs:
enabled: true
swagger-ui:
query-config-enabled: true
oauth:
client-id: <clientId>
client-secret: <clientSecret>
use-pkce-with-authorization-code-grant: true
oauth2RedirectUrl: <redirectUrl>
and this here is my configuration class:
#Configuration
#OpenAPIDefinition
#SecurityScheme(
name = "oauth2",
type = SecuritySchemeType.OAUTH2,
flows =
OAuthFlows(
authorizationCode =
OAuthFlow(
authorizationUrl = "<authorizationUrl>",
tokenUrl = "<tokenUrl>",
scopes =
[
OAuthScope(name = "test1"),
OAuthScope(name = "test2"),
OAuthScope(name = "test3")],
)))
open class OpenApiConfiguration {
#Bean
open fun customOpenAPI(): OpenAPI {
return OpenAPI()
.components(Components())
.info(
Info()
.title("ABC Service Rest API")
.description("description...")
.version("1.0.0"))
}
}
What am I missing here?
UPDATE: (17.02.2023)
After I am changing the redirect_uri in chrome with the correct one, then I can reach the Identity proverders' page, so I only need to find a way to set my redirectUrl configuration properly.

Traefik acme timeouts

Im trying to get Traefik working properly in AKS. Overall it works fine however i can not get the ACME certs to work. Below attached my traefik.toml configuration on which i cant find anything odd.
The 3 domains that are mentioned are dummy in this use case by actually exists and reply as well
# traefik.toml
logLevel = "info"
defaultEntryPoints = ["http","https"]
[entryPoints]
[entryPoints.http]
address = ":80"
compress = true
[entryPoints.https]
address = ":443"
compress = true
[entryPoints.https.tls]
[[entryPoints.https.tls.certificates]]
CertFile = "/ssl/tls.crt"
KeyFile = "/ssl/tls.key"
[entryPoints.traefik]
address = ":8080"
[ping]
entryPoint = "http"
[kubernetes]
[traefikLog]
format = "json"
[acme]
KeyType = "RSA4096"
email = "pimjansen#domain.com"
storage = "/acme/acme.json"
entryPoint = "https"
onHostRule = true
acmeLogging = true
[acme.httpChallenge]
entryPoint = "http"
[[acme.domains]]
main = "traefik.domain.com"
[[acme.domains]]
main = "elasticsearch.domain.com"
[[acme.domains]]
main = "kibana.domain.com"
[api]
entryPoint = "traefik"
dashboard = true
The actual error i am receiving is this:
{"level":"error","msg":"Unable to obtain ACME certificate for domains \"traefik.hardstyletop40.com\" : unable to generate a certificate for the domains [traefik.domain.com]: acme: Error -\u003e One or more domains had a problem:\n[traefik.domain.com] acme: error: 400 :: urn:ietf:params:acme:error:connection :: Fetching http://traefik.hardstyletop40.com/.well-known/acme-challenge/mYkyJzIM-6Y2UIknhXpCkUUTZWjzsAeMuqx7eDCZloY: Error getting validation data, url: \n","time":"2019-09-11T14:47:13Z"}
With details about the challenge:
"challenges": [
{
"type": "http-01",
"status": "invalid",
"error": {
"type": "urn:ietf:params:acme:error:connection",
"detail": "Fetching http://traefik.domain.com/.well-known/acme-challenge/mYkyJzIM-6Y2UIknhXpCkUUTZWjzsAeMuqx7eDCZloY: Error getting validation data",
"status": 400
},
"url": "https://acme-v02.api.letsencrypt.org/acme/chall-v3/293838266/LPH2sA",
"token": "mYkyJzIM-6Y2UIknhXpCkUUTZWjzsAeMuqx7eDCZloY",
"validationRecord": [
{
"url": "http://traefik.domain.com/.well-known/acme-challenge/mYkyJzIM-6Y2UIknhXpCkUUTZWjzsAeMuqx7eDCZloY",
"hostname": "traefik.hardstyletop40.com",
"port": "80",
"addressesResolved": [
"13.79.159.165"
],
"addressUsed": "13.79.159.165"
}
]
},
Thanks in advance
How letsencrypt works is by putting a file in the .well-known directory on your specified webserver.
You're saying they're dummy, so you might be doing them locally? In anycase, if the autogenerated file isn't found on the webserver, it can't be verified that the certificate is requested from the "owning" domain.
How the flow works heavily simplified:
letsencrypt -> generate file name: abc133......
letsencrypt -> find webroot of provided domain in webserver config
letsencrypt -> copy file to .well-known in webroot of given domain
letsencrypt -> send a webrequest with filename and domain name to letsencrypt.org
letsencrypt.org -> try to request the file from the given domain looked up via dns
letsencrypt.org -> successfully requested file and verified, output certificate
letsencrypt -> read certificate and copy to certificates folder, make a few symlinks
letsencrypt -> modify webserver configs if needed
Now if you're working with dummy domains and not on the live server, the above process will fail on step 3, which will cause step 5 to fail, which will result in an error which you are getting.
An alternative is that you set a DNS record key to verify, if you can't run the command on the webserver to generate the certificate.
sudo certbot -d your.dummy.com --manual --preferred-challenges dns certonly
This will give you a code you will need to put in a txt record on your domain server
When you have done that, you confirm in the letsencrypt app that you've set the record and continue.
In short, if you cannot run the command on the webserver to generate the certificates, or cannoot modify the dns records, you cannot obtain a certificate via letsencrypt.

Javascript get request from https server to localhost:port with self signed SSL

I have two servers configured and running om my Debian server. One main server and one Elasticsearch (search engine) server.
The main server is running on a https node server with a NGINX proxy and a purchased SSL certificate. The Elasticsearch server is running on a http server. I've added a new NGINX proxy server to redirect https://localhost:9999 to http://localhost:9200 with a self-signed SSL certificate. There's also a configured authentication on the Elasticsearch server with a username and a password.
Everything seem to be properly configured since I can get a successful response from the server when I'm doing a curl from the servers terminal towards https://localhost:9999 with the -k option to bypass the verication of the self-signed certificate, without it, it does not work.
I cannot do a cross-domain request from my https main server to my http localhost server. Therefore I need to configure https on my localhost server.
Without the -k option:
curl: (60) SSL certificate problem: self signed certificate
More details here: http://curl.haxx.se/docs/sslcerts.html
curl performs SSL certificate verification by default, using a "bundle"
of Certificate Authority (CA) public keys (CA certs). If the default
bundle file isn't adequate, you can specify an alternate file
using the --cacert option.
If this HTTPS server uses a certificate signed by a CA represented in
the bundle, the certificate verification probably failed due to a
problem with the certificate (it might be expired, or the name might
not match the domain name in the URL).
If you'd like to turn off curl's verification of the certificate, use
the -k (or --insecure) option.
With the -k option:
{
"name" : "server-name",
"cluster_name" : "name",
"cluster_uuid" : "uuid",
"version" : {
"number" : "x.x.x",
"build_hash" : "abc123",
"build_date" : "Timestamp",
"build_snapshot" : false,
"lucene_version" : "x.x.x"
},
"tagline" : "You Know, for Search"
}
Which is a successful Elasticsearch server response.
So the full curl request looks something like curl -k https://localhost:9999/ --user username:password.
So, the actual question:
I would like to be able to do a simple jQuery AJAX request towards this server. I'm trying with the following request $.get('https://username:password#localhost:9999/') but I'm getting ERR_CONNECTION_REFUSED.
My guess is that that the AJAX request does not bypass the self-signed certificate verification and therefore it refuses to connect.
Is there any simple way to solve this with request headers or something like that? Or do i need to purchase a CA-certificate to make this work with AJAX?
You are right the problem is the self signed certificate.If you try the same request but as http it will work.
Here is a workaround to make ElasticSearch work with https:
You need to implement your own Http Connector:
var HttpConnector = require('elasticsearch/src/lib/connectors/http');
var inherits = require('util').inherits;
var qs = require('querystring');
var fs = require('fs');
function CustomHttpConnector(host, config) {
HttpConnector.call(this, host, config);
}
inherits(CustomHttpConnector, HttpConnector);
// This function is copied and modified from elasticsearch-js/src/lib/connectors/http.js
CustomHttpConnector.prototype.makeReqParams = function (params) {
params = params || {};
var host = this.host;
var reqParams = {
method: params.method || 'GET',
protocol: host.protocol + ':',
auth: host.auth,
hostname: host.host,
port: host.port,
path: (host.path || '') + (params.path || ''),
headers: host.getHeaders(params.headers),
agent: this.agent,
rejectUnauthorized: true,
ca: fs.readFileSync('publicCertificate.crt', 'utf8')
};
if (!reqParams.path) {
reqParams.path = '/';
}
var query = host.getQuery(params.query);
if (query) {
reqParams.path = reqParams.path + '?' + qs.stringify(query);
}
return reqParams;
};
module.exports = CustomHttpConnector;
Then register it like so:
var elasticsearch = require('elasticsearch');
var CustomHttpConnector = require('./customHttpConnector');
var Elasticsearch = function() {
this.client = new elasticsearch.Client({
host: {
host: 'my.server.com',
port: '443',
protocol: 'https',
auth: 'user:passwd'
},
keepAlive: true,
apiVerison: "1.3",
connectionClass: CustomHttpConnector
});
}
https://gist.github.com/fractalf/d08de3b59c32197ccd65
If you want to make simple ajax calls not using ES the only thing you can do is prompt the user to visit the page and accept the certificate themselves when the request is denied.
Also see: https://stackoverflow.com/a/4566055/5758328

How do you configure an emailAdapter for parse-server?

I'm trying to test out the password reset flow on a locally running parse-server instance. Every time I send a password reset request I get the following error error: Uncaught internal server error. Trying to send a reset password but no adapter is set undefined. I know I'm supposed to configure the emailAdapter in cli-definitions but I'm not too sure what exactly I'm supposed to put there. I tried changing the contructor in ParseServer.js to have
emailAdapter: {
module: 'parse-server-simple-mailgun-adapter',
options: {
// The address that your emails come from
fromAddress: 'parse#example.com',
// Your domain from mailgun.com
domain: 'example.com',
// Your API key from mailgun.com
apiKey: 'key-mykey',
}
}
but that did not work. Any help is greatly appreciated!
Just got this configured myself and I think you may just be missing a few parameters.
In the configuration in your index.js or other file where ParseServer is being initialized, you need all of the following:
verifyUserEmails: true,
// Same as the SERVER_URL used to configure ParseServer, in my case it uses Heroku
publicServerURL: 'http://MY_HEROKU_APP.herokuapp.com/parse',
appName: 'MY_APP',
emailAdapter: {
module: 'parse-server-simple-mailgun-adapter',
options: {
fromAddress: 'no-reply#example.com',
domain: 'example.com',
apiKey: 'key-XXXXXX',
}
}

Is it possible to create JKS keystore file without a password?

I'm experimenting with OSGi conditional permissions mechanism. More specifically, I'm trying to use org.osgi.service.condpermadmin.BundleSignerCondition to restrict which bundles can be started. Documentation I have states that in order to use this permission, I must specify the path to JKS keystores using org.osgi.framework.trust.repositories framework configuration property. However, the same documentation mentions that JKS mentioned in this property must not have a password. So the question is: how to create a JKS without a password? Keytool utility refuses to create JKS with blank password.
You cannot create a keystore with a blank password with keytool since a while, but you can still do it programmatically.
Read a cert like this:
private static Certificate readCert(String path) throws IOException, CertificateException {
try (FileInputStream fin = new FileInputStream(path)) {
return CertificateFactory.getInstance("X.509").generateCertificate(fin);
}
}
Than create the keystore with the empty password like this:
try {
// Reading the cert
Certificate cert = readCert("/tmp/cert.cert");
// Creating an empty JKS keystore
KeyStore keystore = KeyStore.getInstance(KeyStore.getDefaultType());
keystore.load(null, null);
// Adding the cert to the keystore
keystore.setCertificateEntry("somecert", cert);
// Saving the keystore with a zero length password
FileOutputStream fout = new FileOutputStream("/tmp/keystore");
keystore.store(fout, new char[0]);
} catch (GeneralSecurityException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Run the command:
keytool -list -keystore keystore
It will ask for a password but you can simply push an enter. You will get the following warning, but the content of the keystore will be listed:
***************** WARNING WARNING WARNING *****************
* The integrity of the information stored in your keystore *
* has NOT been verified! In order to verify its integrity, *
* you must provide your keystore password. *
***************** WARNING WARNING WARNING *****************
This might work for you.

Resources