Skip to content

Commit

Permalink
cmd: add 'connect' command
Browse files Browse the repository at this point in the history
Issue: #199
  • Loading branch information
adamdecaf committed Jul 13, 2018
1 parent afad60f commit 7b8041e
Show file tree
Hide file tree
Showing 2 changed files with 132 additions and 11 deletions.
62 changes: 51 additions & 11 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,10 @@
package main

import (
"errors"
"flag"
"fmt"
"net/url"
"os"
"runtime"
"strings"
Expand Down Expand Up @@ -67,10 +69,11 @@ func init() {
fmt.Printf(`Usage of cert-manage version %s
SUB-COMMANDS
add Add certificate(s) to a store
Accepts: -app, -file
backup Take a backup of the specified certificate store
connect Attempt to load a remote URL with the platform (or app) store
gen-whitelist Create a whitelist from various sources
list List the currently installed and trusted certificates
Expand Down Expand Up @@ -189,8 +192,7 @@ func main() {
cert-manage add -file <path> -app <name>
APPS
Supported apps: %s`,
strings.Join(store.GetApps(), ", ")),
Supported apps: %s`, strings.Join(store.GetApps(), ", ")),
}
commands["backup"] = &command{
fn: func() error {
Expand All @@ -204,8 +206,30 @@ APPS
Backup a certificate store. This can be done for the platform or a given app.
APPS
Supported apps: %s`,
strings.Join(store.GetApps(), ", ")),
Supported apps: %s`, strings.Join(store.GetApps(), ", ")),
}
commands["connect"] = &command{
fn: func() error {
u, err := parseConnectUrl(fs)
if err != nil {
return err
}
return cmd.ConnectWithPlatformStore(u, cfg)
},
appfn: func(a string) error {
u, err := parseConnectUrl(fs)
if err != nil {
return err
}
return cmd.ConnectWithAppStore(u, *flagApp, cfg)
},
help: fmt.Sprintf(`Usage: cert-manage connect [-app <name>] <url>
Attempt an HTTP connect to <url> with the given certificate store. If -app is provided then
the certificates for that application are loaded and used for the connection.
APPS
Supported apps: %s`, strings.Join(store.GetApps(), ", ")),
}
commands["gen-whitelist"] = &command{
fn: func() error {
Expand All @@ -229,8 +253,7 @@ APPS
cert-manage gen-whitelist -from browsers -out whitelist.json
APPS
Supported apps: %s`,
strings.Join(store.GetApps(), ", ")),
Supported apps: %s`, strings.Join(store.GetApps(), ", ")),
}
commands["list"] = &command{
fn: func() error {
Expand Down Expand Up @@ -298,8 +321,7 @@ APPS
cert-manage restore -app java
APPS
Supported apps: %s`,
strings.Join(store.GetApps(), ", ")),
Supported apps: %s`, strings.Join(store.GetApps(), ", ")),
}
commands["whitelist"] = &command{
fn: func() error {
Expand All @@ -325,8 +347,7 @@ APPS
cert-manage whitelist -file whitelist.json -app java
APPS
Supported apps: %s`,
strings.Join(store.GetApps(), ", ")),
Supported apps: %s`, strings.Join(store.GetApps(), ", ")),
}
commands["version"] = &command{
fn: func() error {
Expand Down Expand Up @@ -375,3 +396,22 @@ APPS
func getVersion() string {
return fmt.Sprintf("%s (Go: %s)", Version, runtime.Version())
}

func parseConnectUrl(fs *flag.FlagSet) (*url.URL, error) {
if fs.NArg() != 1 {
return nil, fmt.Errorf("unknown arguments: %s", strings.Join(fs.Args(), ", "))
}

raw := fs.Arg(0)
if raw == "" {
return nil, errors.New("no url specified")
}
u, err := url.Parse(raw)
if err != nil {
return nil, fmt.Errorf("failed to parse %s: %v", raw, err)
}
if u.Scheme != "https" {
return nil, fmt.Errorf("non-secure url scheme used: %s", u.String())
}
return u, nil
}
81 changes: 81 additions & 0 deletions pkg/cmd/connect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
// Copyright 2018 Adam Shannon
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package cmd

import (
"crypto/x509"
"fmt"
"net/http"
"net/url"

"github.com/adamdecaf/cert-manage/pkg/httputil"
"github.com/adamdecaf/cert-manage/pkg/store"
"github.com/adamdecaf/cert-manage/pkg/ui"
)

func ConnectWithPlatformStore(uri *url.URL, cfg *ui.Config) error {
st := store.Platform()
certs, err := st.List(&store.ListOptions{
Trusted: true,
})
if err != nil {
return fmt.Errorf("problem getting certs: %v", err)
}
return connect(uri, certs)
}

func ConnectWithAppStore(uri *url.URL, app string, cfg *ui.Config) error {
st, err := store.ForApp(app)
if err != nil {
return fmt.Errorf("problem finding %s: %v", app, err)
}
certs, err := st.List(&store.ListOptions{
Trusted: true,
})
if err != nil {
return fmt.Errorf("problem getting certs for %q: %v", app, err)
}
return connect(uri, certs)
}

func connect(uri *url.URL, roots []*x509.Certificate) error {
req, err := http.NewRequest("HEAD", uri.String(), nil)
if err != nil {
return fmt.Errorf("unable to make request for %s: %v", uri.String(), err)
}
req.Close = true

pool := x509.NewCertPool()
for i := range roots {
pool.AddCert(roots[i])
}

client := httputil.New()
tr, ok := client.Transport.(*http.Transport)
if ok {
tr.TLSClientConfig.RootCAs = pool
}
client.Transport = tr

resp, err := client.Do(req)
if err != nil {
return fmt.Errorf("problem with HEAD to %s: %v", uri.String(), err)
}
if resp.Body != nil {
resp.Body.Close()
}
fmt.Printf("Connection to %s passed!\n", uri.String())
return nil
}

0 comments on commit 7b8041e

Please sign in to comment.