commit 88ea71f172930c0445598078ddc30c34a200b430 Author: guessi Date: Sat Feb 22 23:39:16 2020 +0800 Initial commit diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..6b8710a --- /dev/null +++ b/.dockerignore @@ -0,0 +1 @@ +.git diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..66fd13c --- /dev/null +++ b/.gitignore @@ -0,0 +1,15 @@ +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..3cb8775 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM golang:1.13-alpine3.11 AS builder +RUN apk add --no-cache git +WORKDIR ${GOPATH}/src/github.com/guessi/ssl-certs-checker +COPY . . +RUN go build -o /go/bin/ssl-certs-checker + +FROM alpine:3.11 +COPY --from=builder /go/bin/ssl-certs-checker /opt/ +WORKDIR /opt/ +ENTRYPOINT ["/opt/ssl-certs-checker"] diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..a8ed630 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2020 guessi + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..6e47381 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# SSL certificate checker written in golang + + +## Setup Guide + + go get -u github.com/guessi/ssl-certs-checker + +## Examples + +install binary to your ${GOPATH} + + go install github.com/ssl-certs-checker + +check single target host certificates infomation + + ${GOPATH}/bin/ssl-certs-checker --hosts "www.google.com" + + +----------------+----------------+-------------------------------+-------------------------------+------------+ + | Host | Common Name | Not Before | Not After | Issuer | + +----------------+----------------+-------------------------------+-------------------------------+------------+ + | www.google.com | www.google.com | 2020-02-12 11:47:41 +0000 UTC | 2020-05-06 11:47:41 +0000 UTC | GTS CA 1O1 | + +----------------+----------------+-------------------------------+-------------------------------+------------+ + +check multiple target hosts' certificates at once + + ${GOPATH}/bin/ssl-certs-checker --hosts "www.google.com,www.azure.com,www.amazon.com" + + +----------------+----------------+-------------------------------+-------------------------------+-----------------------+ + | Host | Common Name | Not Before | Not After | Issuer | + +----------------+----------------+-------------------------------+-------------------------------+-----------------------+ + | www.google.com | www.google.com | 2020-02-12 11:47:41 +0000 UTC | 2020-05-06 11:47:41 +0000 UTC | GTS CA 1O1 | + | www.azure.com | *.azure.com | 2019-12-17 19:51:44 +0000 UTC | 2020-12-17 19:51:44 +0000 UTC | Microsoft IT TLS CA 4 | + | www.amazon.com | www.amazon.com | 2019-09-18 00:00:00 +0000 UTC | 2020-08-23 12:00:00 +0000 UTC | DigiCert Global CA G2 | + +----------------+----------------+-------------------------------+-------------------------------+-----------------------+ + +run with docker + + docker build -t ssl-certs-checker . + + docker run --rm -it ssl-certs-checker --hosts "www.google.com" + +# License + +[MIT LICENSE](LICENSE) diff --git a/config.go b/config.go new file mode 100644 index 0000000..86a3667 --- /dev/null +++ b/config.go @@ -0,0 +1,8 @@ +package main + +import "time" + +const ( + dialerTimeout = 10 * time.Second + protocol = "tcp" +) diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..352108f --- /dev/null +++ b/go.mod @@ -0,0 +1,12 @@ +module github.com/guessi/ssl-certs-checker + +go 1.13 + +require ( + github.com/go-openapi/strfmt v0.19.4 // indirect + github.com/jedib0t/go-pretty v4.3.0+incompatible + github.com/mattn/go-runewidth v0.0.8 // indirect + github.com/stretchr/testify v1.5.1 // indirect + github.com/urfave/cli/v2 v2.1.1 + golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..faff6f0 --- /dev/null +++ b/go.sum @@ -0,0 +1,47 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSYgptZMwQh2aRr3LuazLJIa+Pg3Kc1ylSYVY= +github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/go-openapi/errors v0.19.2 h1:a2kIyV3w+OS3S97zxUndRVD46+FhGOUBDFY7nmu4CsY= +github.com/go-openapi/errors v0.19.2/go.mod h1:qX0BLWsyaKfvhluLejVpVNwNRdXZhEbTA4kxxpKBC94= +github.com/go-openapi/strfmt v0.19.4 h1:eRvaqAhpL0IL6Trh5fDsGnGhiXndzHFuA05w6sXH6/g= +github.com/go-openapi/strfmt v0.19.4/go.mod h1:eftuHTlB/dI8Uq8JJOyRlieZf+WkkxUuk0dgdHXr2Qk= +github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/jedib0t/go-pretty v4.3.0+incompatible h1:CGs8AVhEKg/n9YbUenWmNStRW2PHJzaeDodcfvRAbIo= +github.com/jedib0t/go-pretty v4.3.0+incompatible/go.mod h1:XemHduiw8R651AF9Pt4FwCTKeG3oo7hrHJAoznj9nag= +github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0= +github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/russross/blackfriday/v2 v2.0.1 h1:lPqVAte+HuHNfhJ/0LC98ESWRz8afy9tM/0RK8m9o+Q= +github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/tidwall/pretty v1.0.0 h1:HsD+QiTn7sK6flMKIvNmpqz1qrpP3Ps6jOKIKMooyg4= +github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= +github.com/urfave/cli/v2 v2.1.1 h1:Qt8FeAtxE/vfdrLmR3rxR6JRE0RoVmbXu8+6kZtYU4k= +github.com/urfave/cli/v2 v2.1.1/go.mod h1:SE9GqnLQmjVa0iPEY0f1w3ygNIYcIJ0OKPMoW2caLfQ= +go.mongodb.org/mongo-driver v1.0.3 h1:GKoji1ld3tw2aC+GX1wbr/J2fX13yNacEYoJ8Nhr0yU= +go.mongodb.org/mongo-driver v1.0.3/go.mod h1:u7ryQJ+DOzQmeO7zB6MHyr8jkEQvC8vH7qLUO4lqsUM= +golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c h1:jceGD5YNJGgGMkJz79agzOln1K9TaZUjv5ird16qniQ= +golang.org/x/sys v0.0.0-20200219091948-cb0a6d8edb6c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= diff --git a/main.go b/main.go new file mode 100644 index 0000000..e9354a6 --- /dev/null +++ b/main.go @@ -0,0 +1,32 @@ +package main + +import ( + "os" + + "github.com/urfave/cli/v2" +) + +func main() { + app := &cli.App{ + Name: "SSL Certificate Checker", + Usage: "check SSL certificates at once", + Flags: []cli.Flag{ + &cli.StringFlag{ + Name: "hosts", + Aliases: []string{"H"}, + Value: "", + Usage: "target hosts, splits by comma", + Required: true, + }, + }, + Action: func(c *cli.Context) error { + prettyPrintCertsInfo(c.String("hosts")) + return nil + }, + } + + err := app.Run(os.Args) + if err != nil { + os.Exit(1) + } +} diff --git a/utils.go b/utils.go new file mode 100644 index 0000000..2a8bb37 --- /dev/null +++ b/utils.go @@ -0,0 +1,71 @@ +package main + +import ( + "crypto/tls" + "crypto/x509" + "net" + "os" + "strconv" + "strings" + + "github.com/jedib0t/go-pretty/table" + "github.com/jedib0t/go-pretty/text" +) + +func getPeerCertificates(h string, port int) ([]*x509.Certificate, error) { + conn, err := tls.DialWithDialer( + &net.Dialer{ + Timeout: dialerTimeout, + }, + protocol, + h+":"+strconv.Itoa(port), + &tls.Config{ + ServerName: h, + }) + if err != nil { + return nil, err + } + defer conn.Close() + + if err := conn.Handshake(); err != nil { + return nil, err + } + return conn.ConnectionState().PeerCertificates, nil +} + +func getCells(t table.Writer, h string) { + certs, err := getPeerCertificates(h, 443) + if err != nil { + return // skip if target host invalid + } + + for _, c := range certs { + if c.IsCA { + continue + } + t.AppendRows([]table.Row{ + {h, (*c).Subject.CommonName, (*c).NotBefore, (*c).NotAfter, (*c).Issuer.CommonName}, + }) + } +} + +func prettyPrintCertsInfo(h string) { + targets := strings.Split(h, ",") + + t := table.NewWriter() + t.SetOutputMirror(os.Stdout) + t.AppendHeader(table.Row{ + "Host", + "Common Name", + "Not Before", + "Not After", + "Issuer", + }) + + for _, target := range targets { + getCells(t, target) + } + + t.Style().Format.Header = text.FormatDefault + t.Render() +}