Skip to content

Commit

Permalink
Add new tool to support copying/filtering certs
Browse files Browse the repository at this point in the history
CHANGES

The new `cpcert` prototype tool supports copying certificate chains
from a server or input file and optionally filtering to specific
certificate types before saving to an output file (PEM format).

Config validation logic has been refactored to help the new tool
fit within the project without duplicating existing work.

Overall, this is a "MVP" build and while usable, it should be
considered to be of "alpha" level quality. Please report issues
that you encounter.

Many of the exposed flags, help text and summary output are subject to
change significantly in later releases.

Feedback on the new `cpcert` tool is welcome:

- #963

REFERENCES

- refs GH-171
- refs GH-956
  • Loading branch information
atc0005 committed Oct 4, 2024
1 parent 084e3aa commit 9c74c6c
Show file tree
Hide file tree
Showing 20 changed files with 1,411 additions and 114 deletions.
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
SHELL := /bin/bash

# Space-separated list of cmd/BINARY_NAME directories to build
WHAT := check_cert lscert certsum
WHAT := check_cert lscert certsum cpcert

PROJECT_NAME := check-cert

Expand Down
363 changes: 353 additions & 10 deletions README.md

Large diffs are not rendered by default.

16 changes: 8 additions & 8 deletions cmd/check_cert/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ func main() {

// Honor request to parse filename first
switch {
case cfg.Filename != "":
case cfg.InputFilename != "":

log.Debug().Msg("Attempting to parse certificate file")

Expand All @@ -91,7 +91,7 @@ func main() {
var parseAttemptLeftovers []byte

var err error
certChain, parseAttemptLeftovers, err = certs.GetCertsFromFile(cfg.Filename)
certChain, parseAttemptLeftovers, err = certs.GetCertsFromFile(cfg.InputFilename)
if err != nil {
log.Error().Err(err).Msg(
"Error parsing certificates file")
Expand All @@ -100,14 +100,14 @@ func main() {
plugin.ServiceOutput = fmt.Sprintf(
"%s: Error parsing certificates file %q",
nagios.StateCRITICALLabel,
cfg.Filename,
cfg.InputFilename,
)
plugin.ExitStatusCode = nagios.StateCRITICALExitCode

return
}

certChainSource = cfg.Filename
certChainSource = cfg.InputFilename

log.Debug().Msg("Certificate file parsed")

Expand All @@ -118,18 +118,18 @@ func main() {
plugin.AddError(fmt.Errorf(
"%d unknown/unparsed bytes remaining at end of cert file %q",
len(parseAttemptLeftovers),
cfg.Filename,
cfg.InputFilename,
))
plugin.ServiceOutput = fmt.Sprintf(
"%s: Unknown data encountered while parsing certificates file %q",
nagios.StateWARNINGLabel,
cfg.Filename,
cfg.InputFilename,
)

plugin.LongServiceOutput = fmt.Sprintf(
"The following text from the %q certificate file failed to parse"+
" and is provided here for troubleshooting purposes:%s%s%s",
cfg.Filename,
cfg.InputFilename,
nagios.CheckOutputEOL,
nagios.CheckOutputEOL,
string(parseAttemptLeftovers),
Expand Down Expand Up @@ -353,7 +353,7 @@ func main() {
// both cases?
var template string
switch {
case cfg.Filename != "":
case cfg.InputFilename != "":
template = "%d certs found in %s%s%s"
default:
template = "%d certs retrieved for %s%s%s"
Expand Down
18 changes: 18 additions & 0 deletions cmd/cpcert/doc.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// Copyright 2020 Adam Chalkley
//
// https://github.com/atc0005/check-cert
//
// Licensed under the MIT License. See LICENSE file in the project root for
// full license information.

// CLI app used to copy and manipulate certificates.
//
// See our [GitHub repo]:
//
// - to review documentation (including examples)
// - for the latest code
// - to file an issue or submit improvements for review and potential
// inclusion into the project
//
// [GitHub repo]: https://github.com/atc0005/check-cert
package main
54 changes: 54 additions & 0 deletions cmd/cpcert/filter.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright 2024 Adam Chalkley
//
// https://github.com/atc0005/check-cert
//
// Licensed under the MIT License. See LICENSE file in the project root for
// full license information.

package main

import (
"crypto/x509"

"github.com/atc0005/check-cert/internal/certs"
"github.com/atc0005/check-cert/internal/config"
"github.com/atc0005/check-cert/internal/textutils"
)

// filterCertChain filters the given certificate chain to the specified list
// of certificate types.
func filterCertChain(filterKeywords []string, certChain []*x509.Certificate) []*x509.Certificate {
filteredCertChain := make([]*x509.Certificate, 0, len(certChain))

// Validation prevents other keywords from being specified alongside this
// one.
if textutils.InList(config.CertTypeAll, filterKeywords, true) {
filteredCertChain = append(filteredCertChain, certChain...)
}

if textutils.InList(config.CertTypeLeaf, filterKeywords, true) {
for _, cert := range certChain {
if certs.IsLeafCert(cert, certChain) {
filteredCertChain = append(filteredCertChain, cert)
}
}
}

if textutils.InList(config.CertTypeIntermediate, filterKeywords, true) {
for _, cert := range certChain {
if certs.IsIntermediateCert(cert, certChain) {
filteredCertChain = append(filteredCertChain, cert)
}
}
}

if textutils.InList(config.CertTypeRoot, filterKeywords, true) {
for _, cert := range certChain {
if certs.IsRootCert(cert, certChain) {
filteredCertChain = append(filteredCertChain, cert)
}
}
}

return filteredCertChain
}
Loading

0 comments on commit 9c74c6c

Please sign in to comment.