Skip to content

feat(inputs.x509_cert): Add support for JKS and PKCS#12 keystores #16508

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

Merged
merged 40 commits into from
Mar 10, 2025

Conversation

paulojmdias
Copy link
Contributor

Summary

I initially thought about creating a separate input plugin for JKS & PKCS12 key stores but decided to integrate it into x509_cert since the changes were minimal. This update allows x509_cert to automatically detect and parse JKS & PKCS12 files while maintaining the existing certificate validation and metric collection logic. I also added tests to ensure everything works smoothly without overcomplicating the code.

Please evaluate if make sense and let me know what changes you feel are needed to accommodate this feature.

Checklist

  • No AI generated code was used in this PR

Related issues

resolves #7013

Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
@telegraf-tiger telegraf-tiger bot added feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin plugin/input 1. Request for new input plugins 2. Issues/PRs that are related to input plugins labels Feb 12, 2025
@srebhan
Copy link
Member

srebhan commented Feb 12, 2025

@paulojmdias please check the linter issues. You can reproduce them locally using make check-deps.

@srebhan srebhan self-assigned this Feb 12, 2025
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
@paulojmdias
Copy link
Contributor Author

@paulojmdias please check the linter issues. You can reproduce them locally using make check-deps.

Fixed 🙌

@paulojmdias paulojmdias changed the title feat(inputs.x509_cert): add support for JKS and PKCS#12 keystors feat(inputs.x509_cert): add support for JKS and PKCS#12 keystores Feb 13, 2025
Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

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

Thanks for your contribution @paulojmdias! This is very much appreciated! I do have some comments in the code...

@srebhan srebhan changed the title feat(inputs.x509_cert): add support for JKS and PKCS#12 keystores feat(inputs.x509_cert): Add support for JKS and PKCS#12 keystores Feb 17, 2025
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

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

@paulojmdias some more comments...

err = jks.Store(output, []byte("test-password"))
require.NoError(t, err)

return "jks://" + jksPath
Copy link
Member

Choose a reason for hiding this comment

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

This doesn't work on Windows, you should do

Suggested change
return "jks://" + jksPath
jksPath = filepath.ToSlash(jksPath)
if !strings.HasPrefix(jksPath, "/") {
jksPath = "/" + jksPath
}
return "jks://" + jksPath

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Fixed also pkcs12Path

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@srebhan i fixed in the commit 0824e05

Your suggestion also does not work, so I have included the following.

pkcs12Path = filepath.ToSlash(pkcs12Path)
if runtime.GOOS == "windows" {
	pkcs12Path = strings.TrimPrefix(pkcs12Path, "/")
}

return "pkcs12://" + pkcs12Path

Please review and let me know.

paulojmdias and others added 13 commits February 18, 2025 17:13
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
paulojmdias and others added 11 commits February 21, 2025 01:09
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
…keystore_cert

Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

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

Thanks @paulojmdias, this looks really nice already. Some more comments regarding the test code but we are almost there.

Comment on lines 137 to 139
if runtime.GOOS == "windows" {
jksPath = strings.TrimPrefix(jksPath, "/")
}
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Member

Choose a reason for hiding this comment

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

Hmmm what if in Windows I use a path like \TEMP\foo which is an absolute path, won't this be converted to TEMP/foo instead of /TEMP/foo?

Copy link
Member

Choose a reason for hiding this comment

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

Ok I see the issue now. The problem is that you do solve something in the test which must be solved in the actual code. Imagine the user wants to specify a JKS path of C:\MyCerts\foo.jks, then he has to set

[[inputs.x509_cert]]
  sources = ["jks:///C:/MyCerts/foo.jks"]

and then you need to remove the leading slash in your regular code if (and only if) you get a drive letter. Please see line 282 and following for how to do this!

Comment on lines 104 to 106
if runtime.GOOS == "windows" {
pkcs12Path = strings.TrimPrefix(pkcs12Path, "/")
}
Copy link
Member

Choose a reason for hiding this comment

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

Why do you need this?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Answered above

Comment on lines 144 to 193
func TestGatherKeystores(t *testing.T) {
pkcs12Path, jksPath := generateTestKeystores(t)

tests := []struct {
name string
mode os.FileMode
content string
password string
error bool
}{
{name: "valid PKCS12 keystore", mode: 0640, content: pkcs12Path, password: "test-password"},
{name: "valid JKS keystore", mode: 0640, content: jksPath, password: "test-password"},
{name: "missing password PKCS12", mode: 0640, content: pkcs12Path, error: true},
{name: "missing password JKS", mode: 0640, content: jksPath, error: true},
{name: "wrong password PKCS12", mode: 0640, content: pkcs12Path, password: "wrong-password", error: true},
{name: "wrong password JKS", mode: 0640, content: jksPath, password: "wrong-password", error: true},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
if runtime.GOOS != "windows" {
// To be Reviewed
path := strings.TrimPrefix(test.content, "pkcs12://")
path = strings.TrimPrefix(path, "jks://")
require.NoError(t, os.Chmod(path, test.mode))
}

sc := X509Cert{
Sources: []string{test.content},
Log: testutil.Logger{},
}

// Set password if provided
if test.password != "" {
sc.Password = config.NewSecret([]byte(test.password))
} else {
sc.Password = config.NewSecret(nil)
}

require.NoError(t, sc.Init())

acc := testutil.Accumulator{}
err := sc.Gather(&acc)

if (len(acc.Errors) > 0) != test.error {
t.Errorf("%s", err)
}
})
}
}
Copy link
Member

Choose a reason for hiding this comment

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

How about splitting this function into one testing for failures (those with error: true) and another one where we don't expect errors. This has the advantage of simplifying the code and it is easier to follow as the function will set the context.

Please also test for the error message with require.ErrorContains(t, err, "essential part of the expected error message")!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@srebhan added on commit 17e0dfa

Please take a look and let me know WDYT.

paulojmdias and others added 4 commits March 4, 2025 17:37
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

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

Thanks @paulojmdias! You need to fix the path handling on Windows. Currently your tests will pass but any real Windows path will fail as you need to remove the leading slash for the path contains a drive-letter.

Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
…keystore_cert

Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
Signed-off-by: Paulo Dias <paulodias.gm@gmail.com>
@telegraf-tiger
Copy link
Contributor

telegraf-tiger bot commented Mar 6, 2025

Copy link
Member

@srebhan srebhan left a comment

Choose a reason for hiding this comment

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

Awesome! Thanks a lot for your contribution and patience @paulojmdias!

@srebhan srebhan added the ready for final review This pull request has been reviewed and/or tested by multiple users and is ready for a final review. label Mar 7, 2025
@srebhan srebhan assigned mstrandboge and unassigned srebhan Mar 7, 2025
@mstrandboge mstrandboge merged commit d231442 into influxdata:master Mar 10, 2025
27 checks passed
@github-actions github-actions bot added this to the v1.34.0 milestone Mar 10, 2025
@paulojmdias paulojmdias deleted the keystore_cert branch March 10, 2025 08:02
asaharn pushed a commit to asaharn/telegraf that referenced this pull request Mar 10, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feat Improvement on an existing feature such as adding a new setting/mode to an existing plugin plugin/input 1. Request for new input plugins 2. Issues/PRs that are related to input plugins ready for final review This pull request has been reviewed and/or tested by multiple users and is ready for a final review.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add support for JKS files to x509_cert plugin
3 participants