cmd: start linting fixes.
This commit is contained in:
@@ -18,6 +18,19 @@ issues:
|
|||||||
# Default: 3
|
# Default: 3
|
||||||
max-same-issues: 50
|
max-same-issues: 50
|
||||||
|
|
||||||
|
# Exclude some lints for CLI programs under cmd/ (package main).
|
||||||
|
# The project allows fmt.Print* in command-line tools; keep forbidigo for libraries.
|
||||||
|
exclude-rules:
|
||||||
|
- path: ^cmd/
|
||||||
|
linters:
|
||||||
|
- forbidigo
|
||||||
|
- path: cmd/.*
|
||||||
|
linters:
|
||||||
|
- forbidigo
|
||||||
|
- path: .*/cmd/.*
|
||||||
|
linters:
|
||||||
|
- forbidigo
|
||||||
|
|
||||||
formatters:
|
formatters:
|
||||||
enable:
|
enable:
|
||||||
- goimports # checks if the code and import statements are formatted according to the 'goimports' command
|
- goimports # checks if the code and import statements are formatted according to the 'goimports' command
|
||||||
|
|||||||
@@ -1,20 +1,20 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"flag"
|
"errors"
|
||||||
"errors"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"net"
|
||||||
"net"
|
"os"
|
||||||
"os"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||||
hosts "git.wntrmute.dev/kyle/goutils/certlib/hosts"
|
hosts "git.wntrmute.dev/kyle/goutils/certlib/hosts"
|
||||||
"git.wntrmute.dev/kyle/goutils/certlib/revoke"
|
"git.wntrmute.dev/kyle/goutils/certlib/revoke"
|
||||||
"git.wntrmute.dev/kyle/goutils/fileutil"
|
"git.wntrmute.dev/kyle/goutils/fileutil"
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@@ -23,6 +23,13 @@ var (
|
|||||||
verbose bool
|
verbose bool
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
strOK = "OK"
|
||||||
|
strExpired = "EXPIRED"
|
||||||
|
strRevoked = "REVOKED"
|
||||||
|
strUnknown = "UNKNOWN"
|
||||||
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.BoolVar(&hardfail, "hardfail", false, "treat revocation check failures as fatal")
|
flag.BoolVar(&hardfail, "hardfail", false, "treat revocation check failures as fatal")
|
||||||
flag.DurationVar(&timeout, "timeout", 10*time.Second, "network timeout for OCSP/CRL fetches and TLS site connects")
|
flag.DurationVar(&timeout, "timeout", 10*time.Second, "network timeout for OCSP/CRL fetches and TLS site connects")
|
||||||
@@ -42,16 +49,16 @@ func main() {
|
|||||||
for _, target := range flag.Args() {
|
for _, target := range flag.Args() {
|
||||||
status, err := processTarget(target)
|
status, err := processTarget(target)
|
||||||
switch status {
|
switch status {
|
||||||
case "OK":
|
case strOK:
|
||||||
fmt.Printf("%s: OK\n", target)
|
fmt.Printf("%s: %s\n", target, strOK)
|
||||||
case "EXPIRED":
|
case strExpired:
|
||||||
fmt.Printf("%s: EXPIRED: %v\n", target, err)
|
fmt.Printf("%s: %s: %v\n", target, strExpired, err)
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
case "REVOKED":
|
case strRevoked:
|
||||||
fmt.Printf("%s: REVOKED\n", target)
|
fmt.Printf("%s: %s\n", target, strRevoked)
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
case "UNKNOWN":
|
case strUnknown:
|
||||||
fmt.Printf("%s: UNKNOWN: %v\n", target, err)
|
fmt.Printf("%s: %s: %v\n", target, strUnknown, err)
|
||||||
if hardfail {
|
if hardfail {
|
||||||
// In hardfail, treat unknown as failure
|
// In hardfail, treat unknown as failure
|
||||||
exitCode = 1
|
exitCode = 1
|
||||||
@@ -67,74 +74,67 @@ func processTarget(target string) (string, error) {
|
|||||||
return checkFile(target)
|
return checkFile(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not a file; treat as site
|
|
||||||
return checkSite(target)
|
return checkSite(target)
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkFile(path string) (string, error) {
|
func checkFile(path string) (string, error) {
|
||||||
in, err := ioutil.ReadFile(path)
|
// Prefer high-level helpers from certlib to load certificates from disk
|
||||||
if err != nil {
|
if certs, err := certlib.LoadCertificates(path); err == nil && len(certs) > 0 {
|
||||||
return "UNKNOWN", err
|
// Evaluate the first certificate (leaf) by default
|
||||||
}
|
return evaluateCert(certs[0])
|
||||||
|
}
|
||||||
|
|
||||||
// Try PEM first; if that fails, try single DER cert
|
cert, err := certlib.LoadCertificate(path)
|
||||||
certs, err := certlib.ReadCertificates(in)
|
if err != nil || cert == nil {
|
||||||
if err != nil || len(certs) == 0 {
|
return strUnknown, err
|
||||||
cert, _, derr := certlib.ReadCertificate(in)
|
}
|
||||||
if derr != nil || cert == nil {
|
return evaluateCert(cert)
|
||||||
if err == nil {
|
|
||||||
err = derr
|
|
||||||
}
|
|
||||||
return "UNKNOWN", err
|
|
||||||
}
|
|
||||||
return evaluateCert(cert)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Evaluate the first certificate (leaf) by default
|
|
||||||
return evaluateCert(certs[0])
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkSite(hostport string) (string, error) {
|
func checkSite(hostport string) (string, error) {
|
||||||
// Use certlib/hosts to parse host/port (supports https URLs and host:port)
|
// Use certlib/hosts to parse host/port (supports https URLs and host:port)
|
||||||
target, err := hosts.ParseHost(hostport)
|
target, err := hosts.ParseHost(hostport)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "UNKNOWN", err
|
return strUnknown, err
|
||||||
}
|
}
|
||||||
|
|
||||||
d := &net.Dialer{Timeout: timeout}
|
d := &net.Dialer{Timeout: timeout}
|
||||||
conn, err := tls.DialWithDialer(d, "tcp", target.String(), &tls.Config{InsecureSkipVerify: true, ServerName: target.Host})
|
conn, err := tls.DialWithDialer(
|
||||||
|
d,
|
||||||
|
"tcp",
|
||||||
|
target.String(),
|
||||||
|
&tls.Config{InsecureSkipVerify: true, ServerName: target.Host}, // #nosec G402
|
||||||
|
)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "UNKNOWN", err
|
return strUnknown, err
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
state := conn.ConnectionState()
|
state := conn.ConnectionState()
|
||||||
if len(state.PeerCertificates) == 0 {
|
if len(state.PeerCertificates) == 0 {
|
||||||
return "UNKNOWN", errors.New("no peer certificates presented")
|
return strUnknown, errors.New("no peer certificates presented")
|
||||||
}
|
}
|
||||||
return evaluateCert(state.PeerCertificates[0])
|
return evaluateCert(state.PeerCertificates[0])
|
||||||
}
|
}
|
||||||
|
|
||||||
func evaluateCert(cert *x509.Certificate) (string, error) {
|
func evaluateCert(cert *x509.Certificate) (string, error) {
|
||||||
// Expiry check
|
// Delegate validity and revocation checks to certlib/revoke helper.
|
||||||
now := time.Now()
|
// It returns revoked=true for both revoked and expired/not-yet-valid.
|
||||||
if !now.Before(cert.NotAfter) {
|
// Map those cases back to our statuses using the returned error text.
|
||||||
return "EXPIRED", fmt.Errorf("expired at %s", cert.NotAfter)
|
revoked, ok, err := revoke.VerifyCertificateError(cert)
|
||||||
}
|
if revoked {
|
||||||
if !now.After(cert.NotBefore) {
|
if err != nil {
|
||||||
return "EXPIRED", fmt.Errorf("not valid until %s", cert.NotBefore)
|
msg := err.Error()
|
||||||
}
|
if strings.Contains(msg, "expired") || strings.Contains(msg, "isn't valid until") || strings.Contains(msg, "not valid until") {
|
||||||
|
return strExpired, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return strRevoked, err
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
// Revocation status could not be determined
|
||||||
|
return strUnknown, err
|
||||||
|
}
|
||||||
|
|
||||||
// Revocation check using certlib/revoke
|
return strOK, nil
|
||||||
revoked, ok, err := revoke.VerifyCertificateError(cert)
|
|
||||||
if revoked {
|
|
||||||
// If revoked is true, ok will be true per implementation, err may describe why
|
|
||||||
return "REVOKED", err
|
|
||||||
}
|
|
||||||
if !ok {
|
|
||||||
// Revocation status could not be determined
|
|
||||||
return "UNKNOWN", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return "OK", nil
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/die"
|
"git.wntrmute.dev/kyle/goutils/die"
|
||||||
@@ -34,6 +35,6 @@ func main() {
|
|||||||
chain += string(pem.EncodeToMemory(&p))
|
chain += string(pem.EncodeToMemory(&p))
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(chain)
|
fmt.Fprintln(os.Stdout, chain)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,30 +101,30 @@ func extUsage(ext []x509.ExtKeyUsage) string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func showBasicConstraints(cert *x509.Certificate) {
|
func showBasicConstraints(cert *x509.Certificate) {
|
||||||
fmt.Printf("\tBasic constraints: ")
|
fmt.Fprint(os.Stdout, "\tBasic constraints: ")
|
||||||
if cert.BasicConstraintsValid {
|
if cert.BasicConstraintsValid {
|
||||||
fmt.Printf("valid")
|
fmt.Fprint(os.Stdout, "valid")
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("invalid")
|
fmt.Fprint(os.Stdout, "invalid")
|
||||||
}
|
}
|
||||||
|
|
||||||
if cert.IsCA {
|
if cert.IsCA {
|
||||||
fmt.Printf(", is a CA certificate")
|
fmt.Fprint(os.Stdout, ", is a CA certificate")
|
||||||
if !cert.BasicConstraintsValid {
|
if !cert.BasicConstraintsValid {
|
||||||
fmt.Printf(" (basic constraint failure)")
|
fmt.Fprint(os.Stdout, " (basic constraint failure)")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf("is not a CA certificate")
|
fmt.Fprint(os.Stdout, "is not a CA certificate")
|
||||||
if cert.KeyUsage&x509.KeyUsageKeyEncipherment != 0 {
|
if cert.KeyUsage&x509.KeyUsageKeyEncipherment != 0 {
|
||||||
fmt.Printf(" (key encipherment usage enabled!)")
|
fmt.Fprint(os.Stdout, " (key encipherment usage enabled!)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cert.MaxPathLen == 0 && cert.MaxPathLenZero) || (cert.MaxPathLen > 0) {
|
if (cert.MaxPathLen == 0 && cert.MaxPathLenZero) || (cert.MaxPathLen > 0) {
|
||||||
fmt.Printf(", max path length %d", cert.MaxPathLen)
|
fmt.Fprintf(os.Stdout, ", max path length %d", cert.MaxPathLen)
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\n")
|
fmt.Fprintln(os.Stdout)
|
||||||
}
|
}
|
||||||
|
|
||||||
const oneTrueDateFormat = "2006-01-02T15:04:05-0700"
|
const oneTrueDateFormat = "2006-01-02T15:04:05-0700"
|
||||||
@@ -135,41 +135,41 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func wrapPrint(text string, indent int) {
|
func wrapPrint(text string, indent int) {
|
||||||
tabs := ""
|
tabs := ""
|
||||||
for i := 0; i < indent; i++ {
|
for i := 0; i < indent; i++ {
|
||||||
tabs += "\t"
|
tabs += "\t"
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf(tabs+"%s\n", wrap(text, indent))
|
fmt.Fprintf(os.Stdout, tabs+"%s\n", wrap(text, indent))
|
||||||
}
|
}
|
||||||
|
|
||||||
func displayCert(cert *x509.Certificate) {
|
func displayCert(cert *x509.Certificate) {
|
||||||
fmt.Println("CERTIFICATE")
|
fmt.Fprintln(os.Stdout, "CERTIFICATE")
|
||||||
if showHash {
|
if showHash {
|
||||||
fmt.Println(wrap(fmt.Sprintf("SHA256: %x", sha256.Sum256(cert.Raw)), 0))
|
fmt.Fprintln(os.Stdout, wrap(fmt.Sprintf("SHA256: %x", sha256.Sum256(cert.Raw)), 0))
|
||||||
}
|
}
|
||||||
fmt.Println(wrap("Subject: "+displayName(cert.Subject), 0))
|
fmt.Fprintln(os.Stdout, wrap("Subject: "+displayName(cert.Subject), 0))
|
||||||
fmt.Println(wrap("Issuer: "+displayName(cert.Issuer), 0))
|
fmt.Fprintln(os.Stdout, wrap("Issuer: "+displayName(cert.Issuer), 0))
|
||||||
fmt.Printf("\tSignature algorithm: %s / %s\n", sigAlgoPK(cert.SignatureAlgorithm),
|
fmt.Fprintf(os.Stdout, "\tSignature algorithm: %s / %s\n", sigAlgoPK(cert.SignatureAlgorithm),
|
||||||
sigAlgoHash(cert.SignatureAlgorithm))
|
sigAlgoHash(cert.SignatureAlgorithm))
|
||||||
fmt.Println("Details:")
|
fmt.Fprintln(os.Stdout, "Details:")
|
||||||
wrapPrint("Public key: "+certPublic(cert), 1)
|
wrapPrint("Public key: "+certPublic(cert), 1)
|
||||||
fmt.Printf("\tSerial number: %s\n", cert.SerialNumber)
|
fmt.Fprintf(os.Stdout, "\tSerial number: %s\n", cert.SerialNumber)
|
||||||
|
|
||||||
if len(cert.AuthorityKeyId) > 0 {
|
if len(cert.AuthorityKeyId) > 0 {
|
||||||
fmt.Printf("\t%s\n", wrap("AKI: "+dumpHex(cert.AuthorityKeyId), 1))
|
fmt.Fprintf(os.Stdout, "\t%s\n", wrap("AKI: "+dumpHex(cert.AuthorityKeyId), 1))
|
||||||
}
|
}
|
||||||
if len(cert.SubjectKeyId) > 0 {
|
if len(cert.SubjectKeyId) > 0 {
|
||||||
fmt.Printf("\t%s\n", wrap("SKI: "+dumpHex(cert.SubjectKeyId), 1))
|
fmt.Fprintf(os.Stdout, "\t%s\n", wrap("SKI: "+dumpHex(cert.SubjectKeyId), 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
wrapPrint("Valid from: "+cert.NotBefore.Format(dateFormat), 1)
|
wrapPrint("Valid from: "+cert.NotBefore.Format(dateFormat), 1)
|
||||||
fmt.Printf("\t until: %s\n", cert.NotAfter.Format(dateFormat))
|
fmt.Fprintf(os.Stdout, "\t until: %s\n", cert.NotAfter.Format(dateFormat))
|
||||||
fmt.Printf("\tKey usages: %s\n", keyUsages(cert.KeyUsage))
|
fmt.Fprintf(os.Stdout, "\tKey usages: %s\n", keyUsages(cert.KeyUsage))
|
||||||
|
|
||||||
if len(cert.ExtKeyUsage) > 0 {
|
if len(cert.ExtKeyUsage) > 0 {
|
||||||
fmt.Printf("\tExtended usages: %s\n", extUsage(cert.ExtKeyUsage))
|
fmt.Fprintf(os.Stdout, "\tExtended usages: %s\n", extUsage(cert.ExtKeyUsage))
|
||||||
}
|
}
|
||||||
|
|
||||||
showBasicConstraints(cert)
|
showBasicConstraints(cert)
|
||||||
|
|
||||||
@@ -217,19 +217,19 @@ func displayCert(cert *x509.Certificate) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func displayAllCerts(in []byte, leafOnly bool) {
|
func displayAllCerts(in []byte, leafOnly bool) {
|
||||||
certs, err := certlib.ParseCertificatesPEM(in)
|
certs, err := certlib.ParseCertificatesPEM(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
certs, _, err = certlib.ParseCertificatesDER(in, "")
|
certs, _, err = certlib.ParseCertificatesDER(in, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "failed to parse certificates")
|
_, _ = lib.Warn(err, "failed to parse certificates")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(certs) == 0 {
|
if len(certs) == 0 {
|
||||||
lib.Warnx("no certificates found")
|
_, _ = lib.Warnx("no certificates found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leafOnly {
|
if leafOnly {
|
||||||
displayCert(certs[0])
|
displayCert(certs[0])
|
||||||
@@ -243,11 +243,11 @@ func displayAllCerts(in []byte, leafOnly bool) {
|
|||||||
|
|
||||||
func displayAllCertsWeb(uri string, leafOnly bool) {
|
func displayAllCertsWeb(uri string, leafOnly bool) {
|
||||||
ci := getConnInfo(uri)
|
ci := getConnInfo(uri)
|
||||||
conn, err := tls.Dial("tcp", ci.Addr, permissiveConfig())
|
conn, err := tls.Dial("tcp", ci.Addr, permissiveConfig())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "couldn't connect to %s", ci.Addr)
|
_, _ = lib.Warn(err, "couldn't connect to %s", ci.Addr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
state := conn.ConnectionState()
|
state := conn.ConnectionState()
|
||||||
@@ -260,34 +260,34 @@ func displayAllCertsWeb(uri string, leafOnly bool) {
|
|||||||
state = conn.ConnectionState()
|
state = conn.ConnectionState()
|
||||||
}
|
}
|
||||||
conn.Close()
|
conn.Close()
|
||||||
} else {
|
} else {
|
||||||
lib.Warn(err, "TLS verification error with server name %s", ci.Host)
|
_, _ = lib.Warn(err, "TLS verification error with server name %s", ci.Host)
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(state.PeerCertificates) == 0 {
|
if len(state.PeerCertificates) == 0 {
|
||||||
lib.Warnx("no certificates found")
|
_, _ = lib.Warnx("no certificates found")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if leafOnly {
|
if leafOnly {
|
||||||
displayCert(state.PeerCertificates[0])
|
displayCert(state.PeerCertificates[0])
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(state.VerifiedChains) == 0 {
|
if len(state.VerifiedChains) == 0 {
|
||||||
lib.Warnx("no verified chains found; using peer chain")
|
_, _ = lib.Warnx("no verified chains found; using peer chain")
|
||||||
for i := range state.PeerCertificates {
|
for i := range state.PeerCertificates {
|
||||||
displayCert(state.PeerCertificates[i])
|
displayCert(state.PeerCertificates[i])
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("TLS chain verified successfully.")
|
fmt.Fprintln(os.Stdout, "TLS chain verified successfully.")
|
||||||
for i := range state.VerifiedChains {
|
for i := range state.VerifiedChains {
|
||||||
fmt.Printf("--- Verified certificate chain %d ---\n", i+1)
|
fmt.Fprintf(os.Stdout, "--- Verified certificate chain %d ---%s", i+1, "\n")
|
||||||
for j := range state.VerifiedChains[i] {
|
for j := range state.VerifiedChains[i] {
|
||||||
displayCert(state.VerifiedChains[i][j])
|
displayCert(state.VerifiedChains[i][j])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -298,11 +298,11 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
if flag.NArg() == 0 || (flag.NArg() == 1 && flag.Arg(0) == "-") {
|
if flag.NArg() == 0 || (flag.NArg() == 1 && flag.Arg(0) == "-") {
|
||||||
certs, err := io.ReadAll(os.Stdin)
|
certs, err := io.ReadAll(os.Stdin)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "couldn't read certificates from standard input")
|
_, _ = lib.Warn(err, "couldn't read certificates from standard input")
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is needed for getting certs from JSON/jq.
|
// This is needed for getting certs from JSON/jq.
|
||||||
certs = bytes.TrimSpace(certs)
|
certs = bytes.TrimSpace(certs)
|
||||||
@@ -311,15 +311,15 @@ func main() {
|
|||||||
displayAllCerts(certs, leafOnly)
|
displayAllCerts(certs, leafOnly)
|
||||||
} else {
|
} else {
|
||||||
for _, filename := range flag.Args() {
|
for _, filename := range flag.Args() {
|
||||||
fmt.Printf("--%s ---\n", filename)
|
fmt.Fprintf(os.Stdout, "--%s ---%s", filename, "\n")
|
||||||
if strings.HasPrefix(filename, "https://") {
|
if strings.HasPrefix(filename, "https://") {
|
||||||
displayAllCertsWeb(filename, leafOnly)
|
displayAllCertsWeb(filename, leafOnly)
|
||||||
} else {
|
} else {
|
||||||
in, err := os.ReadFile(filename)
|
in, err := os.ReadFile(filename)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "couldn't read certificate")
|
_, _ = lib.Warn(err, "couldn't read certificate")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
displayAllCerts(in, leafOnly)
|
displayAllCerts(in, leafOnly)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,64 +26,85 @@ var keyUsage = map[x509.KeyUsage]string{
|
|||||||
}
|
}
|
||||||
|
|
||||||
var extKeyUsages = map[x509.ExtKeyUsage]string{
|
var extKeyUsages = map[x509.ExtKeyUsage]string{
|
||||||
x509.ExtKeyUsageAny: "any",
|
x509.ExtKeyUsageAny: "any",
|
||||||
x509.ExtKeyUsageServerAuth: "server auth",
|
x509.ExtKeyUsageServerAuth: "server auth",
|
||||||
x509.ExtKeyUsageClientAuth: "client auth",
|
x509.ExtKeyUsageClientAuth: "client auth",
|
||||||
x509.ExtKeyUsageCodeSigning: "code signing",
|
x509.ExtKeyUsageCodeSigning: "code signing",
|
||||||
x509.ExtKeyUsageEmailProtection: "s/mime",
|
x509.ExtKeyUsageEmailProtection: "s/mime",
|
||||||
x509.ExtKeyUsageIPSECEndSystem: "ipsec end system",
|
x509.ExtKeyUsageIPSECEndSystem: "ipsec end system",
|
||||||
x509.ExtKeyUsageIPSECTunnel: "ipsec tunnel",
|
x509.ExtKeyUsageIPSECTunnel: "ipsec tunnel",
|
||||||
x509.ExtKeyUsageIPSECUser: "ipsec user",
|
x509.ExtKeyUsageIPSECUser: "ipsec user",
|
||||||
x509.ExtKeyUsageTimeStamping: "timestamping",
|
x509.ExtKeyUsageTimeStamping: "timestamping",
|
||||||
x509.ExtKeyUsageOCSPSigning: "ocsp signing",
|
x509.ExtKeyUsageOCSPSigning: "ocsp signing",
|
||||||
x509.ExtKeyUsageMicrosoftServerGatedCrypto: "microsoft sgc",
|
x509.ExtKeyUsageMicrosoftServerGatedCrypto: "microsoft sgc",
|
||||||
x509.ExtKeyUsageNetscapeServerGatedCrypto: "netscape sgc",
|
x509.ExtKeyUsageNetscapeServerGatedCrypto: "netscape sgc",
|
||||||
|
x509.ExtKeyUsageMicrosoftCommercialCodeSigning: "microsoft commercial code signing",
|
||||||
|
x509.ExtKeyUsageMicrosoftKernelCodeSigning: "microsoft kernel code signing",
|
||||||
}
|
}
|
||||||
|
|
||||||
func pubKeyAlgo(a x509.PublicKeyAlgorithm) string {
|
func pubKeyAlgo(a x509.PublicKeyAlgorithm) string {
|
||||||
switch a {
|
switch a {
|
||||||
case x509.RSA:
|
case x509.UnknownPublicKeyAlgorithm:
|
||||||
return "RSA"
|
return "unknown public key algorithm"
|
||||||
case x509.ECDSA:
|
case x509.RSA:
|
||||||
return "ECDSA"
|
return "RSA"
|
||||||
case x509.DSA:
|
case x509.ECDSA:
|
||||||
return "DSA"
|
return "ECDSA"
|
||||||
default:
|
case x509.DSA:
|
||||||
return "unknown public key algorithm"
|
return "DSA"
|
||||||
}
|
case x509.Ed25519:
|
||||||
|
return "Ed25519"
|
||||||
|
default:
|
||||||
|
return "unknown public key algorithm"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sigAlgoPK(a x509.SignatureAlgorithm) string {
|
func sigAlgoPK(a x509.SignatureAlgorithm) string {
|
||||||
switch a {
|
switch a {
|
||||||
|
case x509.MD2WithRSA, x509.MD5WithRSA, x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
|
||||||
case x509.MD2WithRSA, x509.MD5WithRSA, x509.SHA1WithRSA, x509.SHA256WithRSA, x509.SHA384WithRSA, x509.SHA512WithRSA:
|
return "RSA"
|
||||||
return "RSA"
|
case x509.SHA256WithRSAPSS, x509.SHA384WithRSAPSS, x509.SHA512WithRSAPSS:
|
||||||
case x509.ECDSAWithSHA1, x509.ECDSAWithSHA256, x509.ECDSAWithSHA384, x509.ECDSAWithSHA512:
|
return "RSA-PSS"
|
||||||
return "ECDSA"
|
case x509.ECDSAWithSHA1, x509.ECDSAWithSHA256, x509.ECDSAWithSHA384, x509.ECDSAWithSHA512:
|
||||||
case x509.DSAWithSHA1, x509.DSAWithSHA256:
|
return "ECDSA"
|
||||||
return "DSA"
|
case x509.DSAWithSHA1, x509.DSAWithSHA256:
|
||||||
default:
|
return "DSA"
|
||||||
return "unknown public key algorithm"
|
case x509.PureEd25519:
|
||||||
}
|
return "Ed25519"
|
||||||
|
case x509.UnknownSignatureAlgorithm:
|
||||||
|
return "unknown public key algorithm"
|
||||||
|
default:
|
||||||
|
return "unknown public key algorithm"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func sigAlgoHash(a x509.SignatureAlgorithm) string {
|
func sigAlgoHash(a x509.SignatureAlgorithm) string {
|
||||||
switch a {
|
switch a {
|
||||||
case x509.MD2WithRSA:
|
case x509.MD2WithRSA:
|
||||||
return "MD2"
|
return "MD2"
|
||||||
case x509.MD5WithRSA:
|
case x509.MD5WithRSA:
|
||||||
return "MD5"
|
return "MD5"
|
||||||
case x509.SHA1WithRSA, x509.ECDSAWithSHA1, x509.DSAWithSHA1:
|
case x509.SHA1WithRSA, x509.ECDSAWithSHA1, x509.DSAWithSHA1:
|
||||||
return "SHA1"
|
return "SHA1"
|
||||||
case x509.SHA256WithRSA, x509.ECDSAWithSHA256, x509.DSAWithSHA256:
|
case x509.SHA256WithRSA, x509.ECDSAWithSHA256, x509.DSAWithSHA256:
|
||||||
return "SHA256"
|
return "SHA256"
|
||||||
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
|
case x509.SHA256WithRSAPSS:
|
||||||
return "SHA384"
|
return "SHA256"
|
||||||
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
|
case x509.SHA384WithRSA, x509.ECDSAWithSHA384:
|
||||||
return "SHA512"
|
return "SHA384"
|
||||||
default:
|
case x509.SHA384WithRSAPSS:
|
||||||
return "unknown hash algorithm"
|
return "SHA384"
|
||||||
}
|
case x509.SHA512WithRSA, x509.ECDSAWithSHA512:
|
||||||
|
return "SHA512"
|
||||||
|
case x509.SHA512WithRSAPSS:
|
||||||
|
return "SHA512"
|
||||||
|
case x509.PureEd25519:
|
||||||
|
return "SHA512"
|
||||||
|
case x509.UnknownSignatureAlgorithm:
|
||||||
|
return "unknown hash algorithm"
|
||||||
|
default:
|
||||||
|
return "unknown hash algorithm"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const maxLine = 78
|
const maxLine = 78
|
||||||
|
|||||||
@@ -1,18 +1,17 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"os"
|
"strings"
|
||||||
"strings"
|
"time"
|
||||||
"time"
|
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/certlib"
|
"git.wntrmute.dev/kyle/goutils/certlib"
|
||||||
"git.wntrmute.dev/kyle/goutils/die"
|
"git.wntrmute.dev/kyle/goutils/die"
|
||||||
"git.wntrmute.dev/kyle/goutils/lib"
|
"git.wntrmute.dev/kyle/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
var warnOnly bool
|
var warnOnly bool
|
||||||
@@ -80,21 +79,21 @@ func main() {
|
|||||||
flag.DurationVar(&leeway, "t", leeway, "warn if certificates are closer than this to expiring")
|
flag.DurationVar(&leeway, "t", leeway, "warn if certificates are closer than this to expiring")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
for _, file := range flag.Args() {
|
for _, file := range flag.Args() {
|
||||||
in, err := ioutil.ReadFile(file)
|
in, err := os.ReadFile(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "failed to read file")
|
_, _ = lib.Warn(err, "failed to read file")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
certs, err := certlib.ParseCertificatesPEM(in)
|
certs, err := certlib.ParseCertificatesPEM(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "while parsing certificates")
|
_, _ = lib.Warn(err, "while parsing certificates")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cert := range certs {
|
for _, cert := range certs {
|
||||||
checkCert(cert)
|
checkCert(cert)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,16 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
"errors"
|
||||||
"net"
|
"log"
|
||||||
"os"
|
"net"
|
||||||
"strings"
|
"os"
|
||||||
"sync"
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/lib"
|
"git.wntrmute.dev/kyle/goutils/lib"
|
||||||
"github.com/pkg/sftp"
|
"github.com/pkg/sftp"
|
||||||
@@ -92,12 +93,12 @@ func exec(wg *sync.WaitGroup, user, host string, commands []string) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
for i := len(shutdown) - 1; i >= 0; i-- {
|
for i := len(shutdown) - 1; i >= 0; i-- {
|
||||||
err := shutdown[i]()
|
err := shutdown[i]()
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && !errors.Is(err, io.EOF) {
|
||||||
logError(host, err, "shutting down")
|
logError(host, err, "shutting down")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
conf := sshConfig(user)
|
conf := sshConfig(user)
|
||||||
@@ -149,12 +150,12 @@ func upload(wg *sync.WaitGroup, user, host, local, remote string) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
for i := len(shutdown) - 1; i >= 0; i-- {
|
for i := len(shutdown) - 1; i >= 0; i-- {
|
||||||
err := shutdown[i]()
|
err := shutdown[i]()
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && !errors.Is(err, io.EOF) {
|
||||||
logError(host, err, "shutting down")
|
logError(host, err, "shutting down")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
conf := sshConfig(user)
|
conf := sshConfig(user)
|
||||||
@@ -199,13 +200,13 @@ func upload(wg *sync.WaitGroup, user, host, local, remote string) {
|
|||||||
fmt.Printf("[%s] wrote %d-byte chunk\n", host, n)
|
fmt.Printf("[%s] wrote %d-byte chunk\n", host, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == io.EOF {
|
if errors.Is(err, io.EOF) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
logError(host, err, "reading chunk")
|
logError(host, err, "reading chunk")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Printf("[%s] %s uploaded to %s\n", host, remote, local)
|
fmt.Printf("[%s] %s uploaded to %s\n", host, remote, local)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -214,12 +215,12 @@ func download(wg *sync.WaitGroup, user, host, local, remote string) {
|
|||||||
|
|
||||||
defer func() {
|
defer func() {
|
||||||
for i := len(shutdown) - 1; i >= 0; i-- {
|
for i := len(shutdown) - 1; i >= 0; i-- {
|
||||||
err := shutdown[i]()
|
err := shutdown[i]()
|
||||||
if err != nil && err != io.EOF {
|
if err != nil && !errors.Is(err, io.EOF) {
|
||||||
logError(host, err, "shutting down")
|
logError(host, err, "shutting down")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
conf := sshConfig(user)
|
conf := sshConfig(user)
|
||||||
@@ -265,12 +266,12 @@ func download(wg *sync.WaitGroup, user, host, local, remote string) {
|
|||||||
fmt.Printf("[%s] wrote %d-byte chunk\n", host, n)
|
fmt.Printf("[%s] wrote %d-byte chunk\n", host, n)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err == io.EOF {
|
if errors.Is(err, io.EOF) {
|
||||||
break
|
break
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
logError(host, err, "reading chunk")
|
logError(host, err, "reading chunk")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
fmt.Printf("[%s] %s downloaded to %s\n", host, remote, local)
|
fmt.Printf("[%s] %s downloaded to %s\n", host, remote, local)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -262,11 +262,11 @@ func main() {
|
|||||||
|
|
||||||
tfr := tar.NewReader(r)
|
tfr := tar.NewReader(r)
|
||||||
for {
|
for {
|
||||||
hdr, err := tfr.Next()
|
hdr, err := tfr.Next()
|
||||||
if err == io.EOF {
|
if errors.Is(err, io.EOF) {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|
||||||
err = processFile(tfr, hdr, top)
|
err = processFile(tfr, hdr, top)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|||||||
@@ -7,8 +7,7 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"log"
|
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/die"
|
"git.wntrmute.dev/kyle/goutils/die"
|
||||||
)
|
)
|
||||||
@@ -17,12 +16,12 @@ func main() {
|
|||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
for _, fileName := range flag.Args() {
|
for _, fileName := range flag.Args() {
|
||||||
in, err := ioutil.ReadFile(fileName)
|
in, err := os.ReadFile(fileName)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|
||||||
if p, _ := pem.Decode(in); p != nil {
|
if p, _ := pem.Decode(in); p != nil {
|
||||||
if p.Type != "CERTIFICATE REQUEST" {
|
if p.Type != "CERTIFICATE REQUEST" {
|
||||||
log.Fatal("INVALID FILE TYPE")
|
die.With("INVALID FILE TYPE")
|
||||||
}
|
}
|
||||||
in = p.Bytes
|
in = p.Bytes
|
||||||
}
|
}
|
||||||
@@ -48,8 +47,8 @@ func main() {
|
|||||||
Bytes: out,
|
Bytes: out,
|
||||||
}
|
}
|
||||||
|
|
||||||
err = ioutil.WriteFile(fileName+".pub", pem.EncodeToMemory(p), 0644)
|
err = os.WriteFile(fileName+".pub", pem.EncodeToMemory(p), 0o644)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
fmt.Printf("[+] wrote %s.\n", fileName+".pub")
|
fmt.Fprintf(os.Stdout, "[+] wrote %s.\n", fileName+".pub")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,12 +95,12 @@ func main() {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
fmtStr += "\n"
|
fmtStr += "\n"
|
||||||
for i := start; !endFunc(i); i++ {
|
for i := start; !endFunc(i); i++ {
|
||||||
if *quiet {
|
if *quiet {
|
||||||
fmt.Println(lines[i])
|
fmt.Fprintln(os.Stdout, lines[i])
|
||||||
} else {
|
} else {
|
||||||
fmt.Printf(fmtStr, i, lines[i])
|
fmt.Fprintf(os.Stdout, fmtStr, i, lines[i])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
102
cmd/jlp/jlp.go
102
cmd/jlp/jlp.go
@@ -1,51 +1,51 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/lib"
|
"git.wntrmute.dev/kyle/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func prettify(file string, validateOnly bool) error {
|
func prettify(file string, validateOnly bool) error {
|
||||||
var in []byte
|
var in []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if file == "-" {
|
if file == "-" {
|
||||||
in, err = ioutil.ReadAll(os.Stdin)
|
in, err = io.ReadAll(os.Stdin)
|
||||||
} else {
|
} else {
|
||||||
in, err = ioutil.ReadFile(file)
|
in, err = os.ReadFile(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "ReadFile")
|
_, _ = lib.Warn(err, "ReadFile")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf = &bytes.Buffer{}
|
var buf = &bytes.Buffer{}
|
||||||
err = json.Indent(buf, in, "", " ")
|
err = json.Indent(buf, in, "", " ")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "%s", file)
|
_, _ = lib.Warn(err, "%s", file)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if validateOnly {
|
if validateOnly {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if file == "-" {
|
if file == "-" {
|
||||||
_, err = os.Stdout.Write(buf.Bytes())
|
_, err = os.Stdout.Write(buf.Bytes())
|
||||||
} else {
|
} else {
|
||||||
err = ioutil.WriteFile(file, buf.Bytes(), 0644)
|
err = os.WriteFile(file, buf.Bytes(), 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "WriteFile")
|
_, _ = lib.Warn(err, "WriteFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -54,44 +54,44 @@ func compact(file string, validateOnly bool) error {
|
|||||||
var in []byte
|
var in []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
if file == "-" {
|
if file == "-" {
|
||||||
in, err = ioutil.ReadAll(os.Stdin)
|
in, err = io.ReadAll(os.Stdin)
|
||||||
} else {
|
} else {
|
||||||
in, err = ioutil.ReadFile(file)
|
in, err = os.ReadFile(file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "ReadFile")
|
_, _ = lib.Warn(err, "ReadFile")
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
var buf = &bytes.Buffer{}
|
var buf = &bytes.Buffer{}
|
||||||
err = json.Compact(buf, in)
|
err = json.Compact(buf, in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "%s", file)
|
_, _ = lib.Warn(err, "%s", file)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if validateOnly {
|
if validateOnly {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if file == "-" {
|
if file == "-" {
|
||||||
_, err = os.Stdout.Write(buf.Bytes())
|
_, err = os.Stdout.Write(buf.Bytes())
|
||||||
} else {
|
} else {
|
||||||
err = ioutil.WriteFile(file, buf.Bytes(), 0644)
|
err = os.WriteFile(file, buf.Bytes(), 0o644)
|
||||||
}
|
}
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "WriteFile")
|
_, _ = lib.Warn(err, "WriteFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func usage() {
|
func usage() {
|
||||||
progname := lib.ProgName()
|
progname := lib.ProgName()
|
||||||
fmt.Printf(`Usage: %s [-h] files...
|
fmt.Fprintf(os.Stdout, `Usage: %s [-h] files...
|
||||||
%s is used to lint and prettify (or compact) JSON files. The
|
%s is used to lint and prettify (or compact) JSON files. The
|
||||||
files will be updated in-place.
|
files will be updated in-place.
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,8 @@ func main() {
|
|||||||
max, err := strconv.Atoi(flag.Arg(2))
|
max, err := strconv.Atoi(flag.Arg(2))
|
||||||
dieIf(err)
|
dieIf(err)
|
||||||
|
|
||||||
code := kind << 6
|
code := kind << 6
|
||||||
code += (min << 3)
|
code += (min << 3)
|
||||||
code += max
|
code += max
|
||||||
fmt.Printf("%0o\n", code)
|
fmt.Fprintf(os.Stdout, "%0o\n", code)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,8 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"io"
|
||||||
"io/ioutil"
|
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/lib"
|
"git.wntrmute.dev/kyle/goutils/lib"
|
||||||
@@ -19,19 +18,21 @@ func main() {
|
|||||||
var in []byte
|
var in []byte
|
||||||
var err error
|
var err error
|
||||||
|
|
||||||
path := flag.Arg(0)
|
path := flag.Arg(0)
|
||||||
if path == "-" {
|
if path == "-" {
|
||||||
in, err = ioutil.ReadAll(os.Stdin)
|
in, err = io.ReadAll(os.Stdin)
|
||||||
} else {
|
} else {
|
||||||
in, err = ioutil.ReadFile(flag.Arg(0))
|
in, err = os.ReadFile(flag.Arg(0))
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Err(lib.ExitFailure, err, "couldn't read file")
|
lib.Err(lib.ExitFailure, err, "couldn't read file")
|
||||||
}
|
}
|
||||||
|
|
||||||
p, _ := pem.Decode(in)
|
p, _ := pem.Decode(in)
|
||||||
if p == nil {
|
if p == nil {
|
||||||
lib.Errx(lib.ExitFailure, "%s isn't a PEM-encoded file", flag.Arg(0))
|
lib.Errx(lib.ExitFailure, "%s isn't a PEM-encoded file", flag.Arg(0))
|
||||||
}
|
}
|
||||||
fmt.Printf("%s", p.Bytes)
|
if _, err := os.Stdout.Write(p.Bytes); err != nil {
|
||||||
|
lib.Err(lib.ExitFailure, err, "writing body")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,24 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"os"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
for _, fileName := range flag.Args() {
|
for _, fileName := range flag.Args() {
|
||||||
data, err := ioutil.ReadFile(fileName)
|
data, err := os.ReadFile(fileName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintf(os.Stderr, "[!] %s: %v\n", fileName, err)
|
fmt.Fprintf(os.Stderr, "[!] %s: %v\n", fileName, err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("[+] %s:\n", fileName)
|
fmt.Fprintf(os.Stdout, "[+] %s:\n", fileName)
|
||||||
rest := data[:]
|
rest := data[:]
|
||||||
for {
|
for {
|
||||||
var p *pem.Block
|
var p *pem.Block
|
||||||
@@ -34,7 +33,7 @@ func main() {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Printf("\t%+v\n", cert.Subject.CommonName)
|
fmt.Fprintf(os.Stdout, "\t%+v\n", cert.Subject.CommonName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,27 +109,27 @@ func main() {
|
|||||||
|
|
||||||
for _, file := range flag.Args() {
|
for _, file := range flag.Args() {
|
||||||
renamed, err := newName(file)
|
renamed, err := newName(file)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "failed to get new file name")
|
_, _ = lib.Warn(err, "failed to get new file name")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if verbose && !printChanged {
|
if verbose && !printChanged {
|
||||||
fmt.Println(file)
|
fmt.Fprintln(os.Stdout, file)
|
||||||
}
|
}
|
||||||
|
|
||||||
if renamed != file {
|
if renamed != file {
|
||||||
if !dryRun {
|
if !dryRun {
|
||||||
err = move(renamed, file, force)
|
err = move(renamed, file, force)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "failed to rename file from %s to %s", file, renamed)
|
_, _ = lib.Warn(err, "failed to rename file from %s to %s", file, renamed)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if printChanged && !verbose {
|
if printChanged && !verbose {
|
||||||
fmt.Println(file, "->", renamed)
|
fmt.Fprintln(os.Stdout, file, "->", renamed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -66,24 +66,19 @@ func main() {
|
|||||||
for _, remote := range flag.Args() {
|
for _, remote := range flag.Args() {
|
||||||
u, err := url.Parse(remote)
|
u, err := url.Parse(remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "parsing %s", remote)
|
_, _ = lib.Warn(err, "parsing %s", remote)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
name := filepath.Base(u.Path)
|
name := filepath.Base(u.Path)
|
||||||
if name == "" {
|
if name == "" {
|
||||||
lib.Warnx("source URL doesn't appear to name a file")
|
_, _ = lib.Warnx("source URL doesn't appear to name a file")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
resp, err := http.Get(remote)
|
resp, err := http.Get(remote)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "fetching %s", remote)
|
_, _ = lib.Warn(err, "fetching %s", remote)
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
lib.Warn(err, "fetching %s", remote)
|
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math/rand"
|
"math/rand/v2"
|
||||||
"os"
|
"os"
|
||||||
"regexp"
|
"regexp"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -17,11 +17,11 @@ func rollDie(count, sides int) []int {
|
|||||||
sum := 0
|
sum := 0
|
||||||
var rolls []int
|
var rolls []int
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
roll := rand.Intn(sides) + 1
|
roll := rand.IntN(sides) + 1
|
||||||
sum += roll
|
sum += roll
|
||||||
rolls = append(rolls, roll)
|
rolls = append(rolls, roll)
|
||||||
}
|
}
|
||||||
|
|
||||||
rolls = append(rolls, sum)
|
rolls = append(rolls, sum)
|
||||||
return rolls
|
return rolls
|
||||||
|
|||||||
@@ -1,24 +1,23 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/sha1"
|
"crypto/sha1"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"crypto/x509/pkix"
|
"crypto/x509/pkix"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"os"
|
"strings"
|
||||||
"strings"
|
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/die"
|
"git.wntrmute.dev/kyle/goutils/die"
|
||||||
"git.wntrmute.dev/kyle/goutils/lib"
|
"git.wntrmute.dev/kyle/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func usage(w io.Writer) {
|
func usage(w io.Writer) {
|
||||||
@@ -40,14 +39,14 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parse(path string) (public []byte, kt, ft string) {
|
func parse(path string) (public []byte, kt, ft string) {
|
||||||
data, err := ioutil.ReadFile(path)
|
data, err := os.ReadFile(path)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|
||||||
data = bytes.TrimSpace(data)
|
data = bytes.TrimSpace(data)
|
||||||
p, rest := pem.Decode(data)
|
p, rest := pem.Decode(data)
|
||||||
if len(rest) > 0 {
|
if len(rest) > 0 {
|
||||||
lib.Warnx("trailing data in PEM file")
|
_, _ = lib.Warnx("trailing data in PEM file")
|
||||||
}
|
}
|
||||||
|
|
||||||
if p == nil {
|
if p == nil {
|
||||||
die.With("no PEM data found")
|
die.With("no PEM data found")
|
||||||
@@ -73,7 +72,7 @@ func parse(path string) (public []byte, kt, ft string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func parseKey(data []byte) (public []byte, kt string) {
|
func parseKey(data []byte) (public []byte, kt string) {
|
||||||
privInterface, err := x509.ParsePKCS8PrivateKey(data)
|
privInterface, err := x509.ParsePKCS8PrivateKey(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
privInterface, err = x509.ParsePKCS1PrivateKey(data)
|
privInterface, err = x509.ParsePKCS1PrivateKey(data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -85,12 +84,12 @@ func parseKey(data []byte) (public []byte, kt string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var priv crypto.Signer
|
var priv crypto.Signer
|
||||||
switch privInterface.(type) {
|
switch p := privInterface.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
priv = privInterface.(*rsa.PrivateKey)
|
priv = p
|
||||||
kt = "RSA"
|
kt = "RSA"
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
priv = privInterface.(*ecdsa.PrivateKey)
|
priv = p
|
||||||
kt = "ECDSA"
|
kt = "ECDSA"
|
||||||
default:
|
default:
|
||||||
die.With("unknown private key type %T", privInterface)
|
die.With("unknown private key type %T", privInterface)
|
||||||
@@ -171,10 +170,10 @@ func main() {
|
|||||||
|
|
||||||
var subPKI subjectPublicKeyInfo
|
var subPKI subjectPublicKeyInfo
|
||||||
_, err := asn1.Unmarshal(public, &subPKI)
|
_, err := asn1.Unmarshal(public, &subPKI)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "failed to get subject PKI")
|
_, _ = lib.Warn(err, "failed to get subject PKI")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes)
|
pubHash := sha1.Sum(subPKI.SubjectPublicKey.Bytes)
|
||||||
pubHashString := dumpHex(pubHash[:])
|
pubHashString := dumpHex(pubHash[:])
|
||||||
@@ -182,10 +181,10 @@ func main() {
|
|||||||
ski = pubHashString
|
ski = pubHashString
|
||||||
}
|
}
|
||||||
|
|
||||||
if shouldMatch && ski != pubHashString {
|
if shouldMatch && ski != pubHashString {
|
||||||
lib.Warnx("%s: SKI mismatch (%s != %s)",
|
_, _ = lib.Warnx("%s: SKI mismatch (%s != %s)",
|
||||||
path, ski, pubHashString)
|
path, ski, pubHashString)
|
||||||
}
|
}
|
||||||
fmt.Printf("%s %s (%s %s)\n", path, pubHashString, kt, ft)
|
fmt.Printf("%s %s (%s %s)\n", path, pubHashString, kt, ft)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,26 +3,26 @@ package main
|
|||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"net"
|
"net"
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/die"
|
"git.wntrmute.dev/kyle/goutils/die"
|
||||||
|
"git.wntrmute.dev/kyle/goutils/lib"
|
||||||
)
|
)
|
||||||
|
|
||||||
func proxy(conn net.Conn, inside string) error {
|
func proxy(conn net.Conn, inside string) error {
|
||||||
proxyConn, err := net.Dial("tcp", inside)
|
proxyConn, err := net.Dial("tcp", inside)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
defer proxyConn.Close()
|
defer proxyConn.Close()
|
||||||
defer conn.Close()
|
defer conn.Close()
|
||||||
|
|
||||||
go func() {
|
go func() {
|
||||||
io.Copy(conn, proxyConn)
|
_, _ = io.Copy(conn, proxyConn)
|
||||||
}()
|
}()
|
||||||
_, err = io.Copy(proxyConn, conn)
|
_, err = io.Copy(proxyConn, conn)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
@@ -34,13 +34,17 @@ func main() {
|
|||||||
l, err := net.Listen("tcp", "0.0.0.0:"+outside)
|
l, err := net.Listen("tcp", "0.0.0.0:"+outside)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
conn, err := l.Accept()
|
conn, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Println(err)
|
_, _ = lib.Warn(err, "accept failed")
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
go proxy(conn, "127.0.0.1:"+inside)
|
go func() {
|
||||||
}
|
if err := proxy(conn, "127.0.0.1:"+inside); err != nil {
|
||||||
|
_, _ = lib.Warn(err, "proxy error")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ import (
|
|||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
|
||||||
"net"
|
"net"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
@@ -46,18 +45,18 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
cfg.Certificates = append(cfg.Certificates, cert)
|
cfg.Certificates = append(cfg.Certificates, cert)
|
||||||
if sysRoot != "" {
|
if sysRoot != "" {
|
||||||
pemList, err := ioutil.ReadFile(sysRoot)
|
pemList, err := os.ReadFile(sysRoot)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|
||||||
roots := x509.NewCertPool()
|
roots := x509.NewCertPool()
|
||||||
if !roots.AppendCertsFromPEM(pemList) {
|
if !roots.AppendCertsFromPEM(pemList) {
|
||||||
fmt.Printf("[!] no valid roots found")
|
fmt.Printf("[!] no valid roots found")
|
||||||
roots = nil
|
roots = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
cfg.RootCAs = roots
|
cfg.RootCAs = roots
|
||||||
}
|
}
|
||||||
|
|
||||||
l, err := net.Listen("tcp", listenAddr)
|
l, err := net.Listen("tcp", listenAddr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -65,42 +64,46 @@ func main() {
|
|||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
conn, err := l.Accept()
|
conn, err := l.Accept()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println(err.Error())
|
fmt.Println(err.Error())
|
||||||
}
|
continue
|
||||||
|
}
|
||||||
raddr := conn.RemoteAddr()
|
handleConn(conn, cfg)
|
||||||
tconn := tls.Server(conn, cfg)
|
}
|
||||||
err = tconn.Handshake()
|
}
|
||||||
if err != nil {
|
|
||||||
fmt.Printf("[+] %v: failed to complete handshake: %v\n", raddr, err)
|
// handleConn performs a TLS handshake, extracts the peer chain, and writes it to a file.
|
||||||
continue
|
func handleConn(conn net.Conn, cfg *tls.Config) {
|
||||||
}
|
defer conn.Close()
|
||||||
cs := tconn.ConnectionState()
|
raddr := conn.RemoteAddr()
|
||||||
if len(cs.PeerCertificates) == 0 {
|
tconn := tls.Server(conn, cfg)
|
||||||
fmt.Printf("[+] %v: no chain presented\n", raddr)
|
if err := tconn.Handshake(); err != nil {
|
||||||
continue
|
fmt.Printf("[+] %v: failed to complete handshake: %v\n", raddr, err)
|
||||||
}
|
return
|
||||||
|
}
|
||||||
var chain []byte
|
cs := tconn.ConnectionState()
|
||||||
for _, cert := range cs.PeerCertificates {
|
if len(cs.PeerCertificates) == 0 {
|
||||||
p := &pem.Block{
|
fmt.Printf("[+] %v: no chain presented\n", raddr)
|
||||||
Type: "CERTIFICATE",
|
return
|
||||||
Bytes: cert.Raw,
|
}
|
||||||
}
|
|
||||||
chain = append(chain, pem.EncodeToMemory(p)...)
|
var chain []byte
|
||||||
}
|
for _, cert := range cs.PeerCertificates {
|
||||||
|
p := &pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw}
|
||||||
var nonce [16]byte
|
chain = append(chain, pem.EncodeToMemory(p)...)
|
||||||
_, err = rand.Read(nonce[:])
|
}
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
var nonce [16]byte
|
||||||
}
|
if _, err := rand.Read(nonce[:]); err != nil {
|
||||||
fname := fmt.Sprintf("%v-%v.pem", raddr, hex.EncodeToString(nonce[:]))
|
fmt.Printf("[+] %v: failed to generate filename nonce: %v\n", raddr, err)
|
||||||
err = ioutil.WriteFile(fname, chain, 0644)
|
return
|
||||||
die.If(err)
|
}
|
||||||
fmt.Printf("%v: [+] wrote %v.\n", raddr, fname)
|
fname := fmt.Sprintf("%v-%v.pem", raddr, hex.EncodeToString(nonce[:]))
|
||||||
}
|
if err := os.WriteFile(fname, chain, 0o644); err != nil {
|
||||||
|
fmt.Printf("[+] %v: failed to write %v: %v\n", raddr, fname, err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
fmt.Printf("%v: [+] wrote %v.\n", raddr, fname)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -57,16 +57,16 @@ func getSubjectInfoHash(cert *x509.Certificate, issuer bool) []byte {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func printDigests(paths []string, issuer bool) {
|
func printDigests(paths []string, issuer bool) {
|
||||||
for _, path := range paths {
|
for _, path := range paths {
|
||||||
cert, err := certlib.LoadCertificate(path)
|
cert, err := certlib.LoadCertificate(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
lib.Warn(err, "failed to load certificate from %s", path)
|
_, _ = lib.Warn(err, "failed to load certificate from %s", path)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
digest := getSubjectInfoHash(cert, issuer)
|
digest := getSubjectInfoHash(cert, issuer)
|
||||||
fmt.Printf("%x %s\n", digest, path)
|
fmt.Printf("%x %s\n", digest, path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func matchDigests(paths []string, issuer bool) {
|
func matchDigests(paths []string, issuer bool) {
|
||||||
@@ -87,10 +87,10 @@ func matchDigests(paths []string, issuer bool) {
|
|||||||
die.If(err)
|
die.If(err)
|
||||||
sndCert, err := certlib.LoadCertificate(snd)
|
sndCert, err := certlib.LoadCertificate(snd)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
if !bytes.Equal(getSubjectInfoHash(fstCert, issuer), getSubjectInfoHash(sndCert, issuer)) {
|
if !bytes.Equal(getSubjectInfoHash(fstCert, issuer), getSubjectInfoHash(sndCert, issuer)) {
|
||||||
lib.Warnx("certificates don't match: %s and %s", fst, snd)
|
_, _ = lib.Warnx("certificates don't match: %s and %s", fst, snd)
|
||||||
invalid++
|
invalid++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if invalid > 0 {
|
if invalid > 0 {
|
||||||
|
|||||||
@@ -1,21 +1,19 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto"
|
"crypto"
|
||||||
"crypto/ecdsa"
|
"crypto/ecdsa"
|
||||||
"crypto/elliptic"
|
"crypto/elliptic"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"crypto/x509"
|
"crypto/x509"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"errors"
|
"errors"
|
||||||
"flag"
|
"flag"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"os"
|
||||||
"log"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.wntrmute.dev/kyle/goutils/die"
|
"git.wntrmute.dev/kyle/goutils/die"
|
||||||
)
|
)
|
||||||
|
|
||||||
var validPEMs = map[string]bool{
|
var validPEMs = map[string]bool{
|
||||||
@@ -52,8 +50,75 @@ func getECCurve(pub interface{}) int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// matchRSA compares an RSA public key from certificate against RSA public key from private key.
|
||||||
|
// It returns true on match.
|
||||||
|
func matchRSA(certPub *rsa.PublicKey, keyPub *rsa.PublicKey) bool {
|
||||||
|
return keyPub.N.Cmp(certPub.N) == 0 && keyPub.E == certPub.E
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchECDSA compares ECDSA public keys for equality and compatible curve.
|
||||||
|
// It returns match=true when they are on the same curve and have the same X/Y.
|
||||||
|
// If curves mismatch, match is false.
|
||||||
|
func matchECDSA(certPub *ecdsa.PublicKey, keyPub *ecdsa.PublicKey) bool {
|
||||||
|
if getECCurve(certPub) != getECCurve(keyPub) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if keyPub.X.Cmp(certPub.X) != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if keyPub.Y.Cmp(certPub.Y) != 0 {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// matchKeys determines whether the certificate's public key matches the given private key.
|
||||||
|
// It returns true if they match; otherwise, it returns false and a human-friendly reason.
|
||||||
|
func matchKeys(cert *x509.Certificate, priv crypto.Signer) (bool, string) {
|
||||||
|
switch keyPub := priv.Public().(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
switch certPub := cert.PublicKey.(type) {
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
if matchRSA(certPub, keyPub) {
|
||||||
|
return true, ""
|
||||||
|
}
|
||||||
|
return false, "public keys don't match"
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
return false, "RSA private key, EC public key"
|
||||||
|
default:
|
||||||
|
return false, fmt.Sprintf("unsupported certificate public key type: %T", cert.PublicKey)
|
||||||
|
}
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
switch certPub := cert.PublicKey.(type) {
|
||||||
|
case *ecdsa.PublicKey:
|
||||||
|
if matchECDSA(certPub, keyPub) {
|
||||||
|
return true, ""
|
||||||
|
}
|
||||||
|
// Determine a more precise reason
|
||||||
|
kc := getECCurve(keyPub)
|
||||||
|
cc := getECCurve(certPub)
|
||||||
|
if kc == curveInvalid {
|
||||||
|
return false, "invalid private key curve"
|
||||||
|
}
|
||||||
|
if cc == curveRSA {
|
||||||
|
return false, "private key is EC, certificate is RSA"
|
||||||
|
}
|
||||||
|
if kc != cc {
|
||||||
|
return false, "EC curves don't match"
|
||||||
|
}
|
||||||
|
return false, "public keys don't match"
|
||||||
|
case *rsa.PublicKey:
|
||||||
|
return false, "private key is EC, certificate is RSA"
|
||||||
|
default:
|
||||||
|
return false, fmt.Sprintf("unsupported certificate public key type: %T", cert.PublicKey)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return false, fmt.Sprintf("unrecognised private key type: %T", priv.Public())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func loadKey(path string) (crypto.Signer, error) {
|
func loadKey(path string) (crypto.Signer, error) {
|
||||||
in, err := ioutil.ReadFile(path)
|
in, err := os.ReadFile(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -67,7 +132,7 @@ func loadKey(path string) (crypto.Signer, error) {
|
|||||||
in = p.Bytes
|
in = p.Bytes
|
||||||
}
|
}
|
||||||
|
|
||||||
priv, err := x509.ParsePKCS8PrivateKey(in)
|
priv, err := x509.ParsePKCS8PrivateKey(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
priv, err = x509.ParsePKCS1PrivateKey(in)
|
priv, err = x509.ParsePKCS1PrivateKey(in)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -78,15 +143,15 @@ func loadKey(path string) (crypto.Signer, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch priv.(type) {
|
switch p := priv.(type) {
|
||||||
case *rsa.PrivateKey:
|
case *rsa.PrivateKey:
|
||||||
return priv.(*rsa.PrivateKey), nil
|
return p, nil
|
||||||
case *ecdsa.PrivateKey:
|
case *ecdsa.PrivateKey:
|
||||||
return priv.(*ecdsa.PrivateKey), nil
|
return p, nil
|
||||||
}
|
default:
|
||||||
|
// should never reach here
|
||||||
// should never reach here
|
return nil, errors.New("invalid private key")
|
||||||
return nil, errors.New("invalid private key")
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -96,7 +161,7 @@ func main() {
|
|||||||
flag.StringVar(&certFile, "c", "", "TLS `certificate` file")
|
flag.StringVar(&certFile, "c", "", "TLS `certificate` file")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
in, err := ioutil.ReadFile(certFile)
|
in, err := os.ReadFile(certFile)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|
||||||
p, _ := pem.Decode(in)
|
p, _ := pem.Decode(in)
|
||||||
@@ -112,50 +177,11 @@ func main() {
|
|||||||
priv, err := loadKey(keyFile)
|
priv, err := loadKey(keyFile)
|
||||||
die.If(err)
|
die.If(err)
|
||||||
|
|
||||||
switch pub := priv.Public().(type) {
|
matched, reason := matchKeys(cert, priv)
|
||||||
case *rsa.PublicKey:
|
if matched {
|
||||||
switch certPub := cert.PublicKey.(type) {
|
fmt.Println("Match.")
|
||||||
case *rsa.PublicKey:
|
return
|
||||||
if pub.N.Cmp(certPub.N) != 0 || pub.E != certPub.E {
|
}
|
||||||
fmt.Println("No match (public keys don't match).")
|
fmt.Printf("No match (%s).\n", reason)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
|
||||||
fmt.Println("Match.")
|
|
||||||
return
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
fmt.Println("No match (RSA private key, EC public key).")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
case *ecdsa.PublicKey:
|
|
||||||
privCurve := getECCurve(pub)
|
|
||||||
certCurve := getECCurve(cert.PublicKey)
|
|
||||||
log.Printf("priv: %d\tcert: %d\n", privCurve, certCurve)
|
|
||||||
|
|
||||||
if certCurve == curveRSA {
|
|
||||||
fmt.Println("No match (private key is EC, certificate is RSA).")
|
|
||||||
os.Exit(1)
|
|
||||||
} else if privCurve == curveInvalid {
|
|
||||||
fmt.Println("No match (invalid private key curve).")
|
|
||||||
os.Exit(1)
|
|
||||||
} else if privCurve != certCurve {
|
|
||||||
fmt.Println("No match (EC curves don't match).")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
certPub := cert.PublicKey.(*ecdsa.PublicKey)
|
|
||||||
if pub.X.Cmp(certPub.X) != 0 {
|
|
||||||
fmt.Println("No match (public keys don't match).")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
if pub.Y.Cmp(certPub.Y) != 0 {
|
|
||||||
fmt.Println("No match (public keys don't match).")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Match.")
|
|
||||||
default:
|
|
||||||
fmt.Printf("Unrecognised private key type: %T\n", priv.Public())
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -123,13 +123,14 @@ func main() {
|
|||||||
|
|
||||||
for _, path := range pathList {
|
for _, path := range pathList {
|
||||||
if isDir(path) {
|
if isDir(path) {
|
||||||
err := filepath.Walk(path, buildWalker(search))
|
if err := filepath.Walk(path, buildWalker(search)); err != nil {
|
||||||
if err != nil {
|
|
||||||
errorf("%v", err)
|
errorf("%v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
searchFile(path, search)
|
if err := searchFile(path, search); err != nil {
|
||||||
|
errorf("%v", err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
18
lib/lib.go
18
lib/lib.go
@@ -81,12 +81,12 @@ var (
|
|||||||
|
|
||||||
// Duration returns a prettier string for time.Durations.
|
// Duration returns a prettier string for time.Durations.
|
||||||
func Duration(d time.Duration) string {
|
func Duration(d time.Duration) string {
|
||||||
var s string
|
var s string
|
||||||
if d >= yearDuration {
|
if d >= yearDuration {
|
||||||
years := d / yearDuration
|
years := int64(d / yearDuration)
|
||||||
s += fmt.Sprintf("%dy", years)
|
s += fmt.Sprintf("%dy", years)
|
||||||
d -= years * yearDuration
|
d -= time.Duration(years) * yearDuration
|
||||||
}
|
}
|
||||||
|
|
||||||
if d >= dayDuration {
|
if d >= dayDuration {
|
||||||
days := d / dayDuration
|
days := d / dayDuration
|
||||||
@@ -97,9 +97,9 @@ func Duration(d time.Duration) string {
|
|||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|
||||||
d %= 1 * time.Second
|
d %= 1 * time.Second
|
||||||
hours := d / time.Hour
|
hours := int64(d / time.Hour)
|
||||||
d -= hours * time.Hour
|
d -= time.Duration(hours) * time.Hour
|
||||||
s += fmt.Sprintf("%dh%s", hours, d)
|
s += fmt.Sprintf("%dh%s", hours, d)
|
||||||
return s
|
return s
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package logging
|
package logging
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
@@ -61,9 +62,9 @@ func NewSplitFile(outpath, errpath string, overwrite bool) (*File, error) {
|
|||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if closeErr := fl.Close(); closeErr != nil {
|
if closeErr := fl.Close(); closeErr != nil {
|
||||||
return nil, fmt.Errorf("failed to open error log: cleanup close failed: %v: %w", closeErr, err)
|
return nil, fmt.Errorf("failed to open error log: %w", errors.Join(closeErr, err))
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, fmt.Errorf("failed to open error log: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
fl.LogWriter = NewLogWriter(fl.fo, fl.fe)
|
fl.LogWriter = NewLogWriter(fl.fo, fl.fe)
|
||||||
|
|||||||
Reference in New Issue
Block a user