Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add support for Kafka secure connection with certificates X509 #70

Merged
merged 8 commits into from
Jun 22, 2021

Conversation

derberg
Copy link
Member

@derberg derberg commented May 20, 2021

Fixes #66 and adds support for Kafka security using X509 security scheme.

  • enabled kafka-secure
  • added new securityScheme parameter that one need to use to provide the name of the scheme to use for code generation
  • move some logic to filters as doing some stuff with nunjucks made my eyes bleed 😄
  • in case of security of X509 type the following additional code goes to app/index.js
const fs = require('fs')

serverConfig.ssl.ca = fs.readFileSync('ca.pem');
serverConfig.ssl.key = fs.readFileSync('service.key');
serverConfig.ssl.cert = fs.readFileSync('service.cert');

How I tested:

  1. created account and kafka instance on Aiven
  2. took spec from https://github.com/aiven/thingum-industries/blob/main/asyncapi.yaml and just changed the server URL and that is it
  3. generated server by passing additional new param -p securityScheme=creds
  4. got certs from Aiven
  5. put certs files in the root of server generated using my changes
  6. installed generated server
  7. run server NODE_ENV=production npm start
  8. Got the following:
 PUB  Will eventually publish to factorysensor
Thingum Industries Sensors 1.0.0 is ready! 

🔗  Kafka adapter is connected!

I assume it works, but did not really test sending any events. @lornajane could you have a look, review PR and check if it works as expected. To use my template changes, do not run ag against @asyncapi/nodejs-template but https://github.com/derberg/nodejs-template#x509

@lornajane
Copy link

Amazing! I tried it on the branch and the only thing I needed to change was to add an SSL entry to my config:

      ssl:
        rejectUnauthorized: true

Without it, there's no ssl property to write to. I'm not sure though if I've done something wrong in my aysncapi file to cause this to be missing!

@derberg
Copy link
Member Author

derberg commented May 20, 2021

@lornajane I think you are not passing the NODE_ENV like this NODE_ENV=production npm start

below setting is there in the config for production environment:

ssl:
        rejectUnauthorized: true

it is not set to true by default because default !== production and therefor should be flexible aka non secure for testing

@lornajane
Copy link

OK yes, that made it work! But then why am I specifying the server at generate time if I am going to specify at run time anyway? And must the servers be named like the sections in the configuration file? Production / development etc? Can I have a server named "Fred"?

@derberg
Copy link
Member Author

derberg commented May 26, 2021

@lornajane you can even have a server called Wilma if you really want 😄

I think always 2 cases should be supported:

  1. if you have servers in the AsyncAPI document anyway, then why not reusing it
  2. still you should be able to provide different server URL at runtime

what you see in common.yml is not 1:1 related to the servers section from AsyncAPI document. It is only related to https://github.com/motdotla/dotenv and the 12factor, to have config in env variables, easy to overwrite, so NODE_ENV=production only specifies that what is in default must be combined with what is in production. When you have a look on the template, we do not read server URL or anything like that but just apply some production-specific config for kafka only https://github.com/asyncapi/nodejs-template/blob/master/template/config/common.yml#L58.

Does it make sense?

@derberg
Copy link
Member Author

derberg commented May 26, 2021

@lornajane maybe I should add something more to readme, so code change suggestions would be highly appreciated 🙏🏼

@derberg
Copy link
Member Author

derberg commented Jun 2, 2021

@lornajane thoughts?

@lornajane
Copy link

lornajane commented Jun 13, 2021

I think a note in the README (maybe in the generated README?) on using the environment variable to set which server config to combine with the default would be useful.

But the PR change here is definitely an improvement and should be merged when we can. Thankyou!

@derberg
Copy link
Member Author

derberg commented Jun 14, 2021

@lornajane I pushed some readme updates. Need approval from another maintainer and we can merge

Copy link
Member

@fmvilas fmvilas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How difficult would it be to make the location of the certificates configurable?

README.md Outdated Show resolved Hide resolved
Co-authored-by: Fran Méndez <fmvilas@gmail.com>
@derberg
Copy link
Member Author

derberg commented Jun 14, 2021

How difficult would it be to make the location of the certificates configurable?

@fmvilas depends, if all would be expected in single directory, then we just need to add one more param, so the default would be root, otherwise provided dirname. If custom directory per file, then the complicated part would be to agree on params names and how they should be provided. Sounds like better would be to get a use case first really

README.md Outdated
## How to use the template

This template must be used with the AsyncAPI Generator. You can find all available options [here](https://github.com/asyncapi/generator/).

In case you use X509 security and need to provide certificates, place them in the root of generated server with the following names: `ca.pem`, `service.cert`, `service.key`. Since you can have multiple different security schemes, to use the one of X509 type, you need to pass the name of the scheme like this: `-p securityScheme=SCHEME_NAME`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a suggestion: Could we provide the absolute path instead? I think it would be easier for the users and simplifies a lot the file handling. Most of this paragraph will be then removed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I just realized @fmvilas wrote some similar suggestion at #70 (review)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

option to provide custom location for the files is valid, but I don't get how will it affect this paragraph, other then extend it more 😄

Copy link
Member

@smoya smoya Jun 15, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For me this is simplified not only in terms of documentation but improved UX.

Suggested change
In case you use X509 security and need to provide certificates, place them in the root of generated server with the following names: `ca.pem`, `service.cert`, `service.key`. Since you can have multiple different security schemes, to use the one of X509 type, you need to pass the name of the scheme like this: `-p securityScheme=SCHEME_NAME`.
In case you use X509 security and need to provide certificates, specify their location at BLA BLA. Since you can have multiple different security schemes, to use the one of X509 type, you need to pass the name of the scheme like this: `-p securityScheme=SCHEME_NAME`.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for me best DX is defaults and later config. Description updated

{%- if params.securityScheme and (asyncapi.server(params.server).protocol() === 'kafka' or asyncapi.server(params.server).protocol() === 'kafka-secure') and asyncapi.components().securityScheme(params.securityScheme).type() === 'X509' %}
const fs = require('fs')

serverConfig.ssl.ca = fs.readFileSync('ca.pem');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if the file does not exist? is somehow erroring?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

added proper try/catch, thanks!

@fmvilas
Copy link
Member

fmvilas commented Jun 14, 2021

Sounds like better would be to get a use case first really

It's pretty common to have certificates in a system directory instead of the app directory, so they can be shared. Actually, beyond proof-of-concepts, it's not very common to find certificate files in the same dir as the code. I think having the ability to simply setup the root directory where to look for certificates would be good enough to start.

@derberg
Copy link
Member Author

derberg commented Jun 15, 2021

It's pretty common to have certificates in a system directory instead of the app directory, so they can be shared. Actually, beyond proof-of-concepts, it's not very common to find certificate files in the same dir as the code. I think having the ability to simply setup the root directory where to look for certificates would be good enough to start.

makes sense to me, will work on it

@derberg
Copy link
Member Author

derberg commented Jun 15, 2021

@fmvilas @smoya

certFilesDir added, docs extended

@derberg derberg requested review from fmvilas and smoya June 15, 2021 09:58
Copy link
Member

@smoya smoya left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM 👏 💯 🚀

@derberg
Copy link
Member Author

derberg commented Jun 22, 2021

@fmvilas kind reminder 😉

@sonarqubecloud
Copy link

Kudos, SonarCloud Quality Gate passed!

Bug A 0 Bugs
Vulnerability A 0 Vulnerabilities
Security Hotspot A 0 Security Hotspots
Code Smell A 0 Code Smells

No Coverage information No Coverage information
0.0% 0.0% Duplication

Copy link
Member

@fmvilas fmvilas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great stuff! 🚀

@derberg derberg merged commit 12c8435 into asyncapi:master Jun 22, 2021
@derberg derberg deleted the x509 branch June 22, 2021 09:25
@asyncapi-bot
Copy link
Contributor

🎉 This PR is included in version 0.11.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Kafka broker details missing from config
5 participants