File: client.go

package info (click to toggle)
golang-github-smallstep-certificates 0.20.0-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 23,144 kB
  • sloc: sh: 278; makefile: 170
file content (101 lines) | stat: -rw-r--r-- 2,474 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
package identity

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
	"os"

	"github.com/pkg/errors"
)

// Client wraps http.Client with a transport using the step root and identity.
type Client struct {
	CaURL *url.URL
	*http.Client
}

// ResolveReference resolves the given reference from the CaURL.
func (c *Client) ResolveReference(ref *url.URL) *url.URL {
	return c.CaURL.ResolveReference(ref)
}

// LoadClient configures an http.Client with the root in
// $STEPPATH/config/defaults.json and the identity defined in
// $STEPPATH/config/identity.json
func LoadClient() (*Client, error) {
	defaultsFile := DefaultsFile()
	b, err := os.ReadFile(defaultsFile)
	if err != nil {
		return nil, errors.Wrapf(err, "error reading %s", defaultsFile)
	}

	var defaults defaultsConfig
	if err := json.Unmarshal(b, &defaults); err != nil {
		return nil, errors.Wrapf(err, "error unmarshaling %s", defaultsFile)
	}
	if err := defaults.Validate(); err != nil {
		return nil, errors.Wrapf(err, "error validating %s", defaultsFile)
	}
	caURL, err := url.Parse(defaults.CaURL)
	if err != nil {
		return nil, errors.Wrapf(err, "error validating %s", defaultsFile)
	}
	if caURL.Scheme == "" {
		caURL.Scheme = "https"
	}

	identity, err := LoadDefaultIdentity()
	if err != nil {
		return nil, err
	}
	if err := identity.Validate(); err != nil {
		return nil, errors.Wrapf(err, "error validating %s", IdentityFile())
	}
	if kind := identity.Kind(); kind != MutualTLS {
		return nil, errors.Errorf("unsupported identity %s: only mTLS is currently supported", kind)
	}

	// Prepare transport with information in defaults.json and identity.json
	tr := http.DefaultTransport.(*http.Transport).Clone()
	tr.TLSClientConfig = &tls.Config{
		GetClientCertificate: identity.GetClientCertificateFunc(),
	}

	// RootCAs
	b, err = os.ReadFile(defaults.Root)
	if err != nil {
		return nil, errors.Wrapf(err, "error loading %s", defaults.Root)
	}
	pool := x509.NewCertPool()
	if pool.AppendCertsFromPEM(b) {
		tr.TLSClientConfig.RootCAs = pool
	}

	return &Client{
		CaURL: caURL,
		Client: &http.Client{
			Transport: tr,
		},
	}, nil

}

type defaultsConfig struct {
	CaURL string `json:"ca-url"`
	Root  string `json:"root"`
}

func (c *defaultsConfig) Validate() error {
	switch {
	case c.CaURL == "":
		return fmt.Errorf("missing or invalid `ca-url` property")
	case c.Root == "":
		return fmt.Errorf("missing or invalid `root` property")
	default:
		return nil
	}
}