File: x509.go

package info (click to toggle)
golang-github-juju-utils 0.0~git20200923.4646bfe-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,324 kB
  • sloc: makefile: 37
file content (198 lines) | stat: -rw-r--r-- 4,800 bytes parent folder | download
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
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
// Copyright 2016 Canonical ltd.
// Copyright 2016 Cloudbase solutions
// Licensed under the LGPLv3, see licence file for details.

package winrm

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"sync"
	"time"

	"github.com/juju/errors"
	"github.com/juju/utils/v2"
	"github.com/juju/utils/v2/cert"
)

// List of exported internal errors
var (
	errNoClientCert       = errors.New("No client winrm cert inside dir")
	errNoClientPrivateKey = errors.New("No client private key inside dir")
	errNoX509Folder       = errors.New("No configuration x509 folder is present in this location")
)

// X509 type that defines windows remote manager
// credentials for client-server secure communication
type X509 struct {
	mu sync.Mutex

	cert   []byte // client cert
	key    []byte // client private key
	cacert []byte // ca server cert

}

// NewX509 returns a new to an empty X509
func NewX509() *X509 {
	return &X509{}
}

// LoadClientCert generates client cert for x509 authentication
// if the directory files are not already there , if they are already there
// it will load them into memory
func (x *X509) LoadClientCert(certFile, keyFile string) error {
	x.mu.Lock()
	defer x.mu.Unlock()

	b1, key := filepath.Split(keyFile)
	b2, cert := filepath.Split(certFile)
	if strings.Compare(b1, b2) != 0 {
		return fmt.Errorf("Cert and Key base paths dosen't match")
	}

	base, err := utils.NormalizePath(b1)
	if err != nil {
		return err
	}
	logger.Debugf("Init winrm credentials path for the module %s", base)
	logger.Debugf("Init winrm path key %s", keyFile)
	logger.Debugf("Init winrm path cert %s", certFile)

	if err = x.read(base, key, cert); err != nil &&
		err != errNoClientCert &&
		err != errNoX509Folder &&
		err != errNoClientPrivateKey {
		return err
	}

	if err == errNoClientCert ||
		err == errNoX509Folder ||
		err == errNoClientPrivateKey {
		if err = os.RemoveAll(base); err != nil {
			return err
		}
	}

	if err := os.MkdirAll(base, 0700); err != nil {
		return err
	}
	if err = x.write(base, key, cert); err != nil {
		return err
	}

	return nil
}

// LoadCACert reads ca cert into memory
func (x *X509) LoadCACert(path string) error {
	var err error
	x.cacert, err = ioutil.ReadFile(path)
	if err != nil {
		return fmt.Errorf("No CA detected in the path %s, this defaults to use insecure option for https", path)
	}
	return nil
}

// write generates the client cert/key pair and writes them on disk
func (x *X509) write(base, key, cert string) error {

	if x.key != nil && x.cert != nil {
		return nil
	}

	var err error
	logger.Debugf("Generating winrm cert and private key")
	x.cert, x.key, err = newCredentials()
	if err != nil {
		return err
	}

	logger.Debugf("Writing newly generated winrm cert and private key")
	key, cert = filepath.Join(base, key), filepath.Join(base, cert)
	if err = ioutil.WriteFile(key, x.key, 0644); err != nil {
		return err
	}

	if err = ioutil.WriteFile(cert, x.cert, 0644); err != nil {
		return err
	}

	return nil
}

// read reads from disk client cert,key pair
func (x *X509) read(base, keyFile, certFile string) error {
	keyFile = filepath.Join(base, keyFile)
	certFile = filepath.Join(base, certFile)

	var err error
	if err = confExists(base, keyFile, certFile); err != nil {
		return err
	}

	logger.Debugf("Reading winrm private key and cert")
	if x.cert, err = ioutil.ReadFile(certFile); err != nil {
		return err
	}

	if x.key, err = ioutil.ReadFile(keyFile); err != nil {
		return err
	}

	return nil
}

// confExists checks whenever the conf folder and files already exists or not.
func confExists(base, key, cert string) error {
	if _, err := os.Stat(base); os.IsNotExist(err) {
		return errNoX509Folder
	}

	if _, err := os.Stat(cert); os.IsNotExist(err) {
		return errNoClientCert
	}

	if _, err := os.Stat(key); os.IsNotExist(err) {
		return errNoClientPrivateKey
	}

	return nil
}

// newCredentials makes winrm RSA Cert and Key
// with default configuration for winrm juju connections
func newCredentials() ([]byte, []byte, error) {
	now := time.Now()
	expiry := now.AddDate(10, 0, 0) // 10 years is enough
	cert, key, err := cert.NewClientCert(
		fmt.Sprintf("juju-generated client cert for model %s", "Administrator"),
		"", expiry, 2048)
	return []byte(cert), []byte(key), err
}

// Reset fills up the internal state with nil values
func (x *X509) Reset() {
	x.key = nil
	x.cert = nil
	x.cacert = nil
	logger.Debugf("Reseting the internal winrm cert and key state")
}

// ClientCert returns the internal credential client x509 cert
func (x *X509) ClientCert() []byte {
	return x.cert
}

// ClientKey returns the internal credential client x509 private key
func (x *X509) ClientKey() []byte {
	return x.key
}

// CACert returns the internal credential client ca cert
func (x *X509) CACert() []byte {
	return x.cacert
}