Skip to content

Commit

Permalink
Added leaf loaders modules: file, folder, pem aand storage
Browse files Browse the repository at this point in the history
  • Loading branch information
armadi1809 committed Feb 28, 2024
1 parent fe4febc commit 3e60ec5
Show file tree
Hide file tree
Showing 5 changed files with 465 additions and 1 deletion.
5 changes: 4 additions & 1 deletion modules/caddytls/connpolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -735,7 +735,10 @@ func (l *LeafCertClientAuth) Provision(ctx caddy.Context) error {
if err != nil {
return fmt.Errorf("Could not parse leaf certificates loaders: %s", err.Error())
}
trustedLeafCertloaders := val.([]LeafCertificateLoader)
trustedLeafCertloaders := []LeafCertificateLoader{}
for _, loader := range val.([]any) {
trustedLeafCertloaders = append(trustedLeafCertloaders, loader.(LeafCertificateLoader))
}
trustedLeafCertificates := []*x509.Certificate{}
for _, loader := range trustedLeafCertloaders {
certs, err := loader.LoadLeafCertificates()
Expand Down
89 changes: 89 additions & 0 deletions modules/caddytls/leafderloader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package caddytls

import (
"crypto/x509"
"fmt"

"github.com/caddyserver/caddy/v2"
)

func init() {
caddy.RegisterModule(LeafDERLoader{})
}

// LeafDERLoader loads leaf certificates by
// decoding their DER blocks directly. This has the advantage
// of not needing to store them on disk at all.
type LeafDERLoader struct {
Ders []LeafCertDER `json:"ders,omitempty"`
}

// Provision implements caddy.Provisioner.
func (pl LeafDERLoader) Provision(ctx caddy.Context) error {
repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
if !ok {
repl = caddy.NewReplacer()
}
for k, pair := range pl.Ders {
for i, tag := range pair.Tags {
pair.Tags[i] = repl.ReplaceKnown(tag, "")
}
pl.Ders[k] = LeafCertDER{
CertificateDER: repl.ReplaceKnown(pair.CertificateDER, ""),
Tags: pair.Tags,
}
}
return nil
}

// CaddyModule returns the Caddy module information.
func (LeafDERLoader) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "tls.leaf_cert_loader.der",
New: func() caddy.Module { return new(LeafDERLoader) },
}
}

// LeafCertDER contains DER-encoded Leaf certificate.
type LeafCertDER struct {
// The leaf certificate in DER format.
CertificateDER string `json:"certificate"`

// Arbitrary values to associate with this certificate.
// Can be useful when you want to select a particular
// certificate when there may be multiple valid candidates.
Tags []string `json:"tags,omitempty"`
}

// LoadLeafCertificates returns the certificates contained in pl.
func (pl LeafDERLoader) LoadLeafCertificates() ([]*x509.Certificate, error) {
certs := make([]*x509.Certificate, 0, len(pl.Ders))
for i, pair := range pl.Ders {
cert, err := x509.ParseCertificate([]byte(pair.CertificateDER))
if err != nil {
return nil, fmt.Errorf("DER cert %d: %v", i, err)
}
certs = append(certs, cert)
}
return certs, nil
}

// Interface guard
var (
_ LeafCertificateLoader = (*LeafDERLoader)(nil)
_ caddy.Provisioner = (*LeafDERLoader)(nil)
)
128 changes: 128 additions & 0 deletions modules/caddytls/leaffileloader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package caddytls

import (
"crypto/x509"
"encoding/pem"
"fmt"
"os"

"github.com/caddyserver/caddy/v2"
)

func init() {
caddy.RegisterModule(LeafFileLoader{})
}

// LeafFileLoader loads leaf certificates from disk.
type LeafFileLoader struct {
Files []LeafCertFile `json:"files,omitempty"`
}

// Provision implements caddy.Provisioner.
func (fl LeafFileLoader) Provision(ctx caddy.Context) error {
repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
if !ok {
repl = caddy.NewReplacer()
}
for k, pair := range fl.Files {
for i, tag := range pair.Tags {
pair.Tags[i] = repl.ReplaceKnown(tag, "")
}
fl.Files[k] = LeafCertFile{
LeafCertificate: repl.ReplaceKnown(pair.LeafCertificate, ""),
Format: repl.ReplaceKnown(pair.Format, ""),
Tags: pair.Tags,
}
}
return nil
}

// CaddyModule returns the Caddy module information.
func (LeafFileLoader) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "tls.leaf_cert_loader.file",
New: func() caddy.Module { return new(LeafFileLoader) },
}
}

// LeafCertFile associates leaf certificate file name along with its
// encoding format so that they can be loaded from disk.
type LeafCertFile struct {
// Path to the certificate file.
LeafCertificate string `json:"certificate"`

// The format of the cert. Can be "pem". Default: "pem"
Format string `json:"format,omitempty"`

// Arbitrary values to associate with this certificate.
// Can be useful when you want to select a particular
// certificate when there may be multiple valid candidates.
Tags []string `json:"tags,omitempty"`
}

// LoadLEafCertificates returns the certificates to be loaded by fl.
func (fl LeafFileLoader) LoadLeafCertificates() ([]*x509.Certificate, error) {
certificates := make([]*x509.Certificate, 0, len(fl.Files))
for _, pair := range fl.Files {
switch pair.Format {
case "":
fallthrough
case "pem":
ders, err := convertPEMFilesToDERBytes(pair.LeafCertificate)
if err != nil {
return nil, err
}
certs, err := x509.ParseCertificates(ders)
if err != nil {
return nil, err
}
certificates = append(certificates, certs...)
default:
return nil, fmt.Errorf("unrecognized certificate/key encoding format: %s", pair.Format)
}
}
return certificates, nil
}

func convertPEMFilesToDERBytes(filename string) ([]byte, error) {
certDataPEM, err := os.ReadFile(filename)
if err != nil {
return nil, err
}
var ders []byte
// while block is not nil, we have more certificates in the file
for block, rest := pem.Decode(certDataPEM); block != nil; block, rest = pem.Decode(rest) {
if block.Type != "CERTIFICATE" {
return nil, fmt.Errorf("no CERTIFICATE pem block found in %s", filename)
}
ders = append(
ders,
block.Bytes...,
)
}
// if we decoded nothing, return an error
if len(ders) == 0 {
return nil, fmt.Errorf("no CERTIFICATE pem block found in %s", filename)
}
return ders, nil
}

// Interface guard
var (
_ LeafCertificateLoader = (*LeafFileLoader)(nil)
_ caddy.Provisioner = (*LeafFileLoader)(nil)
)
97 changes: 97 additions & 0 deletions modules/caddytls/leaffolderloader.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// Copyright 2015 Matthew Holt and The Caddy Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package caddytls

import (
"crypto/x509"
"fmt"
"os"
"path/filepath"
"strings"

"github.com/caddyserver/caddy/v2"
)

func init() {
caddy.RegisterModule(LeafFolderLoader{})
}

// LeafFolderLoader loads certificates and their associated keys from disk
// by recursively walking the specified directories, looking for PEM
// files which contain both a certificate and a key.
type LeafFolderLoader struct {
Folders []string `json:"folders"`
}

// CaddyModule returns the Caddy module information.
func (LeafFolderLoader) CaddyModule() caddy.ModuleInfo {
return caddy.ModuleInfo{
ID: "tls.leaf_cert_loader.folders",
New: func() caddy.Module { return new(LeafFolderLoader) },
}
}

// Provision implements caddy.Provisioner.
func (fl LeafFolderLoader) Provision(ctx caddy.Context) error {
repl, ok := ctx.Value(caddy.ReplacerCtxKey).(*caddy.Replacer)
if !ok {
repl = caddy.NewReplacer()
}
for k, path := range fl.Folders {
fl.Folders[k] = repl.ReplaceKnown(path, "")
}
return nil
}

// LoadLeafCertificates loads all the leaf certificates in the directories
// listed in fl from all files ending with .pem.
func (fl LeafFolderLoader) LoadLeafCertificates() ([]*x509.Certificate, error) {
var certs []*x509.Certificate
for _, dir := range fl.Folders {
err := filepath.Walk(dir, func(fpath string, info os.FileInfo, err error) error {
if err != nil {
return fmt.Errorf("unable to traverse into path: %s", fpath)
}
if info.IsDir() {
return nil
}
if !strings.HasSuffix(strings.ToLower(info.Name()), ".pem") {
return nil
}

certData, err := os.ReadFile(fpath)
if err != nil {
return err
}
cert, err := x509.ParseCertificate(certData)
if err != nil {
return fmt.Errorf("%s: %w", fpath, err)
}

certs = append(certs, cert)

return nil
})
if err != nil {
return nil, err
}
}
return certs, nil
}

var (
_ LeafCertificateLoader = (*LeafFolderLoader)(nil)
_ caddy.Provisioner = (*LeafFolderLoader)(nil)
)
Loading

0 comments on commit 3e60ec5

Please sign in to comment.