From ffd20492d19f547de3456249ed374ba752c2e1ab Mon Sep 17 00:00:00 2001 From: Franck Cuny Date: Mon, 22 Jan 2024 08:07:58 -0800 Subject: build all the binaries using a Makefile Add a Makefile to build the local binaries. Rename all the commands without a dash. We can build the commands with `make all` or by being explicit, for example `make bin/x509-info`. Add a common package to keep track of build information (commit and build date) so we can reuse the same pattern across all the commands. --- .gitignore | 1 + Makefile | 24 ++++++++ cmd/flake-info/main.go | 43 ------------- cmd/flakeinfo/main.go | 43 +++++++++++++ cmd/x509-info/README.md | 54 ----------------- cmd/x509-info/main.go | 153 ----------------------------------------------- cmd/x509info/README.md | 54 +++++++++++++++++ cmd/x509info/main.go | 150 ++++++++++++++++++++++++++++++++++++++++++++++ internal/version/main.go | 12 ++++ 9 files changed, 284 insertions(+), 250 deletions(-) create mode 100644 Makefile delete mode 100644 cmd/flake-info/main.go create mode 100644 cmd/flakeinfo/main.go delete mode 100644 cmd/x509-info/README.md delete mode 100644 cmd/x509-info/main.go create mode 100644 cmd/x509info/README.md create mode 100644 cmd/x509info/main.go create mode 100644 internal/version/main.go diff --git a/.gitignore b/.gitignore index 3eceeeb..14d4519 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,4 @@ /.pre-commit-config.yaml **/target/ /*.qcow2 +bin/ diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..9e28dc2 --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +.PHONY: all build-binaries clean + +all: build-binaries + +BUILD_DIR=bin + +VERSION=$(shell git describe --tag --always --dirty) +BUILD_DATE ?= $(shell TZ=UTC0 date +%Y-%m-%dT%H:%M:%SZ) + +PKG:=github.com/fcuny/world + +BINARIES = bin/x509-info bin/flake-info + +ALL_BINARIES = $(foreach binary, $(BINARIES), ./$(binary)) + +bin/%: + go build -o $@ \ + -ldflags "-X $(PKG)/internal/version.Version=${VERSION} -X $(PKG)/internal/version.BuildDate=${BUILD_DATE}" \ + -trimpath ./cmd/$(subst -,,$*) + +build-binaries: $(ALL_BINARIES) + +clean: + rm -rf bin/ diff --git a/cmd/flake-info/main.go b/cmd/flake-info/main.go deleted file mode 100644 index d41f321..0000000 --- a/cmd/flake-info/main.go +++ /dev/null @@ -1,43 +0,0 @@ -package main - -import ( - "errors" - "flag" - "fmt" - "os" - "time" - - "github.com/fcuny/world/pkg/flake/lock" -) - -func main() { - var flakeLockPath string - - flag.StringVar(&flakeLockPath, "flake-lock", "flake.lock", "path to the flake lock file") - - flag.Parse() - - if _, err := os.Stat(flakeLockPath); err != nil { - if errors.Is(err, os.ErrNotExist) { - fmt.Fprintf(os.Stderr, "%s does not exists\n", flakeLockPath) - } else { - fmt.Fprintf(os.Stderr, "failed to check if %s exists: %v\n", flakeLockPath, err) - } - os.Exit(1) - } - - lock, err := lock.New(flakeLockPath) - if err != nil { - fmt.Fprintf(os.Stderr, "failed to parse the lockfile for %s: %+v\n", flakeLockPath, err) - os.Exit(1) - } - - fmt.Printf("%s info:\n", flakeLockPath) - fmt.Printf("version: %d\n", lock.Version) - fmt.Printf("all nodes:\n") - for nodeName, node := range lock.Nodes { - date := time.Unix(node.Locked.LastModified, 0) - unitTimeInRFC3339 := date.Format(time.RFC3339) - fmt.Printf("- %s was updated on %s\n", nodeName, unitTimeInRFC3339) - } -} diff --git a/cmd/flakeinfo/main.go b/cmd/flakeinfo/main.go new file mode 100644 index 0000000..d41f321 --- /dev/null +++ b/cmd/flakeinfo/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "errors" + "flag" + "fmt" + "os" + "time" + + "github.com/fcuny/world/pkg/flake/lock" +) + +func main() { + var flakeLockPath string + + flag.StringVar(&flakeLockPath, "flake-lock", "flake.lock", "path to the flake lock file") + + flag.Parse() + + if _, err := os.Stat(flakeLockPath); err != nil { + if errors.Is(err, os.ErrNotExist) { + fmt.Fprintf(os.Stderr, "%s does not exists\n", flakeLockPath) + } else { + fmt.Fprintf(os.Stderr, "failed to check if %s exists: %v\n", flakeLockPath, err) + } + os.Exit(1) + } + + lock, err := lock.New(flakeLockPath) + if err != nil { + fmt.Fprintf(os.Stderr, "failed to parse the lockfile for %s: %+v\n", flakeLockPath, err) + os.Exit(1) + } + + fmt.Printf("%s info:\n", flakeLockPath) + fmt.Printf("version: %d\n", lock.Version) + fmt.Printf("all nodes:\n") + for nodeName, node := range lock.Nodes { + date := time.Unix(node.Locked.LastModified, 0) + unitTimeInRFC3339 := date.Format(time.RFC3339) + fmt.Printf("- %s was updated on %s\n", nodeName, unitTimeInRFC3339) + } +} diff --git a/cmd/x509-info/README.md b/cmd/x509-info/README.md deleted file mode 100644 index 479771c..0000000 --- a/cmd/x509-info/README.md +++ /dev/null @@ -1,54 +0,0 @@ -# x509-info - -At this point it's pretty clear that I'll never remember the syntax for `openssl` to show various information about a certificate. At last I will not have to google for that syntax ever again. - -## Usage - -``` shell -Usage: - x509-info [DOMAIN] - x509-info (-f long) [DOMAIN] - -Options: - -f, --format Format the result. Valid values: short, long. Default: short - -i, --insecure Skip the TLS validation. Default: false - -p, --port Specify the port. Default: 443 - -v, --version Print version information - -h, --help Print this message -``` - -The default format will print a short message: -``` shell -$ ./bin/x509-info github.com -github.com, valid until Thu, 14 Mar 2024 23:59:59 UTC (86 days left) -``` - -It's possible to get more details: -``` shell -$ ./bin/x509-info -f long github.com -certificate - version: 3 - serial: 17034156255497985825694118641198758684 - subject: github.com - issuer: DigiCert TLS Hybrid ECC SHA384 2020 CA1 - -validity: - not before: Tue, 14 Feb 2023 00:00:00 UTC - not after: Thu, 14 Mar 2024 23:59:59 UTC - validity days: 394 - remaining days: 86 - -SANs: - • github.com - • www.github.com -``` - -You can also check expired certificates: -``` shell -$ ./bin/x509-info -i expired.badssl.com -*.badssl.com, not valid since Sun, 12 Apr 2015 23:59:59 UTC (expired 3172 days ago) -``` - -## Notes - -Could the same be achieved with a wrapper around `openssl` ? yes. diff --git a/cmd/x509-info/main.go b/cmd/x509-info/main.go deleted file mode 100644 index 65ac548..0000000 --- a/cmd/x509-info/main.go +++ /dev/null @@ -1,153 +0,0 @@ -package main - -import ( - "crypto/tls" - "crypto/x509" - "flag" - "fmt" - "html/template" - "os" - "time" -) - -const usage = `Usage: - x509-info [DOMAIN] - x509-info (-f long) [DOMAIN] - -Options: - -f, --format Format the result. Valid values: short, long. Default: short - -i, --insecure Skip the TLS validation. Default: false - -p, --port Specify the port. Default: 443 - -v, --version Print version information - -h, --help Print this message -` - -var Version, BuildDate string - -func main() { - flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) } - - var ( - portFlag int - outputFormatFlag string - insecureFlag bool - versionFlag bool - ) - - flag.IntVar(&portFlag, "port", 443, "Port to check") - flag.IntVar(&portFlag, "p", 443, "Port to check") - flag.StringVar(&outputFormatFlag, "format", "short", "Format the output") - flag.StringVar(&outputFormatFlag, "f", "short", "Format the output") - flag.BoolVar(&insecureFlag, "insecure", false, "Whether to bypass secure flag checks") - flag.BoolVar(&insecureFlag, "i", false, "Whether to bypass secure flag checks") - flag.BoolVar(&versionFlag, "version", false, "Print version information") - flag.BoolVar(&versionFlag, "v", false, "Print version information") - - flag.Parse() - - if versionFlag { - if Version != "" { - fmt.Printf("version: %s, build on: %s\n", Version, BuildDate) - return - } - fmt.Println("(unknown)") - return - } - - if flag.NArg() != 1 { - fmt.Fprintf(os.Stderr, "too many arguments: got %d, expected 1\n", flag.NArg()) - flag.Usage() - os.Exit(1) - } - - domain := flag.Arg(0) - - certs, err := getCertificates(domain, portFlag, insecureFlag) - if err != nil { - fmt.Fprintf(os.Stderr, "error: %v\n", err) - os.Exit(1) - } - - switch outputFormatFlag { - case "long": - printLong(certs) - default: - printShort(certs) - } -} - -func getCertificates(domain string, port int, insecureSkipVerify bool) ([]*x509.Certificate, error) { - conf := &tls.Config{ - InsecureSkipVerify: insecureSkipVerify, - } - - remote := fmt.Sprintf("%s:%d", domain, port) - - conn, err := tls.Dial("tcp", remote, conf) - if err != nil { - return nil, fmt.Errorf("failed to get the certificate for %s: %v", remote, err) - } - - defer conn.Close() - - certs := conn.ConnectionState().PeerCertificates - return certs, nil -} - -func printShort(certs []*x509.Certificate) { - cert := certs[0] - - now := time.Now() - remainingDays := cert.NotAfter.Sub(now) - - if remainingDays > 0 { - fmt.Printf("%s, valid until %s (%d days left)\n", cert.Subject.CommonName, cert.NotAfter.Format(time.RFC1123), int(remainingDays.Hours()/24)) - } else { - fmt.Printf("%s, not valid since %s (expired %d days ago)\n", cert.Subject.CommonName, cert.NotAfter.Format(time.RFC1123), int(remainingDays.Abs().Hours()/24)) - } -} - -const tmplLong = `certificate - version: {{ .Version }} - serial: {{ .SerialNumber }} - subject: {{ .Subject.CommonName }} - issuer: {{ .Issuer.CommonName }} - -validity: - not before: {{ rfc1123 .NotBefore }} - not after: {{ rfc1123 .NotAfter }} - validity days: {{ validFor .NotBefore .NotAfter }} - remaining days: {{ remainingDays .NotAfter }} - -SANs: -{{- range $i, $name := .DNSNames }} - • {{ $name }} -{{- end }} -` - -func printLong(certs []*x509.Certificate) { - funcMap := template.FuncMap{ - "validFor": func(before, after time.Time) int { - validForDays := after.Sub(before) - return int(validForDays.Hours() / 24) - }, - "remainingDays": func(notAfter time.Time) int { - now := time.Now() - remainingDays := notAfter.Sub(now) - return int(remainingDays.Hours() / 24) - }, - "rfc1123": func(date time.Time) string { - return date.Format(time.RFC1123) - }, - } - - tmpl, err := template.New("tmpl").Funcs(funcMap).Parse(tmplLong) - if err != nil { - panic(err) - } - - err = tmpl.Execute(os.Stdout, certs[0]) - if err != nil { - panic(err) - } -} diff --git a/cmd/x509info/README.md b/cmd/x509info/README.md new file mode 100644 index 0000000..479771c --- /dev/null +++ b/cmd/x509info/README.md @@ -0,0 +1,54 @@ +# x509-info + +At this point it's pretty clear that I'll never remember the syntax for `openssl` to show various information about a certificate. At last I will not have to google for that syntax ever again. + +## Usage + +``` shell +Usage: + x509-info [DOMAIN] + x509-info (-f long) [DOMAIN] + +Options: + -f, --format Format the result. Valid values: short, long. Default: short + -i, --insecure Skip the TLS validation. Default: false + -p, --port Specify the port. Default: 443 + -v, --version Print version information + -h, --help Print this message +``` + +The default format will print a short message: +``` shell +$ ./bin/x509-info github.com +github.com, valid until Thu, 14 Mar 2024 23:59:59 UTC (86 days left) +``` + +It's possible to get more details: +``` shell +$ ./bin/x509-info -f long github.com +certificate + version: 3 + serial: 17034156255497985825694118641198758684 + subject: github.com + issuer: DigiCert TLS Hybrid ECC SHA384 2020 CA1 + +validity: + not before: Tue, 14 Feb 2023 00:00:00 UTC + not after: Thu, 14 Mar 2024 23:59:59 UTC + validity days: 394 + remaining days: 86 + +SANs: + • github.com + • www.github.com +``` + +You can also check expired certificates: +``` shell +$ ./bin/x509-info -i expired.badssl.com +*.badssl.com, not valid since Sun, 12 Apr 2015 23:59:59 UTC (expired 3172 days ago) +``` + +## Notes + +Could the same be achieved with a wrapper around `openssl` ? yes. diff --git a/cmd/x509info/main.go b/cmd/x509info/main.go new file mode 100644 index 0000000..c425c45 --- /dev/null +++ b/cmd/x509info/main.go @@ -0,0 +1,150 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + "flag" + "fmt" + "html/template" + "os" + "time" + + "github.com/fcuny/world/internal/version" +) + +const usage = `Usage: + x509-info [DOMAIN] + x509-info (-f long) [DOMAIN] + +Options: + -f, --format Format the result. Valid values: short, long. Default: short + -i, --insecure Skip the TLS validation. Default: false + -p, --port Specify the port. Default: 443 + -v, --version Print version information + -h, --help Print this message +` + +func main() { + flag.Usage = func() { fmt.Fprintf(os.Stderr, "%s\n", usage) } + + var ( + portFlag int + outputFormatFlag string + insecureFlag bool + versionFlag bool + ) + + flag.IntVar(&portFlag, "port", 443, "Port to check") + flag.IntVar(&portFlag, "p", 443, "Port to check") + flag.StringVar(&outputFormatFlag, "format", "short", "Format the output") + flag.StringVar(&outputFormatFlag, "f", "short", "Format the output") + flag.BoolVar(&insecureFlag, "insecure", false, "Whether to bypass secure flag checks") + flag.BoolVar(&insecureFlag, "i", false, "Whether to bypass secure flag checks") + flag.BoolVar(&versionFlag, "version", false, "Print version information") + flag.BoolVar(&versionFlag, "v", false, "Print version information") + + flag.Parse() + + if versionFlag { + information := version.VersionAndBuildInfo() + fmt.Println(information) + return + } + + if flag.NArg() != 1 { + fmt.Fprintf(os.Stderr, "too many arguments: got %d, expected 1\n", flag.NArg()) + flag.Usage() + os.Exit(1) + } + + domain := flag.Arg(0) + + certs, err := getCertificates(domain, portFlag, insecureFlag) + if err != nil { + fmt.Fprintf(os.Stderr, "error: %v\n", err) + os.Exit(1) + } + + switch outputFormatFlag { + case "long": + printLong(certs) + default: + printShort(certs) + } +} + +func getCertificates(domain string, port int, insecureSkipVerify bool) ([]*x509.Certificate, error) { + conf := &tls.Config{ + InsecureSkipVerify: insecureSkipVerify, + } + + remote := fmt.Sprintf("%s:%d", domain, port) + + conn, err := tls.Dial("tcp", remote, conf) + if err != nil { + return nil, fmt.Errorf("failed to get the certificate for %s: %v", remote, err) + } + + defer conn.Close() + + certs := conn.ConnectionState().PeerCertificates + return certs, nil +} + +func printShort(certs []*x509.Certificate) { + cert := certs[0] + + now := time.Now() + remainingDays := cert.NotAfter.Sub(now) + + if remainingDays > 0 { + fmt.Printf("%s, valid until %s (%d days left)\n", cert.Subject.CommonName, cert.NotAfter.Format(time.RFC1123), int(remainingDays.Hours()/24)) + } else { + fmt.Printf("%s, not valid since %s (expired %d days ago)\n", cert.Subject.CommonName, cert.NotAfter.Format(time.RFC1123), int(remainingDays.Abs().Hours()/24)) + } +} + +const tmplLong = `certificate + version: {{ .Version }} + serial: {{ .SerialNumber }} + subject: {{ .Subject.CommonName }} + issuer: {{ .Issuer.CommonName }} + +validity: + not before: {{ rfc1123 .NotBefore }} + not after: {{ rfc1123 .NotAfter }} + validity days: {{ validFor .NotBefore .NotAfter }} + remaining days: {{ remainingDays .NotAfter }} + +SANs: +{{- range $i, $name := .DNSNames }} + • {{ $name }} +{{- end }} +` + +func printLong(certs []*x509.Certificate) { + funcMap := template.FuncMap{ + "validFor": func(before, after time.Time) int { + validForDays := after.Sub(before) + return int(validForDays.Hours() / 24) + }, + "remainingDays": func(notAfter time.Time) int { + now := time.Now() + remainingDays := notAfter.Sub(now) + return int(remainingDays.Hours() / 24) + }, + "rfc1123": func(date time.Time) string { + return date.Format(time.RFC1123) + }, + } + + tmpl, err := template.New("tmpl").Funcs(funcMap).Parse(tmplLong) + if err != nil { + panic(err) + } + + err = tmpl.Execute(os.Stdout, certs[0]) + if err != nil { + panic(err) + } +} diff --git a/internal/version/main.go b/internal/version/main.go new file mode 100644 index 0000000..d8a745f --- /dev/null +++ b/internal/version/main.go @@ -0,0 +1,12 @@ +package version + +import "fmt" + +var Version, BuildDate string + +func VersionAndBuildInfo() string { + if Version != "" { + return fmt.Sprintf("version: %s, build on: %s", Version, BuildDate) + } + return "(unknown)" +} -- cgit v1.2.3