cmd: cleaning up programs
This commit is contained in:
@@ -450,6 +450,8 @@ linters:
|
|||||||
linters: [ forbidigo ]
|
linters: [ forbidigo ]
|
||||||
- path: 'logging/example_test.go'
|
- path: 'logging/example_test.go'
|
||||||
linters: [ testableexamples ]
|
linters: [ testableexamples ]
|
||||||
|
- path: 'main.go'
|
||||||
|
linters: [ forbidigo, mnd ]
|
||||||
- source: 'TODO'
|
- source: 'TODO'
|
||||||
linters: [ godot ]
|
linters: [ godot ]
|
||||||
- text: 'should have a package comment'
|
- text: 'should have a package comment'
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
@@ -28,10 +29,16 @@ func connect(addr string, dport string, six bool, timeout time.Duration) error {
|
|||||||
|
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Printf("connecting to %s/%s... ", addr, proto)
|
fmt.Printf("connecting to %s/%s... ", addr, proto)
|
||||||
os.Stdout.Sync()
|
if err = os.Stdout.Sync(); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := net.DialTimeout(proto, addr, timeout)
|
dialer := &net.Dialer{
|
||||||
|
Timeout: timeout,
|
||||||
|
}
|
||||||
|
|
||||||
|
conn, err := dialer.DialContext(context.Background(), proto, addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if verbose {
|
if verbose {
|
||||||
fmt.Println("failed.")
|
fmt.Println("failed.")
|
||||||
@@ -42,8 +49,8 @@ func connect(addr string, dport string, six bool, timeout time.Duration) error {
|
|||||||
if verbose {
|
if verbose {
|
||||||
fmt.Println("OK")
|
fmt.Println("OK")
|
||||||
}
|
}
|
||||||
conn.Close()
|
|
||||||
return nil
|
return conn.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"embed"
|
"embed"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
@@ -14,22 +15,22 @@ import (
|
|||||||
// loadCertsFromFile attempts to parse certificates from a file that may be in
|
// loadCertsFromFile attempts to parse certificates from a file that may be in
|
||||||
// PEM or DER/PKCS#7 format. Returns the parsed certificates or an error.
|
// PEM or DER/PKCS#7 format. Returns the parsed certificates or an error.
|
||||||
func loadCertsFromFile(path string) ([]*x509.Certificate, error) {
|
func loadCertsFromFile(path string) ([]*x509.Certificate, error) {
|
||||||
|
var certs []*x509.Certificate
|
||||||
|
|
||||||
data, err := os.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try PEM first
|
if certs, err = certlib.ParseCertificatesPEM(data); err == nil {
|
||||||
if certs, err := certlib.ParseCertificatesPEM(data); err == nil {
|
|
||||||
return certs, nil
|
return certs, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Try DER/PKCS7/PKCS12 (with no password)
|
if certs, _, err = certlib.ParseCertificatesDER(data, ""); err == nil {
|
||||||
if certs, _, err := certlib.ParseCertificatesDER(data, ""); err == nil {
|
|
||||||
return certs, nil
|
return certs, nil
|
||||||
} else {
|
|
||||||
return nil, err
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePoolFromFile(path string) (*x509.CertPool, error) {
|
func makePoolFromFile(path string) (*x509.CertPool, error) {
|
||||||
@@ -56,22 +57,23 @@ var embeddedTestdata embed.FS
|
|||||||
// loadCertsFromBytes attempts to parse certificates from bytes that may be in
|
// loadCertsFromBytes attempts to parse certificates from bytes that may be in
|
||||||
// PEM or DER/PKCS#7 format.
|
// PEM or DER/PKCS#7 format.
|
||||||
func loadCertsFromBytes(data []byte) ([]*x509.Certificate, error) {
|
func loadCertsFromBytes(data []byte) ([]*x509.Certificate, error) {
|
||||||
// Try PEM first
|
certs, err := certlib.ParseCertificatesPEM(data)
|
||||||
if certs, err := certlib.ParseCertificatesPEM(data); err == nil {
|
if err == nil {
|
||||||
return certs, nil
|
return certs, nil
|
||||||
}
|
}
|
||||||
// Try DER/PKCS7/PKCS12 (with no password)
|
|
||||||
if certs, _, err := certlib.ParseCertificatesDER(data, ""); err == nil {
|
certs, _, err = certlib.ParseCertificatesDER(data, "")
|
||||||
|
if err == nil {
|
||||||
return certs, nil
|
return certs, nil
|
||||||
} else {
|
}
|
||||||
|
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func makePoolFromBytes(data []byte) (*x509.CertPool, error) {
|
func makePoolFromBytes(data []byte) (*x509.CertPool, error) {
|
||||||
certs, err := loadCertsFromBytes(data)
|
certs, err := loadCertsFromBytes(data)
|
||||||
if err != nil || len(certs) == 0 {
|
if err != nil || len(certs) == 0 {
|
||||||
return nil, fmt.Errorf("failed to load CA certificates from embedded bytes")
|
return nil, errors.New("failed to load CA certificates from embedded bytes")
|
||||||
}
|
}
|
||||||
pool := x509.NewCertPool()
|
pool := x509.NewCertPool()
|
||||||
for _, c := range certs {
|
for _, c := range certs {
|
||||||
@@ -98,7 +100,7 @@ func isSelfSigned(cert *x509.Certificate) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyAgainstCA(caPool *x509.CertPool, path string) (ok bool, expiry string) {
|
func verifyAgainstCA(caPool *x509.CertPool, path string) (bool, string) {
|
||||||
certs, err := loadCertsFromFile(path)
|
certs, err := loadCertsFromFile(path)
|
||||||
if err != nil || len(certs) == 0 {
|
if err != nil || len(certs) == 0 {
|
||||||
return false, ""
|
return false, ""
|
||||||
@@ -117,14 +119,14 @@ func verifyAgainstCA(caPool *x509.CertPool, path string) (ok bool, expiry string
|
|||||||
Intermediates: ints,
|
Intermediates: ints,
|
||||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||||
}
|
}
|
||||||
if _, err := leaf.Verify(opts); err != nil {
|
if _, err = leaf.Verify(opts); err != nil {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, leaf.NotAfter.Format("2006-01-02")
|
return true, leaf.NotAfter.Format("2006-01-02")
|
||||||
}
|
}
|
||||||
|
|
||||||
func verifyAgainstCABytes(caPool *x509.CertPool, certData []byte) (ok bool, expiry string) {
|
func verifyAgainstCABytes(caPool *x509.CertPool, certData []byte) (bool, string) {
|
||||||
certs, err := loadCertsFromBytes(certData)
|
certs, err := loadCertsFromBytes(certData)
|
||||||
if err != nil || len(certs) == 0 {
|
if err != nil || len(certs) == 0 {
|
||||||
return false, ""
|
return false, ""
|
||||||
@@ -143,59 +145,86 @@ func verifyAgainstCABytes(caPool *x509.CertPool, certData []byte) (ok bool, expi
|
|||||||
Intermediates: ints,
|
Intermediates: ints,
|
||||||
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
KeyUsages: []x509.ExtKeyUsage{x509.ExtKeyUsageAny},
|
||||||
}
|
}
|
||||||
if _, err := leaf.Verify(opts); err != nil {
|
if _, err = leaf.Verify(opts); err != nil {
|
||||||
return false, ""
|
return false, ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return true, leaf.NotAfter.Format("2006-01-02")
|
return true, leaf.NotAfter.Format("2006-01-02")
|
||||||
}
|
}
|
||||||
|
|
||||||
// selftest runs built-in validation using embedded certificates.
|
type testCase struct {
|
||||||
func selftest() int {
|
|
||||||
type testCase struct {
|
|
||||||
name string
|
name string
|
||||||
caFile string
|
caFile string
|
||||||
certFile string
|
certFile string
|
||||||
expectOK bool
|
expectOK bool
|
||||||
}
|
}
|
||||||
|
|
||||||
cases := []testCase{
|
func (tc testCase) Run() error {
|
||||||
{name: "ISRG Root X1 validates LE E7", caFile: "testdata/isrg-root-x1.pem", certFile: "testdata/le-e7.pem", expectOK: true},
|
|
||||||
{name: "ISRG Root X1 does NOT validate Google WR2", caFile: "testdata/isrg-root-x1.pem", certFile: "testdata/goog-wr2.pem", expectOK: false},
|
|
||||||
{name: "GTS R1 validates Google WR2", caFile: "testdata/gts-r1.pem", certFile: "testdata/goog-wr2.pem", expectOK: true},
|
|
||||||
{name: "GTS R1 does NOT validate LE E7", caFile: "testdata/gts-r1.pem", certFile: "testdata/le-e7.pem", expectOK: false},
|
|
||||||
}
|
|
||||||
|
|
||||||
failures := 0
|
|
||||||
for _, tc := range cases {
|
|
||||||
caBytes, err := embeddedTestdata.ReadFile(tc.caFile)
|
caBytes, err := embeddedTestdata.ReadFile(tc.caFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "selftest: failed to read embedded %s: %v\n", tc.caFile, err)
|
return fmt.Errorf("selftest: failed to read embedded %s: %w", tc.caFile, err)
|
||||||
failures++
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
certBytes, err := embeddedTestdata.ReadFile(tc.certFile)
|
certBytes, err := embeddedTestdata.ReadFile(tc.certFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "selftest: failed to read embedded %s: %v\n", tc.certFile, err)
|
return fmt.Errorf("selftest: failed to read embedded %s: %w", tc.certFile, err)
|
||||||
failures++
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pool, err := makePoolFromBytes(caBytes)
|
pool, err := makePoolFromBytes(caBytes)
|
||||||
if err != nil || pool == nil {
|
if err != nil || pool == nil {
|
||||||
fmt.Fprintf(os.Stderr, "selftest: failed to build CA pool for %s: %v\n", tc.caFile, err)
|
return fmt.Errorf("selftest: failed to build CA pool for %s: %w", tc.caFile, err)
|
||||||
failures++
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ok, exp := verifyAgainstCABytes(pool, certBytes)
|
ok, exp := verifyAgainstCABytes(pool, certBytes)
|
||||||
if ok != tc.expectOK {
|
if ok != tc.expectOK {
|
||||||
fmt.Printf("%s: unexpected result: got %v, want %v\n", tc.name, ok, tc.expectOK)
|
return fmt.Errorf("%s: unexpected result: got %v, want %v", tc.name, ok, tc.expectOK)
|
||||||
failures++
|
}
|
||||||
} else {
|
|
||||||
if ok {
|
if ok {
|
||||||
fmt.Printf("%s: OK (expires %s)\n", tc.name, exp)
|
fmt.Printf("%s: OK (expires %s)\n", tc.name, exp)
|
||||||
} else {
|
|
||||||
fmt.Printf("%s: INVALID (as expected)\n", tc.name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fmt.Printf("%s: INVALID (as expected)\n", tc.name)
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var cases = []testCase{
|
||||||
|
{
|
||||||
|
name: "ISRG Root X1 validates LE E7",
|
||||||
|
caFile: "testdata/isrg-root-x1.pem",
|
||||||
|
certFile: "testdata/le-e7.pem",
|
||||||
|
expectOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "ISRG Root X1 does NOT validate Google WR2",
|
||||||
|
caFile: "testdata/isrg-root-x1.pem",
|
||||||
|
certFile: "testdata/goog-wr2.pem",
|
||||||
|
expectOK: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GTS R1 validates Google WR2",
|
||||||
|
caFile: "testdata/gts-r1.pem",
|
||||||
|
certFile: "testdata/goog-wr2.pem",
|
||||||
|
expectOK: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "GTS R1 does NOT validate LE E7",
|
||||||
|
caFile: "testdata/gts-r1.pem",
|
||||||
|
certFile: "testdata/le-e7.pem",
|
||||||
|
expectOK: false,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// selftest runs built-in validation using embedded certificates.
|
||||||
|
func selftest() int {
|
||||||
|
failures := 0
|
||||||
|
for _, tc := range cases {
|
||||||
|
err := tc.Run()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
failures++
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -231,6 +260,46 @@ func selftest() int {
|
|||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// expiryString returns a YYYY-MM-DD date string to display for certificate
|
||||||
|
// expiry. If an explicit exp string is provided, it is used. Otherwise, if a
|
||||||
|
// leaf certificate is available, its NotAfter is formatted. As a last resort,
|
||||||
|
// it falls back to today's date (should not normally happen).
|
||||||
|
func expiryString(leaf *x509.Certificate, exp string) string {
|
||||||
|
if exp != "" {
|
||||||
|
return exp
|
||||||
|
}
|
||||||
|
if leaf != nil {
|
||||||
|
return leaf.NotAfter.Format("2006-01-02")
|
||||||
|
}
|
||||||
|
return time.Now().Format("2006-01-02")
|
||||||
|
}
|
||||||
|
|
||||||
|
// processCert verifies a single certificate file against the provided CA pool
|
||||||
|
// and prints the result in the required format, handling self-signed
|
||||||
|
// certificates specially.
|
||||||
|
func processCert(caPool *x509.CertPool, certPath string) {
|
||||||
|
ok, exp := verifyAgainstCA(caPool, certPath)
|
||||||
|
name := filepath.Base(certPath)
|
||||||
|
|
||||||
|
// Try to load the leaf cert for self-signed detection and expiry fallback
|
||||||
|
var leaf *x509.Certificate
|
||||||
|
if certs, err := loadCertsFromFile(certPath); err == nil && len(certs) > 0 {
|
||||||
|
leaf = certs[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
// Prefer the SELF-SIGNED label if applicable
|
||||||
|
if isSelfSigned(leaf) {
|
||||||
|
fmt.Printf("%s: SELF-SIGNED\n", name)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if ok {
|
||||||
|
fmt.Printf("%s: OK (expires %s)\n", name, expiryString(leaf, exp))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("%s: INVALID\n", name)
|
||||||
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Special selftest mode: single argument "selftest"
|
// Special selftest mode: single argument "selftest"
|
||||||
if len(os.Args) == 2 && os.Args[1] == "selftest" {
|
if len(os.Args) == 2 && os.Args[1] == "selftest" {
|
||||||
@@ -251,37 +320,6 @@ func main() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, certPath := range os.Args[2:] {
|
for _, certPath := range os.Args[2:] {
|
||||||
ok, exp := verifyAgainstCA(caPool, certPath)
|
processCert(caPool, certPath)
|
||||||
name := filepath.Base(certPath)
|
|
||||||
// Load the leaf once for self-signed detection and potential expiry fallback
|
|
||||||
var leaf *x509.Certificate
|
|
||||||
if certs, err := loadCertsFromFile(certPath); err == nil && len(certs) > 0 {
|
|
||||||
leaf = certs[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the certificate is self-signed, prefer the SELF-SIGNED label
|
|
||||||
if isSelfSigned(leaf) {
|
|
||||||
fmt.Printf("%s: SELF-SIGNED\n", name)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if ok {
|
|
||||||
// Display with the requested format
|
|
||||||
// Example: file: OK (expires 2031-01-01)
|
|
||||||
// Ensure deterministic date formatting
|
|
||||||
// Note: no timezone displayed; date only as per example
|
|
||||||
// If exp ended up empty for some reason, recompute safely
|
|
||||||
if exp == "" {
|
|
||||||
if leaf != nil {
|
|
||||||
exp = leaf.NotAfter.Format("2006-01-02")
|
|
||||||
} else {
|
|
||||||
// fallback to the current date to avoid empty; though shouldn't happen
|
|
||||||
exp = time.Now().Format("2006-01-02")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("%s: OK (expires %s)\n", name, exp)
|
|
||||||
} else {
|
|
||||||
fmt.Printf("%s: INVALID\n", name)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user