Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d511aeb52d | |||
| eac59fd5a6 | |||
| bd5ec3f425 | |||
| b81709cfdd | |||
| 8518cc6e56 | |||
| 0bdd30f506 |
@@ -1,5 +1,14 @@
|
|||||||
CHANGELOG
|
CHANGELOG
|
||||||
|
|
||||||
|
v1.16.0 - 2025-11-20
|
||||||
|
|
||||||
|
Added:
|
||||||
|
- msg: package for command line outputs.
|
||||||
|
|
||||||
|
Changed:
|
||||||
|
- lib: add DummyWriteCloser
|
||||||
|
- Miscellaneous linter fixes and documentation updates.
|
||||||
|
|
||||||
v1.15.8 - 2025-11-20
|
v1.15.8 - 2025-11-20
|
||||||
|
|
||||||
Changed:
|
Changed:
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ Contents:
|
|||||||
lib/ Commonly-useful functions for writing Go programs.
|
lib/ Commonly-useful functions for writing Go programs.
|
||||||
log/ A syslog library.
|
log/ A syslog library.
|
||||||
logging/ A logging library.
|
logging/ A logging library.
|
||||||
|
msg/ Output library for command line programs.
|
||||||
mwc/ MultiwriteCloser implementation.
|
mwc/ MultiwriteCloser implementation.
|
||||||
sbuf/ A byte buffer that can be wiped.
|
sbuf/ A byte buffer that can be wiped.
|
||||||
seekbuf/ A read-seekable byte buffer.
|
seekbuf/ A read-seekable byte buffer.
|
||||||
|
|||||||
@@ -179,6 +179,8 @@ func (ka KeyAlgo) String() string {
|
|||||||
return "Ed25519"
|
return "Ed25519"
|
||||||
case x509.DSA:
|
case x509.DSA:
|
||||||
return "DSA"
|
return "DSA"
|
||||||
|
case x509.UnknownPublicKeyAlgorithm:
|
||||||
|
fallthrough // make linter happy
|
||||||
default:
|
default:
|
||||||
return "unknown"
|
return "unknown"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,7 +101,7 @@ func buildExtraForPath(st unix.Stat_t, path string, setUID, setGID int) []byte {
|
|||||||
gid = uint32(setGID & 0xFFFFFFFF) //#nosec G115 - masked
|
gid = uint32(setGID & 0xFFFFFFFF) //#nosec G115 - masked
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mode := uint32(st.Mode & 0o7777)
|
mode := st.Mode & 0o7777
|
||||||
|
|
||||||
// Use portable helper to gather ctime
|
// Use portable helper to gather ctime
|
||||||
var cts int64
|
var cts int64
|
||||||
|
|||||||
@@ -22,43 +22,50 @@ import (
|
|||||||
// Fetcher is an interface for fetching certificates from a remote source. It
|
// Fetcher is an interface for fetching certificates from a remote source. It
|
||||||
// currently supports fetching from a server or a file.
|
// currently supports fetching from a server or a file.
|
||||||
type Fetcher interface {
|
type Fetcher interface {
|
||||||
|
// Get retrieves the leaf certificate from the source.
|
||||||
Get() (*x509.Certificate, error)
|
Get() (*x509.Certificate, error)
|
||||||
|
|
||||||
|
// GetChain retrieves the entire chain from the Fetcher.
|
||||||
GetChain() ([]*x509.Certificate, error)
|
GetChain() ([]*x509.Certificate, error)
|
||||||
|
|
||||||
|
// String returns a string representation of the Fetcher.
|
||||||
String() string
|
String() string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewFetcher(spec string, tcfg *tls.Config) (Fetcher, error) {
|
||||||
|
if fileutil.FileDoesExist(spec) || spec == "-" {
|
||||||
|
return NewFileFetcher(spec), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher, err := ParseServer(spec, tcfg)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
fetcher.config = tcfg
|
||||||
|
|
||||||
|
return fetcher, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerFetcher retrieves certificates from a TLS connection.
|
||||||
type ServerFetcher struct {
|
type ServerFetcher struct {
|
||||||
host string
|
host string
|
||||||
port int
|
port int
|
||||||
insecure bool
|
config *tls.Config
|
||||||
roots *x509.CertPool
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithRoots sets the roots for the ServerFetcher.
|
|
||||||
func WithRoots(roots *x509.CertPool) func(*ServerFetcher) {
|
|
||||||
return func(sf *ServerFetcher) {
|
|
||||||
sf.roots = roots
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// WithSkipVerify sets the insecure flag for the ServerFetcher.
|
|
||||||
func WithSkipVerify() func(*ServerFetcher) {
|
|
||||||
return func(sf *ServerFetcher) {
|
|
||||||
sf.insecure = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ParseServer parses a server string into a ServerFetcher. It can be a URL or a
|
// ParseServer parses a server string into a ServerFetcher. It can be a URL or a
|
||||||
// a host:port pair.
|
// a host:port pair.
|
||||||
func ParseServer(host string) (*ServerFetcher, error) {
|
func ParseServer(host string, cfg *tls.Config) (*ServerFetcher, error) {
|
||||||
target, err := hosts.ParseHost(host)
|
target, err := hosts.ParseHost(host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse server: %w", err)
|
return nil, fmt.Errorf("failed to parse server: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return &ServerFetcher{
|
return &ServerFetcher{
|
||||||
host: target.Host,
|
host: target.Host,
|
||||||
port: target.Port,
|
port: target.Port,
|
||||||
|
config: cfg,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,10 +75,7 @@ func (sf *ServerFetcher) String() string {
|
|||||||
|
|
||||||
func (sf *ServerFetcher) GetChain() ([]*x509.Certificate, error) {
|
func (sf *ServerFetcher) GetChain() ([]*x509.Certificate, error) {
|
||||||
opts := dialer.Opts{
|
opts := dialer.Opts{
|
||||||
TLSConfig: &tls.Config{
|
TLSConfig: sf.config,
|
||||||
InsecureSkipVerify: sf.insecure, // #nosec G402 - no shit sherlock
|
|
||||||
RootCAs: sf.roots,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
conn, err := dialer.DialTLS(context.Background(), net.JoinHostPort(sf.host, lib.Itoa(sf.port, -1)), opts)
|
conn, err := dialer.DialTLS(context.Background(), net.JoinHostPort(sf.host, lib.Itoa(sf.port, -1)), opts)
|
||||||
@@ -93,6 +97,7 @@ func (sf *ServerFetcher) Get() (*x509.Certificate, error) {
|
|||||||
return certs[0], nil
|
return certs[0], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FileFetcher retrieves certificates from files on disk.
|
||||||
type FileFetcher struct {
|
type FileFetcher struct {
|
||||||
path string
|
path string
|
||||||
}
|
}
|
||||||
@@ -139,20 +144,11 @@ func (ff *FileFetcher) Get() (*x509.Certificate, error) {
|
|||||||
// configuration will be used to control verification behavior (e.g.,
|
// configuration will be used to control verification behavior (e.g.,
|
||||||
// InsecureSkipVerify, RootCAs).
|
// InsecureSkipVerify, RootCAs).
|
||||||
func GetCertificateChain(spec string, cfg *tls.Config) ([]*x509.Certificate, error) {
|
func GetCertificateChain(spec string, cfg *tls.Config) ([]*x509.Certificate, error) {
|
||||||
if fileutil.FileDoesExist(spec) {
|
fetcher, err := NewFetcher(spec, cfg)
|
||||||
return NewFileFetcher(spec).GetChain()
|
|
||||||
}
|
|
||||||
|
|
||||||
fetcher, err := ParseServer(spec)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if cfg != nil {
|
|
||||||
fetcher.insecure = cfg.InsecureSkipVerify
|
|
||||||
fetcher.roots = cfg.RootCAs
|
|
||||||
}
|
|
||||||
|
|
||||||
return fetcher.GetChain()
|
return fetcher.GetChain()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
18
lib/lib.go
18
lib/lib.go
@@ -5,6 +5,7 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"strconv"
|
"strconv"
|
||||||
@@ -329,3 +330,20 @@ func HexEncode(b []byte, mode HexEncodeMode) string {
|
|||||||
panic("invalid hex encode mode")
|
panic("invalid hex encode mode")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DummyWriteCloser wraps an io.Writer in a struct with a no-op Close.
|
||||||
|
type DummyWriteCloser struct {
|
||||||
|
w io.Writer
|
||||||
|
}
|
||||||
|
|
||||||
|
func WithCloser(w io.Writer) io.WriteCloser {
|
||||||
|
return &DummyWriteCloser{w: w}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dwc *DummyWriteCloser) Write(p []byte) (int, error) {
|
||||||
|
return dwc.w.Write(p)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (dwc *DummyWriteCloser) Close() error {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|||||||
127
msg/msg.go
Normal file
127
msg/msg.go
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
// Package msg is a tool for handling commandline output based on
|
||||||
|
// flags for quiet, verbose, and debug modes. The default is to
|
||||||
|
// have all modes disabled.
|
||||||
|
//
|
||||||
|
// The QPrint messages will only output messages if quiet mode is
|
||||||
|
// disabled
|
||||||
|
// The VPrint messages will only output messages if verbose mode
|
||||||
|
// is enabled.
|
||||||
|
// The DPrint messages will only output messages if debug mode
|
||||||
|
// is enabled.
|
||||||
|
package msg
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
|
||||||
|
"git.wntrmute.dev/kyle/goutils/lib"
|
||||||
|
|
||||||
|
"git.wntrmute.dev/kyle/goutils/dbg"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
enableQuiet bool
|
||||||
|
enableVerbose bool
|
||||||
|
debug *dbg.DebugPrinter
|
||||||
|
w io.Writer
|
||||||
|
)
|
||||||
|
|
||||||
|
func SetQuiet(q bool) {
|
||||||
|
enableQuiet = q
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetVerbose(v bool) {
|
||||||
|
enableVerbose = v
|
||||||
|
}
|
||||||
|
|
||||||
|
func SetDebug(d bool) {
|
||||||
|
debug.Enabled = d
|
||||||
|
}
|
||||||
|
|
||||||
|
func Set(q, v, d bool) {
|
||||||
|
SetQuiet(q)
|
||||||
|
SetVerbose(v)
|
||||||
|
SetDebug(d)
|
||||||
|
}
|
||||||
|
|
||||||
|
func QPrint(a ...any) {
|
||||||
|
if enableQuiet {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func QPrintf(format string, a ...any) {
|
||||||
|
if enableQuiet {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func QPrintln(a ...any) {
|
||||||
|
if enableQuiet {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DPrint(a ...any) {
|
||||||
|
debug.Print(a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DPrintf(format string, a ...any) {
|
||||||
|
debug.Printf(format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func DPrintln(a ...any) {
|
||||||
|
debug.Println(a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func StackTrace() {
|
||||||
|
debug.StackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
func VPrint(a ...any) {
|
||||||
|
if !enableVerbose {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VPrintf(format string, a ...any) {
|
||||||
|
if !enableVerbose {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func VPrintln(a ...any) {
|
||||||
|
if !enableVerbose {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Fprintln(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Print(a ...any) {
|
||||||
|
fmt.Fprint(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Printf(format string, a ...any) {
|
||||||
|
fmt.Fprintf(w, format, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Println(a ...any) {
|
||||||
|
fmt.Fprintln(w, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
// SetWriter changes the output for messages.
|
||||||
|
func SetWriter(dst io.Writer) {
|
||||||
|
w = dst
|
||||||
|
debug = dbg.To(lib.WithCloser(w))
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user