Skip to content

Commit 382d238

Browse files
committed
Add new tool to support copying/filtering certs
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: - https://github.com/atc0005/check-cert/discussions/963 REFERENCES - refs GH-171 - refs GH-956
1 parent f921c95 commit 382d238

File tree

20 files changed

+1411
-114
lines changed

20 files changed

+1411
-114
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
SHELL := /bin/bash
2525

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

2929
PROJECT_NAME := check-cert
3030

README.md

+353-10
Large diffs are not rendered by default.

cmd/check_cert/main.go

+8-8
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ func main() {
8080

8181
// Honor request to parse filename first
8282
switch {
83-
case cfg.Filename != "":
83+
case cfg.InputFilename != "":
8484

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

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

9393
var err error
94-
certChain, parseAttemptLeftovers, err = certs.GetCertsFromFile(cfg.Filename)
94+
certChain, parseAttemptLeftovers, err = certs.GetCertsFromFile(cfg.InputFilename)
9595
if err != nil {
9696
log.Error().Err(err).Msg(
9797
"Error parsing certificates file")
@@ -100,14 +100,14 @@ func main() {
100100
plugin.ServiceOutput = fmt.Sprintf(
101101
"%s: Error parsing certificates file %q",
102102
nagios.StateCRITICALLabel,
103-
cfg.Filename,
103+
cfg.InputFilename,
104104
)
105105
plugin.ExitStatusCode = nagios.StateCRITICALExitCode
106106

107107
return
108108
}
109109

110-
certChainSource = cfg.Filename
110+
certChainSource = cfg.InputFilename
111111

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

@@ -118,18 +118,18 @@ func main() {
118118
plugin.AddError(fmt.Errorf(
119119
"%d unknown/unparsed bytes remaining at end of cert file %q",
120120
len(parseAttemptLeftovers),
121-
cfg.Filename,
121+
cfg.InputFilename,
122122
))
123123
plugin.ServiceOutput = fmt.Sprintf(
124124
"%s: Unknown data encountered while parsing certificates file %q",
125125
nagios.StateWARNINGLabel,
126-
cfg.Filename,
126+
cfg.InputFilename,
127127
)
128128

129129
plugin.LongServiceOutput = fmt.Sprintf(
130130
"The following text from the %q certificate file failed to parse"+
131131
" and is provided here for troubleshooting purposes:%s%s%s",
132-
cfg.Filename,
132+
cfg.InputFilename,
133133
nagios.CheckOutputEOL,
134134
nagios.CheckOutputEOL,
135135
string(parseAttemptLeftovers),
@@ -353,7 +353,7 @@ func main() {
353353
// both cases?
354354
var template string
355355
switch {
356-
case cfg.Filename != "":
356+
case cfg.InputFilename != "":
357357
template = "%d certs found in %s%s%s"
358358
default:
359359
template = "%d certs retrieved for %s%s%s"

cmd/cpcert/doc.go

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
// Copyright 2020 Adam Chalkley
2+
//
3+
// https://github.com/atc0005/check-cert
4+
//
5+
// Licensed under the MIT License. See LICENSE file in the project root for
6+
// full license information.
7+
8+
// CLI app used to copy and manipulate certificates.
9+
//
10+
// See our [GitHub repo]:
11+
//
12+
// - to review documentation (including examples)
13+
// - for the latest code
14+
// - to file an issue or submit improvements for review and potential
15+
// inclusion into the project
16+
//
17+
// [GitHub repo]: https://github.com/atc0005/check-cert
18+
package main

cmd/cpcert/filter.go

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// Copyright 2024 Adam Chalkley
2+
//
3+
// https://github.com/atc0005/check-cert
4+
//
5+
// Licensed under the MIT License. See LICENSE file in the project root for
6+
// full license information.
7+
8+
package main
9+
10+
import (
11+
"crypto/x509"
12+
13+
"github.com/atc0005/check-cert/internal/certs"
14+
"github.com/atc0005/check-cert/internal/config"
15+
"github.com/atc0005/check-cert/internal/textutils"
16+
)
17+
18+
// filterCertChain filters the given certificate chain to the specified list
19+
// of certificate types.
20+
func filterCertChain(filterKeywords []string, certChain []*x509.Certificate) []*x509.Certificate {
21+
filteredCertChain := make([]*x509.Certificate, 0, len(certChain))
22+
23+
// Validation prevents other keywords from being specified alongside this
24+
// one.
25+
if textutils.InList(config.CertTypeAll, filterKeywords, true) {
26+
filteredCertChain = append(filteredCertChain, certChain...)
27+
}
28+
29+
if textutils.InList(config.CertTypeLeaf, filterKeywords, true) {
30+
for _, cert := range certChain {
31+
if certs.IsLeafCert(cert, certChain) {
32+
filteredCertChain = append(filteredCertChain, cert)
33+
}
34+
}
35+
}
36+
37+
if textutils.InList(config.CertTypeIntermediate, filterKeywords, true) {
38+
for _, cert := range certChain {
39+
if certs.IsIntermediateCert(cert, certChain) {
40+
filteredCertChain = append(filteredCertChain, cert)
41+
}
42+
}
43+
}
44+
45+
if textutils.InList(config.CertTypeRoot, filterKeywords, true) {
46+
for _, cert := range certChain {
47+
if certs.IsRootCert(cert, certChain) {
48+
filteredCertChain = append(filteredCertChain, cert)
49+
}
50+
}
51+
}
52+
53+
return filteredCertChain
54+
}

0 commit comments

Comments
 (0)