Skip to content

Commit

Permalink
Merge pull request #336 from paketo-buildpacks/pkcs12-cert-loader
Browse files Browse the repository at this point in the history
Enable using PKCS#12 based Java keystores
  • Loading branch information
dmikusa authored Nov 7, 2023
2 parents a59bcd1 + 7bde13e commit 493c263
Show file tree
Hide file tree
Showing 14 changed files with 337 additions and 103 deletions.
9 changes: 3 additions & 6 deletions build.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,10 @@ package libjvm

import (
"fmt"
"strings"

"github.com/mattn/go-shellwords"
"github.com/paketo-buildpacks/libpak/effect"
"strings"

"github.com/buildpacks/libcnb"
"github.com/heroku/color"
Expand Down Expand Up @@ -272,7 +273,7 @@ func (b *Build) contributeNIK(jdkDep libpak.BuildpackDependency, nativeDep libpa

func (b *Build) contributeHelpers(context libcnb.BuildContext, depJRE libpak.BuildpackDependency) {
helpers := []string{"active-processor-count", "java-opts", "jvm-heap", "link-local-dns", "memory-calculator",
"security-providers-configurer", "jmx", "jfr"}
"security-providers-configurer", "jmx", "jfr", "openssl-certificate-loader"}

if IsBeforeJava9(depJRE.Version) {
helpers = append(helpers, "security-providers-classpath-8")
Expand All @@ -282,10 +283,6 @@ func (b *Build) contributeHelpers(context libcnb.BuildContext, depJRE libpak.Bui
helpers = append(helpers, "debug-9")
helpers = append(helpers, "nmt")
}
// Java 18 bug - cacerts keystore type not readable
if IsBeforeJava18(depJRE.Version) {
helpers = append(helpers, "openssl-certificate-loader")
}
found := false
for _, custom := range b.CustomHelpers {
if found {
Expand Down
7 changes: 4 additions & 3 deletions build_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,12 @@
package libjvm_test

import (
"github.com/paketo-buildpacks/libpak/bard"
"io"
"os"
"testing"

"github.com/paketo-buildpacks/libpak/bard"

"github.com/buildpacks/libcnb"
. "github.com/onsi/gomega"
"github.com/paketo-buildpacks/libpak"
Expand Down Expand Up @@ -130,9 +131,9 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
"security-providers-configurer",
"jmx",
"jfr",
"openssl-certificate-loader",
"security-providers-classpath-8",
"debug-8",
"openssl-certificate-loader",
}))
})

Expand Down Expand Up @@ -161,10 +162,10 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
"security-providers-configurer",
"jmx",
"jfr",
"openssl-certificate-loader",
"security-providers-classpath-9",
"debug-9",
"nmt",
"openssl-certificate-loader",
}))
})

Expand Down
60 changes: 6 additions & 54 deletions certificate_loader.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,12 @@ import (
"encoding/pem"
"fmt"
"io"
"io/ioutil"
"os"
"path/filepath"
"regexp"
"time"

"github.com/paketo-buildpacks/libpak/sherpa"
"github.com/pavlo-v-chernykh/keystore-go/v4"
"golang.org/x/sys/unix"
)

const DefaultCertFile = "/etc/ssl/certs/ca-certificates.crt"
Expand Down Expand Up @@ -58,9 +55,9 @@ func NewCertificateLoader() CertificateLoader {
}

func (c *CertificateLoader) Load(path string, password string) error {
ks, err := c.readKeyStore(path, password)
ks, err := DetectKeystore(path)
if err != nil {
return fmt.Errorf("unable to read keystore\n%w", err)
return err
}

files, err := c.certFiles()
Expand All @@ -76,25 +73,14 @@ func (c *CertificateLoader) Load(path string, password string) error {
}

for i, b := range blocks {
entry := keystore.TrustedCertificateEntry{
CreationTime: NormalizedDateTime,
Certificate: keystore.Certificate{
Type: "X.509",
Content: b.Bytes,
},
}

if err := ks.SetTrustedCertificateEntry(fmt.Sprintf("%s-%d", f, i), entry); err != nil {
return fmt.Errorf("unable to add trusted entry\n%w", err)
}

ks.Add(fmt.Sprintf("%s-%d", f, i), b)
added++
}
}

_, _ = fmt.Fprintf(c.Logger, "Adding %d container CA certificates to JVM truststore\n", added)

if err := c.writeKeyStore(ks, path, password); err != nil {
if err := ks.Write(); err != nil {
return fmt.Errorf("unable to write keystore\n%w", err)
}

Expand All @@ -112,7 +98,7 @@ func (c CertificateLoader) certFiles() ([]string, error) {

re := regexp.MustCompile(`^[[:xdigit:]]{8}\.[\d]$`)
for _, d := range c.CertDirs {
c, err := ioutil.ReadDir(d)
c, err := os.ReadDir(d)
if os.IsNotExist(err) {
continue
} else if err != nil {
Expand Down Expand Up @@ -162,7 +148,7 @@ func (c CertificateLoader) readBlocks(path string) ([]*pem.Block, error) {
blocks []*pem.Block
)

rest, err := ioutil.ReadFile(path)
rest, err := os.ReadFile(path)
if err != nil {
return nil, fmt.Errorf("unable to read %s\n%w", path, err)
}
Expand All @@ -177,37 +163,3 @@ func (c CertificateLoader) readBlocks(path string) ([]*pem.Block, error) {

return blocks, nil
}

func (CertificateLoader) readKeyStore(path string, password string) (keystore.KeyStore, error) {
in, err := os.Open(path)
if err != nil {
return keystore.KeyStore{}, fmt.Errorf("unable to open %s\n%w", path, err)
}
defer in.Close()

ks := keystore.New(keystore.WithOrderedAliases())
if err := ks.Load(in, []byte(password)); err != nil {
return keystore.KeyStore{}, fmt.Errorf("unable to decode keystore\n %w", err)
}

return ks, nil
}

func (c CertificateLoader) writeKeyStore(ks keystore.KeyStore, path string, password string) error {
if unix.Access(path, unix.W_OK) != nil {
_, _ = fmt.Fprintf(c.Logger, "WARNING: Unable to add container CA certificates to JVM because %s is read-only", path)
return nil
}

out, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
if err != nil {
return fmt.Errorf("unable to open %s\n%w", path, err)
}
defer out.Close()

if err := ks.Store(out, []byte(password)); err != nil {
return fmt.Errorf("unable to encode keystore\n%w", err)
}

return nil
}
2 changes: 2 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ require (
github.com/sclevine/spec v1.4.0
github.com/stretchr/testify v1.8.4
golang.org/x/sys v0.14.0
software.sslmate.com/src/go-pkcs12 v0.3.0
)

require (
Expand All @@ -30,6 +31,7 @@ require (
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.1 // indirect
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
golang.org/x/crypto v0.14.0 // indirect
golang.org/x/mod v0.14.0 // indirect
golang.org/x/net v0.17.0 // indirect
golang.org/x/text v0.14.0 // indirect
Expand Down
4 changes: 4 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcU
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
golang.org/x/crypto v0.14.0 h1:wBqGXzWJW6m1XrIKlAH0Hs1JJ7+9KBwnIO8v66Q9cHc=
golang.org/x/crypto v0.14.0/go.mod h1:MVFd36DqK4CsrnJYDkBA3VC4m2GkXAM0PvzMCn4JQf4=
golang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
golang.org/x/net v0.17.0 h1:pVaXccu2ozPjCXewfr1S7xza/zcXTity9cCdXQYSjIM=
Expand All @@ -79,3 +81,5 @@ gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
software.sslmate.com/src/go-pkcs12 v0.3.0 h1:ZYaL72OA2n9UgvesM62z1xmb4PYjgzswQ7xkuC08FEI=
software.sslmate.com/src/go-pkcs12 v0.3.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
1 change: 1 addition & 0 deletions init_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,6 @@ func TestUnit(t *testing.T) {
suite("SDKMAN", testSDKMAN)
suite("Versions", testVersions)
suite("JVMVersions", testJVMVersion)
suite("Keystore", testKeystore)
suite.Run(t)
}
14 changes: 4 additions & 10 deletions jdk.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@ import (
"os"
"path/filepath"

"github.com/heroku/color"

"github.com/buildpacks/libcnb"
"github.com/paketo-buildpacks/libpak"
"github.com/paketo-buildpacks/libpak/bard"
Expand Down Expand Up @@ -79,16 +77,12 @@ func (j JDK) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
} else {
keyStorePath = filepath.Join(layer.Path, "lib", "security", "cacerts")
}
if err := os.Chmod(keyStorePath, 0664); err != nil{
return libcnb.Layer{}, fmt.Errorf("unable to set keystore file permissions\n%w", err)
if err := os.Chmod(keyStorePath, 0664); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to set keystore file permissions\n%w", err)
}

if IsBeforeJava18(j.LayerContributor.Dependency.Version) {
if err := j.CertificateLoader.Load(keyStorePath, "changeit"); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to load certificates\n%w", err)
}
} else {
j.Logger.Bodyf("%s: The JVM cacerts entries cannot be loaded with Java 18+, for more information see: https://github.com/paketo-buildpacks/libjvm/issues/158", color.YellowString("Warning"))
if err := j.CertificateLoader.Load(keyStorePath, "changeit"); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to load certificates\n%w", err)
}
return layer, nil
})
Expand Down
17 changes: 7 additions & 10 deletions jlink.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,18 @@ package libjvm
import (
"bytes"
"fmt"
"os"
"path/filepath"
"sort"
"strings"

"github.com/buildpacks/libcnb"
"github.com/heroku/color"
"github.com/magiconair/properties"
"github.com/paketo-buildpacks/libjvm/count"
"github.com/paketo-buildpacks/libpak"
"github.com/paketo-buildpacks/libpak/bard"
"github.com/paketo-buildpacks/libpak/effect"
"os"
"path/filepath"
"sort"
"strings"
)

type JLink struct {
Expand Down Expand Up @@ -88,12 +89,8 @@ func (j JLink) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
return libcnb.Layer{}, fmt.Errorf("unable to set keystore file permissions\n%w", err)
}

if IsBeforeJava18(j.JavaVersion) {
if err := j.CertificateLoader.Load(cacertsPath, "changeit"); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to load certificates\n%w", err)
}
} else {
j.Logger.Bodyf("%s: The JVM cacerts entries cannot be loaded with Java 18+, for more information see: https://github.com/paketo-buildpacks/libjvm/issues/158", color.YellowString("Warning"))
if err := j.CertificateLoader.Load(cacertsPath, "changeit"); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to load certificates\n%w", err)
}

if IsBuildContribution(j.Metadata) {
Expand Down
14 changes: 4 additions & 10 deletions jre.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,6 @@ import (
"sort"
"strings"

"github.com/heroku/color"

"github.com/buildpacks/libcnb"
"github.com/magiconair/properties"
"github.com/paketo-buildpacks/libpak"
Expand Down Expand Up @@ -85,16 +83,12 @@ func (j JRE) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
} else {
cacertsPath = filepath.Join(layer.Path, "lib", "security", "cacerts")
}
if err := os.Chmod(cacertsPath, 0664); err != nil{
return libcnb.Layer{}, fmt.Errorf("unable to set keystore file permissions\n%w", err)
if err := os.Chmod(cacertsPath, 0664); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to set keystore file permissions\n%w", err)
}

if IsBeforeJava18(j.LayerContributor.Dependency.Version) {
if err := j.CertificateLoader.Load(cacertsPath, "changeit"); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to load certificates\n%w", err)
}
} else {
j.Logger.Bodyf("%s: The JVM cacerts entries cannot be loaded with Java 18+, for more information see: https://github.com/paketo-buildpacks/libjvm/issues/158", color.YellowString("Warning"))
if err := j.CertificateLoader.Load(cacertsPath, "changeit"); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to load certificates\n%w", err)
}

if IsBuildContribution(j.Metadata) {
Expand Down
Loading

0 comments on commit 493c263

Please sign in to comment.