Skip to content

Commit 35de430

Browse files
fixes #811 credentials are lost when set (#812)
- adds test for id config vs cred setting
1 parent c44a363 commit 35de430

File tree

3 files changed

+233
-4
lines changed

3 files changed

+233
-4
lines changed

ziti/contexts.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,11 @@ func NewContextWithOpts(cfg *Config, options *Options) (Context, error) {
9898
newContext.maxDefaultConnections = 1
9999
}
100100

101-
idCredentials := edge_apis.NewIdentityCredentialsFromConfig(cfg.ID)
102-
idCredentials.ConfigTypes = cfg.ConfigTypes
103-
cfg.Credentials = idCredentials
101+
if cfg.Credentials == nil {
102+
idCredentials := edge_apis.NewIdentityCredentialsFromConfig(cfg.ID)
103+
idCredentials.ConfigTypes = cfg.ConfigTypes
104+
cfg.Credentials = idCredentials
105+
}
104106

105107
var apiStrs []string
106108
if len(cfg.ZtAPIs) > 0 {

ziti/contexts_test.go

Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
/*
2+
Copyright 2019 NetFoundry Inc.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
https://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package ziti
18+
19+
import (
20+
"crypto"
21+
"crypto/ecdsa"
22+
"crypto/elliptic"
23+
"crypto/rand"
24+
"crypto/x509"
25+
"crypto/x509/pkix"
26+
"encoding/pem"
27+
"math/big"
28+
"net"
29+
"testing"
30+
"time"
31+
32+
"github.com/openziti/identity"
33+
edgeapis "github.com/openziti/sdk-golang/edge-apis"
34+
"github.com/stretchr/testify/require"
35+
)
36+
37+
func Test_NewContext(t *testing.T) {
38+
39+
t.Run("creating a new context with no identity config and no credentials does not error", func(t *testing.T) {
40+
req := require.New(t)
41+
42+
cfg := &Config{
43+
ZtAPI: "https://example.com:1234",
44+
}
45+
46+
ztx, err := NewContext(cfg)
47+
req.NoError(err)
48+
req.NotNil(ztx)
49+
})
50+
51+
t.Run("creating a new context with identity config and no credentials creates identity credentials", func(t *testing.T) {
52+
req := require.New(t)
53+
54+
idTestCert, err := newTestSelfSignedCert("test123", nil, 1*time.Hour)
55+
56+
req.NoError(err)
57+
req.NotNil(idTestCert)
58+
59+
expectedCertPem := string(idTestCert.CertPEM)
60+
expectedKeyPem := string(idTestCert.KeyPEM)
61+
62+
idConfig := identity.Config{
63+
Cert: "pem:" + expectedCertPem,
64+
Key: "pem:" + expectedKeyPem,
65+
}
66+
67+
cfg := &Config{
68+
ZtAPI: "https://example.com:1234",
69+
ID: idConfig,
70+
}
71+
72+
ztx, err := NewContext(cfg)
73+
req.NoError(err)
74+
req.NotNil(ztx)
75+
76+
ztxImpl, ok := ztx.(*ContextImpl)
77+
req.True(ok)
78+
req.NotNil(ztxImpl)
79+
80+
clientIdentityCreds, ok := ztxImpl.CtrlClt.Credentials.(*edgeapis.IdentityCredentials)
81+
req.True(ok)
82+
req.NotNil(clientIdentityCreds)
83+
84+
clientIdentityTslCert := clientIdentityCreds.Identity.Cert()
85+
req.NotNil(clientIdentityTslCert)
86+
87+
req.Equal(idTestCert.Cert.Raw, clientIdentityTslCert.Certificate[0])
88+
req.Equal(idTestCert.PrivateKey, clientIdentityTslCert.PrivateKey)
89+
})
90+
91+
t.Run("creating a new context with credentials uses credentials", func(t *testing.T) {
92+
req := require.New(t)
93+
94+
certCredentialsCert, err := newTestSelfSignedCert("testCert", nil, 1*time.Hour)
95+
req.NoError(err)
96+
req.NotNil(certCredentialsCert)
97+
98+
certPool := x509.NewCertPool()
99+
certCreds := edgeapis.NewCertCredentials([]*x509.Certificate{certCredentialsCert.Cert}, certCredentialsCert.PrivateKey)
100+
certCreds.CaPool = certPool
101+
102+
cfg := &Config{
103+
ZtAPI: "https://example.com:1234",
104+
Credentials: certCreds,
105+
}
106+
107+
ztx, err := NewContext(cfg)
108+
req.NoError(err)
109+
req.NotNil(ztx)
110+
111+
ztxImpl, ok := ztx.(*ContextImpl)
112+
req.True(ok)
113+
req.NotNil(ztxImpl)
114+
115+
req.Equal(certCreds, ztxImpl.CtrlClt.Credentials)
116+
req.Equal(certPool, ztxImpl.CtrlClt.CaPool)
117+
})
118+
119+
t.Run("creating a new context with identity config and credentials, uses credentials", func(t *testing.T) {
120+
req := require.New(t)
121+
122+
idConfigCert, err := newTestSelfSignedCert("testIdConfigCert", nil, 1*time.Hour)
123+
req.NoError(err)
124+
req.NotNil(idConfigCert)
125+
126+
idConfigCertPem := string(idConfigCert.CertPEM)
127+
idConfigKeyPem := string(idConfigCert.KeyPEM)
128+
129+
idConfig := identity.Config{
130+
Cert: "pem:" + idConfigCertPem,
131+
Key: "pem:" + idConfigKeyPem,
132+
}
133+
134+
certCredentialsCert, err := newTestSelfSignedCert("testCertCredentialsCert", nil, 1*time.Hour)
135+
req.NoError(err)
136+
req.NotNil(certCredentialsCert)
137+
138+
certPool := x509.NewCertPool()
139+
certCreds := edgeapis.NewCertCredentials([]*x509.Certificate{certCredentialsCert.Cert}, certCredentialsCert.PrivateKey)
140+
certCreds.CaPool = certPool
141+
142+
cfg := &Config{
143+
ZtAPI: "https://example.com:1234",
144+
Credentials: certCreds,
145+
ID: idConfig,
146+
}
147+
148+
ztx, err := NewContext(cfg)
149+
req.NoError(err)
150+
req.NotNil(ztx)
151+
152+
ztxImpl, ok := ztx.(*ContextImpl)
153+
req.True(ok)
154+
req.NotNil(ztxImpl)
155+
156+
req.Equal(certCreds, ztxImpl.CtrlClt.Credentials)
157+
req.Equal(certPool, ztxImpl.CtrlClt.CaPool)
158+
})
159+
}
160+
161+
type testCert struct {
162+
Cert *x509.Certificate
163+
PrivateKey crypto.PrivateKey
164+
CertPEM []byte
165+
KeyPEM []byte
166+
}
167+
168+
func newTestSelfSignedCert(commonName string, hosts []string, ttl time.Duration) (*testCert, error) {
169+
priv, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader)
170+
if err != nil {
171+
return nil, err
172+
}
173+
174+
serialLimit := new(big.Int).Lsh(big.NewInt(1), 128)
175+
serial, err := rand.Int(rand.Reader, serialLimit)
176+
if err != nil {
177+
return nil, err
178+
}
179+
180+
var dnsNames []string
181+
var ipAddrs []net.IP
182+
for _, h := range hosts {
183+
if ip := net.ParseIP(h); ip != nil {
184+
ipAddrs = append(ipAddrs, ip)
185+
} else if h != "" {
186+
dnsNames = append(dnsNames, h)
187+
}
188+
}
189+
190+
notBefore := time.Now().Add(-1 * time.Minute)
191+
notAfter := notBefore.Add(ttl)
192+
tpl := &x509.Certificate{
193+
SerialNumber: serial,
194+
Subject: pkix.Name{CommonName: commonName},
195+
NotBefore: notBefore,
196+
NotAfter: notAfter,
197+
KeyUsage: x509.KeyUsageDigitalSignature | x509.KeyUsageKeyEncipherment,
198+
ExtKeyUsage: []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth, x509.ExtKeyUsageClientAuth},
199+
DNSNames: dnsNames,
200+
IPAddresses: ipAddrs,
201+
}
202+
203+
der, err := x509.CreateCertificate(rand.Reader, tpl, tpl, &priv.PublicKey, priv)
204+
if err != nil {
205+
return nil, err
206+
}
207+
208+
cert, err := x509.ParseCertificate(der)
209+
if err != nil {
210+
return nil, err
211+
}
212+
213+
certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: der})
214+
215+
keyBytes, err := x509.MarshalECPrivateKey(priv)
216+
if err != nil {
217+
return nil, err
218+
}
219+
keyPEM := pem.EncodeToMemory(&pem.Block{Type: "EC PRIVATE KEY", Bytes: keyBytes})
220+
221+
return &testCert{
222+
Cert: cert,
223+
PrivateKey: priv,
224+
CertPEM: certPEM,
225+
KeyPEM: keyPEM,
226+
}, nil
227+
}

ziti/sdkinfo/build_info.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)