Skip to content

Commit 7a905f4

Browse files
michelvocksSkarlso
authored andcommitted
Unix socket to mutual TLS port connection (#54)
1 parent 70b3dd5 commit 7a905f4

File tree

4 files changed

+84
-14
lines changed

4 files changed

+84
-14
lines changed

cmd/gaia/main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ func main() {
155155
pS := &plugin.Plugin{}
156156

157157
// Initialize scheduler
158-
scheduler := scheduler.NewScheduler(store, pS)
158+
scheduler := scheduler.NewScheduler(store, pS, cert)
159159
err = scheduler.Init()
160160
if err != nil {
161161
gaia.Cfg.Logger.Error("cannot initialize scheduler:", "error", err.Error())

plugin/plugin.go

+49-3
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@ import (
99

1010
"github.com/gaia-pipeline/gaia"
1111
"github.com/gaia-pipeline/gaia/scheduler"
12+
"github.com/gaia-pipeline/gaia/security"
1213
"github.com/gaia-pipeline/protobuf"
1314
plugin "github.com/hashicorp/go-plugin"
1415
)
1516

1617
const (
1718
pluginMapKey = "Plugin"
19+
20+
// env variable key names for TLS cert path
21+
serverCertEnv = "GAIA_PLUGIN_CERT"
22+
serverKeyEnv = "GAIA_PLUGIN_KEY"
23+
rootCACertEnv = "GAIA_PLUGIN_CA_CERT"
1824
)
1925

2026
var handshake = plugin.HandshakeConfig{
21-
ProtocolVersion: 1,
27+
ProtocolVersion: 2,
2228
MagicCookieKey: "GAIA_PLUGIN",
2329
// This cookie should never be changed again
2430
MagicCookieValue: "FdXjW27mN6XuG2zDBP4LixXUwDAGCEkidxwqBGYpUhxiWHzctATYZvpz4ZJdALmh",
@@ -42,12 +48,21 @@ type Plugin struct {
4248

4349
// Writer used to write logs from execution to file
4450
writer *bufio.Writer
51+
52+
// CA instance used to handle certificates
53+
ca security.CAAPI
54+
55+
// Created certificates path for pipeline run
56+
certPath string
57+
keyPath string
58+
serverCertPath string
59+
serverKeyPath string
4560
}
4661

4762
// NewPlugin creates a new instance of Plugin.
4863
// One Plugin instance represents one connection to a plugin.
49-
func (p *Plugin) NewPlugin() scheduler.Plugin {
50-
return &Plugin{}
64+
func (p *Plugin) NewPlugin(ca security.CAAPI) scheduler.Plugin {
65+
return &Plugin{ca: ca}
5166
}
5267

5368
// Connect prepares the log path, starts the plugin, initiates the
@@ -75,13 +90,40 @@ func (p *Plugin) Connect(command *exec.Cmd, logPath *string) error {
7590
// Create new writer
7691
p.writer = bufio.NewWriter(p.logFile)
7792

93+
// Create and sign a new pair of certificates for the server
94+
var err error
95+
p.serverCertPath, p.serverKeyPath, err = p.ca.CreateSignedCert()
96+
if err != nil {
97+
return err
98+
}
99+
100+
// Expose path of server certificates as well as public CA cert.
101+
// This allows the plugin to grab the certificates.
102+
caCert, _ := p.ca.GetCACertPath()
103+
command.Env = append(command.Env, serverCertEnv+"="+p.serverCertPath)
104+
command.Env = append(command.Env, serverKeyEnv+"="+p.serverKeyPath)
105+
command.Env = append(command.Env, rootCACertEnv+"="+caCert)
106+
107+
// Create and sign a new pair of certificates for the client
108+
p.certPath, p.keyPath, err = p.ca.CreateSignedCert()
109+
if err != nil {
110+
return err
111+
}
112+
113+
// Generate TLS config
114+
tlsConfig, err := p.ca.GenerateTLSConfig(p.certPath, p.keyPath)
115+
if err != nil {
116+
return err
117+
}
118+
78119
// Get new client
79120
p.client = plugin.NewClient(&plugin.ClientConfig{
80121
HandshakeConfig: handshake,
81122
Plugins: pluginMap,
82123
Cmd: command,
83124
AllowedProtocols: []plugin.Protocol{plugin.ProtocolGRPC},
84125
Stderr: p.writer,
126+
TLSConfig: tlsConfig,
85127
})
86128

87129
// Connect via gRPC
@@ -176,5 +218,9 @@ func (p *Plugin) Close() {
176218

177219
// Close log file
178220
p.logFile.Close()
221+
222+
// Cleanup certificates
223+
p.ca.CleanupCerts(p.certPath, p.keyPath)
224+
p.ca.CleanupCerts(p.serverCertPath, p.serverKeyPath)
179225
}()
180226
}

scheduler/scheduler.go

+9-4
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import (
1010
"time"
1111

1212
"github.com/gaia-pipeline/gaia"
13+
"github.com/gaia-pipeline/gaia/security"
1314
"github.com/gaia-pipeline/gaia/store"
1415
uuid "github.com/satori/go.uuid"
1516
)
@@ -33,7 +34,7 @@ var (
3334
// during scheduling and execution.
3435
type Plugin interface {
3536
// NewPlugin creates a new instance of plugin
36-
NewPlugin() Plugin
37+
NewPlugin(ca security.CAAPI) Plugin
3738

3839
// Connect initializes the connection with the execution command
3940
// and the log path wbere the logs should be stored.
@@ -60,15 +61,19 @@ type Scheduler struct {
6061

6162
// pluginSystem is the used plugin system.
6263
pluginSystem Plugin
64+
65+
// ca is the instance of the CA used to handle certs.
66+
ca security.CAAPI
6367
}
6468

6569
// NewScheduler creates a new instance of Scheduler.
66-
func NewScheduler(store *store.Store, pS Plugin) *Scheduler {
70+
func NewScheduler(store *store.Store, pS Plugin, ca security.CAAPI) *Scheduler {
6771
// Create new scheduler
6872
s := &Scheduler{
6973
scheduledRuns: make(chan gaia.PipelineRun, schedulerBufferLimit),
7074
storeService: store,
7175
pluginSystem: pS,
76+
ca: ca,
7277
}
7378

7479
return s
@@ -165,7 +170,7 @@ func (s *Scheduler) prepareAndExec(r gaia.PipelineRun) {
165170
}
166171

167172
// Create new plugin instance
168-
pS := s.pluginSystem.NewPlugin()
173+
pS := s.pluginSystem.NewPlugin(s.ca)
169174

170175
// Connect to plugin(pipeline)
171176
path = filepath.Join(path, gaia.LogsFileName)
@@ -368,7 +373,7 @@ func (s *Scheduler) getPipelineJobs(p *gaia.Pipeline) ([]gaia.Job, error) {
368373
}
369374

370375
// Create new Plugin instance
371-
pS := s.pluginSystem.NewPlugin()
376+
pS := s.pluginSystem.NewPlugin(s.ca)
372377

373378
// Connect to plugin(pipeline)
374379
if err := pS.Connect(c, nil); err != nil {

scheduler/scheduler_test.go

+25-6
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,35 @@
11
package scheduler
22

33
import (
4+
"crypto/tls"
45
"hash/fnv"
56
"os"
67
"os/exec"
78
"path/filepath"
89
"testing"
910

1011
"github.com/gaia-pipeline/gaia"
12+
"github.com/gaia-pipeline/gaia/security"
1113
"github.com/gaia-pipeline/gaia/store"
1214
hclog "github.com/hashicorp/go-hclog"
1315
uuid "github.com/satori/go.uuid"
1416
)
1517

1618
type PluginFake struct{}
1719

18-
func (p *PluginFake) NewPlugin() Plugin { return &PluginFake{} }
20+
func (p *PluginFake) NewPlugin(ca security.CAAPI) Plugin { return &PluginFake{} }
1921
func (p *PluginFake) Connect(cmd *exec.Cmd, logPath *string) error { return nil }
2022
func (p *PluginFake) Execute(j *gaia.Job) error { return nil }
2123
func (p *PluginFake) GetJobs() ([]gaia.Job, error) { return prepareJobs(), nil }
2224
func (p *PluginFake) Close() {}
2325

26+
type CAFake struct{}
27+
28+
func (c *CAFake) CreateSignedCert() (string, string, error) { return "", "", nil }
29+
func (c *CAFake) GenerateTLSConfig(certPath, keyPath string) (*tls.Config, error) { return nil, nil }
30+
func (c *CAFake) CleanupCerts(crt, key string) error { return nil }
31+
func (c *CAFake) GetCACertPath() (string, string) { return "", "" }
32+
2433
func TestInit(t *testing.T) {
2534
gaia.Cfg = &gaia.Config{}
2635
storeInstance := store.NewStore()
@@ -36,7 +45,9 @@ func TestInit(t *testing.T) {
3645
if err := storeInstance.Init(); err != nil {
3746
t.Fatal(err)
3847
}
39-
s := NewScheduler(storeInstance, &PluginFake{})
48+
var ca security.CAAPI
49+
ca = &CAFake{}
50+
s := NewScheduler(storeInstance, &PluginFake{}, ca)
4051
err := s.Init()
4152
if err != nil {
4253
t.Fatal(err)
@@ -64,7 +75,9 @@ func TestPrepareAndExec(t *testing.T) {
6475
}
6576
p, r := prepareTestData()
6677
storeInstance.PipelinePut(&p)
67-
s := NewScheduler(storeInstance, &PluginFake{})
78+
var ca security.CAAPI
79+
ca = &CAFake{}
80+
s := NewScheduler(storeInstance, &PluginFake{}, ca)
6881
s.prepareAndExec(r)
6982

7083
// Iterate jobs
@@ -98,7 +111,9 @@ func TestSchedulePipeline(t *testing.T) {
98111
}
99112
p, _ := prepareTestData()
100113
storeInstance.PipelinePut(&p)
101-
s := NewScheduler(storeInstance, &PluginFake{})
114+
var ca security.CAAPI
115+
ca = &CAFake{}
116+
s := NewScheduler(storeInstance, &PluginFake{}, ca)
102117
err := s.Init()
103118
if err != nil {
104119
t.Fatal(err)
@@ -131,7 +146,9 @@ func TestSchedule(t *testing.T) {
131146
}
132147
p, _ := prepareTestData()
133148
storeInstance.PipelinePut(&p)
134-
s := NewScheduler(storeInstance, &PluginFake{})
149+
var ca security.CAAPI
150+
ca = &CAFake{}
151+
s := NewScheduler(storeInstance, &PluginFake{}, ca)
135152
_, err := s.SchedulePipeline(&p)
136153
if err != nil {
137154
t.Fatal(err)
@@ -166,7 +183,9 @@ func TestSetPipelineJobs(t *testing.T) {
166183
}
167184
p, _ := prepareTestData()
168185
p.Jobs = nil
169-
s := NewScheduler(storeInstance, &PluginFake{})
186+
var ca security.CAAPI
187+
ca = &CAFake{}
188+
s := NewScheduler(storeInstance, &PluginFake{}, ca)
170189
err := s.SetPipelineJobs(&p)
171190
if err != nil {
172191
t.Fatal(err)

0 commit comments

Comments
 (0)