Skip to content

Commit 0fdfa5a

Browse files
committed
enable 2 way ssl
1 parent 47fd778 commit 0fdfa5a

12 files changed

+302
-205
lines changed

config.go

+8-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,18 @@
11
package infa_auth
22

3+
import "net/http"
4+
35
// Config has the configuration for the INFA AUTH Authenticator extension.
46
type Config struct {
57
TimeOut int `mapstructure:"time_out"`
68
ClientSideSsl bool `mapstructure:"client_side_ssl"`
79
ValidationURL string `mapstructure:"validation_url"`
810
Headerkey string `mapstructure:"header_key"`
9-
ClientCertPath string `mapstructure:"client_cert_path"`
10-
CACertPath string `mapstructure:"ca_cert_path"`
11+
ClientJksPath string `mapstructure:"client_jks_path"`
12+
ClientJksPassword string `mapstructure:"client_jks_password"`
13+
CAJksPath string `mapstructure:"ca_jks_path"`
14+
CAJksPassword string `mapstructure:"ca_jks_password"`
1115
InsecureSkipVerify bool `mapstructure:"insecure_skip_verify"`
12-
ClientKeyPath string `mapstructure:"client_key_path"`
1316
}
17+
18+
var sessionServiceClient *http.Client

config_test.go

+6-5
Original file line numberDiff line numberDiff line change
@@ -26,14 +26,15 @@ func TestLoadConfig(t *testing.T) {
2626
var tt = Aaa{
2727
id: component.NewID(metadata.Type),
2828
expected: &Config{
29-
TimeOut: 2,
29+
TimeOut: 1,
3030
ClientSideSsl: true,
3131
ValidationURL: "https://pod.ics.dev:444/session-service/api/v1/session/Agent",
3232
Headerkey: "IDS-AGENT-SESSION-ID",
33-
ClientCertPath: "/mnt/a/c1Client.crt",
34-
CACertPath: "/mnt/a/ca_cert_path.crt",
35-
InsecureSkipVerify: true,
36-
ClientKeyPath: "/mnt/a/client_key_path.pem",
33+
ClientJksPath: "/mnt/a/c1Client.crt",
34+
ClientJksPassword: "changeit1",
35+
CAJksPath: "/mnt/a/ca_cert_path.crt",
36+
CAJksPassword: "changeit2",
37+
InsecureSkipVerify: false,
3738
},
3839
}
3940

extension.go

+121-120
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,28 @@ package infa_auth
33
import (
44
"context"
55
"crypto/tls"
6-
"errors"
7-
"io/ioutil"
8-
"net/http"
9-
106
"crypto/x509"
7+
"errors"
118
"github.com/gofiber/fiber/v2/log"
9+
"github.com/lwithers/minijks/jks"
1210
"go.opentelemetry.io/collector/client"
1311
"go.opentelemetry.io/collector/component"
1412
"go.opentelemetry.io/collector/extension/auth"
1513
"go.uber.org/zap"
14+
"net/http"
1615
"os"
16+
"strings"
1717
"time"
1818
)
1919

2020
type infaAuthExtension struct {
21-
cfg *Config
22-
logger *zap.Logger
21+
cfg *Config
22+
logger *zap.Logger
23+
sessionServiceClient *http.Client
2324
}
2425

2526
func newExtension(ctx context.Context, cfg *Config, logger *zap.Logger) (auth.Server, error) {
26-
/*
27-
if cfg.ValidationURL == "" {
28-
return nil, errors.New("validation url is empty")
29-
}
3027

31-
if cfg.Headerkey == "" {
32-
return nil, errors.New("header key is empty")
33-
}
34-
*/
3528
oe := &infaAuthExtension{
3629
cfg: cfg,
3730
logger: logger,
@@ -45,71 +38,76 @@ func (e *infaAuthExtension) start(context.Context, component.Host) error {
4538
log.Debug("begin executing extension.start")
4639

4740
//validation url and header key must NOT be empty
48-
if e.cfg.ValidationURL == "" {
41+
if strings.TrimSpace(e.cfg.ValidationURL) == "" {
4942
return errors.New("ValidationURL is empty")
5043
}
5144

52-
if e.cfg.Headerkey == "" {
45+
if strings.TrimSpace(e.cfg.Headerkey) == "" {
5346
return errors.New("Headerkey is empty")
5447
}
5548

56-
/*
57-
if e.cfg.CACertPath == "" {
58-
log.Debug("CACertPath is empty")
59-
return errors.New("CACertPath is empty")
60-
}
49+
sessionServiceClient, err := getClient(e.cfg)
50+
e.sessionServiceClient = sessionServiceClient
51+
if err != nil {
52+
log.Debug("error while creating client in extension.start")
53+
return err
54+
}
6155

62-
log.Debugf("CACertPath is : %s ", e.cfg.CACertPath)
63-
_, error := os.Stat(e.cfg.CACertPath)
64-
if error != nil {
65-
log.Debugf("error reading CACertPath")
66-
return errors.New("error reading CACertPath")
67-
}
68-
*/
56+
log.Debugf("finished executing extension.start , sessionServiceClient : %A", sessionServiceClient)
57+
return nil
58+
}
6959

70-
//validate client cert
71-
log.Debugf("ClientSideSsl is : %s ", e.cfg.ClientSideSsl)
72-
if e.cfg.ClientSideSsl {
73-
log.Debugf("ClientCertPath is : %s ", e.cfg.ClientCertPath)
74-
if e.cfg.ClientCertPath == "" {
75-
log.Debug("ClientCertPath is empty")
76-
return errors.New("ClientCertPath is empty")
77-
78-
} else {
79-
_, error := os.Stat(e.cfg.ClientCertPath)
80-
if error != nil {
81-
log.Debugf("error reading ClientCert")
82-
return errors.New("error reading ClientCert")
83-
}
84-
}
60+
func getJksKeystore(filename string, password string) (*jks.Keystore, error) {
8561

86-
log.Debugf("ClientKeyPath is : %s ", e.cfg.ClientKeyPath)
87-
if e.cfg.ClientKeyPath == "" {
88-
log.Debug("ClientKeyPath is empty")
89-
return errors.New("ClientKeyPath is empty")
62+
jksContent, err := os.ReadFile(filename)
63+
if err != nil {
64+
return nil, err
65+
}
66+
log.Debug("read keystorefile : " + filename)
9067

91-
} else {
92-
_, error := os.Stat(e.cfg.ClientKeyPath)
93-
if error != nil {
94-
log.Debugf("error reading ClientKeyPath")
95-
return errors.New("error reading ClientKeyPath")
96-
}
68+
var opts *jks.Options
69+
if strings.TrimSpace(password) == "" {
70+
log.Debug("password is empty , will use nil options")
71+
} else {
72+
opts = &jks.Options{
73+
Password: password,
9774
}
75+
}
9876

77+
keyStore, err := jks.Parse(jksContent, opts)
78+
if err != nil {
79+
return nil, err
9980
}
81+
log.Debug("success read keystorefile : " + filename)
82+
return keyStore, nil
83+
}
10084

101-
log.Debug("finished executing extension.start")
102-
return nil
85+
func getClientCert(ks jks.Keystore) (*tls.Certificate, error) {
86+
cert, err := tls.X509KeyPair(ks.Keypairs[0].CertChain[0].Cert.Raw, ks.Keypairs[0].RawKey)
87+
if err != nil {
88+
log.Debugf("getClientCert ERR %A", err)
89+
}
90+
return &cert, nil
91+
}
92+
93+
func getCACertPool(ks jks.Keystore) (*x509.CertPool, error) {
94+
pool := x509.NewCertPool()
95+
for i := 0; i < len(ks.Certs); i++ {
96+
log.Debugf("adding CA cert to pool : " + string(ks.Certs[i].Alias))
97+
pool.AddCert(ks.Certs[i].Cert)
98+
}
99+
return pool, nil
103100
}
104101

105102
// authenticate checks whether the given context contains valid auth data. Successfully authenticated calls will always return a nil error and a context with the auth data.
106103
// this associated to the Authenticate() method
107104
func (e *infaAuthExtension) authenticate(ctx context.Context, headers map[string][]string) (context.Context, error) {
108105
log.Debug("executing extensions.authenticate() ")
109-
log.Debugf("headers :: %A", headers)
110-
log.Debugf("ctx :: %A", ctx)
111-
log.Debugf("e.cfg :: %A", *e.cfg)
112-
106+
/*
107+
log.Debugf("headers :: %A", headers)
108+
log.Debugf("ctx :: %A", ctx)
109+
log.Debugf("e.cfg :: %A", *e.cfg)
110+
*/
113111
var h []string
114112

115113
h = headers["Ids-Agent-Session-Id"]
@@ -134,81 +132,89 @@ func (e *infaAuthExtension) authenticate(ctx context.Context, headers map[string
134132
}
135133

136134
cl := client.FromContext(ctx)
137-
status, err := validateToken(e.cfg, token)
138-
if err != nil || status == false {
135+
status, err := validateToken(e, token)
136+
if err != nil || !status {
139137
return ctx, err
140138
}
141139

142140
//success
143141
return client.NewContext(ctx, cl), nil
144142
}
145143

146-
// this method creates a HTTP client and makes HTTP/S request to session service, if http status is 200 , it returns
147-
// true with nil error , otherwise non-nil error is returned
148-
func validateToken(cfg *Config, sessionToken string) (bool, error) {
149-
// Create an HTTP client
150-
log.Debug("calling extension.validateToken() ")
151-
144+
func getClient(cfg *Config) (*http.Client, error) {
145+
var pool *x509.CertPool
146+
var clientCert *tls.Certificate
147+
client := &http.Client{
148+
Timeout: func() time.Duration {
149+
if cfg.TimeOut > 0 {
150+
return time.Duration(cfg.TimeOut) * time.Second
151+
}
152+
return 2 * time.Second
153+
}(),
154+
}
152155
tr := &http.Transport{}
156+
client.Transport = tr
153157

154-
//read ca-cert file
155-
pool := x509.NewCertPool()
156-
if cfg.CACertPath != "" {
157-
cert, err := ioutil.ReadFile(cfg.CACertPath)
158-
if err != nil {
159-
log.Debugf("Error reading CA certificate: %A", err)
160-
return false, err
161-
} else {
162-
pool.AppendCertsFromPEM(cert)
163-
tr = &http.Transport{
164-
TLSClientConfig: &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify, RootCAs: pool},
165-
}
166-
}
158+
//create client with no TLS
159+
if cfg.InsecureSkipVerify {
160+
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: true}
161+
log.Info("returning client block1 ")
162+
return client, nil
167163
} else {
168-
tr = &http.Transport{
169-
TLSClientConfig: &tls.Config{InsecureSkipVerify: cfg.InsecureSkipVerify},
170-
}
171-
}
164+
//create client with TLS , CACert is mandatory clientCert is optional
165+
caKs, err := getJksKeystore(cfg.CAJksPath, cfg.CAJksPassword)
172166

173-
// Load client certificate and private key
174-
if cfg.ClientSideSsl {
175-
cert, err := tls.LoadX509KeyPair(cfg.ClientCertPath, cfg.ClientKeyPath)
176167
if err != nil {
177-
log.Debugf("Error loading client certificate:", err)
178-
return false, err
168+
log.Info("error while reading CA jksKeystore : " + cfg.CAJksPath)
169+
return nil, err
179170
}
180-
config := &tls.Config{
181-
Certificates: []tls.Certificate{cert},
182-
RootCAs: pool,
183-
InsecureSkipVerify: cfg.InsecureSkipVerify,
171+
172+
pool, err = getCACertPool(*caKs)
173+
if err != nil {
174+
log.Info("error while generating CACertPool : " + cfg.CAJksPath)
175+
return nil, err
184176
}
185-
tr = &http.Transport{
186-
TLSClientConfig: config,
177+
178+
tr.TLSClientConfig = &tls.Config{InsecureSkipVerify: false, RootCAs: pool}
179+
180+
if cfg.ClientSideSsl {
181+
clientKs, err := getJksKeystore(cfg.ClientJksPath, cfg.ClientJksPassword)
182+
if err != nil {
183+
log.Info("error while reading Client jksKeystore : " + cfg.ClientJksPath)
184+
return nil, err
185+
}
186+
187+
clientCert, err = getClientCert(*clientKs)
188+
if err != nil {
189+
log.Info("error while creating Client cert : " + cfg.ClientJksPath)
190+
return nil, err
191+
}
192+
193+
tr.TLSClientConfig.Certificates = []tls.Certificate{*clientCert}
187194
}
188-
}
189195

190-
client := &http.Client{}
191-
if cfg.TimeOut > 0 {
192-
client = &http.Client{
193-
Timeout: time.Duration(cfg.TimeOut) * time.Second,
194-
Transport: tr}
195-
} else {
196-
client = &http.Client{
197-
Timeout: 2 * time.Second,
198-
Transport: tr}
196+
log.Info("returning client block2 ")
197+
client.Transport = tr
198+
return client, nil
199199
}
200+
}
200201

201-
req, err := http.NewRequest("GET", cfg.ValidationURL, nil)
202+
// this method creates a HTTP client and makes HTTP/S request to session service, if http status is 200 , it returns
203+
// true with nil error , otherwise non-nil error is returned
204+
func validateToken(e *infaAuthExtension, sessionToken string) (bool, error) {
205+
// Create an HTTP client
206+
log.Debug("calling extension.validateToken() ")
207+
req, err := http.NewRequest("GET", e.cfg.ValidationURL, nil)
202208
if err != nil {
203209
log.Debugf("Error creating request:", err)
204210
return false, err
205211
}
206212

207-
req.Header.Add(cfg.Headerkey, sessionToken)
213+
req.Header.Add(e.cfg.Headerkey, sessionToken)
208214

209215
// Make the request
210216
log.Debugf("invoking http request : %A", req)
211-
resp, err := client.Do(req)
217+
resp, err := e.sessionServiceClient.Do(req)
212218
log.Debugf("got response %A", resp)
213219

214220
if err != nil {
@@ -218,20 +224,15 @@ func validateToken(cfg *Config, sessionToken string) (bool, error) {
218224

219225
if resp.StatusCode != 200 {
220226
log.Debug("http status is not 200 in response")
221-
return false, errors.New("http status is not 200 in response")
227+
body := make([]byte, 1024)
228+
_, err := resp.Body.Read(body)
229+
if err != nil {
230+
log.Debugf("Error reading response body:", err)
231+
return false, err
232+
}
233+
return false, errors.New("http status is not 200 in response response body is " + string(body))
222234
}
223235

224236
defer resp.Body.Close()
225-
226-
// Read the response body
227-
body, err := ioutil.ReadAll(resp.Body)
228-
if err != nil {
229-
log.Debugf("Error reading response body:", err)
230-
return false, err
231-
}
232-
233-
// Print the response
234-
log.Debugf("response body is %A", string(body))
235237
return true, nil
236-
237238
}

0 commit comments

Comments
 (0)