Skip to content

Commit

Permalink
Add TLS communication between Galadriel Server and Harvester (#146)
Browse files Browse the repository at this point in the history
Add TLS communication between Server and Harvester

Signed-off-by: Max Lambrecht <[email protected]>
  • Loading branch information
Max Lambrecht authored May 4, 2023
1 parent a02b1c4 commit cf8281a
Show file tree
Hide file tree
Showing 14 changed files with 361 additions and 46 deletions.
10 changes: 9 additions & 1 deletion cmd/harvester/cli/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cli
import (
"fmt"
"io"
"net"
"time"

"github.com/HewlettPackard/galadriel/pkg/common/telemetry"
Expand All @@ -28,6 +29,7 @@ type Config struct {
type harvesterConfig struct {
SpireSocketPath string `hcl:"spire_socket_path"`
ServerAddress string `hcl:"server_address"`
ServerTrustBundlePath string `hcl:"server_trust_bundle_path"`
BundleUpdatesInterval string `hcl:"bundle_updates_interval"`
LogLevel string `hcl:"log_level"`
}
Expand Down Expand Up @@ -61,8 +63,14 @@ func NewHarvesterConfig(c *Config) (*harvester.Config, error) {
return nil, fmt.Errorf("failed to parse bundle updates interval: %v", err)
}

serverTCPAddress, err := net.ResolveTCPAddr("tcp", c.Harvester.ServerAddress)
if err != nil {
return nil, fmt.Errorf("failed to resolve server address: %v", err)
}

hc.SpireAddress = spireAddr
hc.ServerAddress = c.Harvester.ServerAddress
hc.ServerAddress = serverTCPAddress
hc.ServerTrustBundlePath = c.Harvester.ServerTrustBundlePath
hc.BundleUpdatesInterval = buInt

hc.Logger = logrus.WithField(telemetry.SubsystemName, telemetry.Harvester)
Expand Down
2 changes: 2 additions & 0 deletions cmd/harvester/cli/config_test.go
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
package cli

// TODO: add tests
32 changes: 32 additions & 0 deletions conf/harvester/dummy_root_ca.crt
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
-----BEGIN CERTIFICATE-----
MIIFkjCCA3qgAwIBAgIUaCjXS2q5FyByi//FJRIoVoxwNLowDQYJKoZIhvcNAQEL
BQAwMTELMAkGA1UEBhMCVVMxDzANBgNVBAoMBlNQSUZGRTERMA8GA1UEAwwIcGVy
ZnRlc3QwHhcNMjMwMzA4MTgwMjAxWhcNMzQwNTI1MTgwMjAxWjAxMQswCQYDVQQG
EwJVUzEPMA0GA1UECgwGU1BJRkZFMREwDwYDVQQDDAhwZXJmdGVzdDCCAiIwDQYJ
KoZIhvcNAQEBBQADggIPADCCAgoCggIBAN3NNPUn6NK7PthpCcGMboyWU/jusjHc
DALVbPsnGaSq2fup/d+7bQ7ElY4R26fK7KibIh8S1s6uGgrjBnOKkJRmVhxxtqZC
C8devw/zWEdEAY5VeVHlN7gzKBJpmy3U3HBwhx6eFqwB85enz/9Y6FgWU5jbTkw5
SyPGMo+jiUPWBhaKH2tGoa3kujbrTcCjyRWYu6jyk6ZjSyJXWriY526HZwaFl6/c
C64AYGdnRSPAFYKi0s4FILd11kbgno2UqZqUb+LkuYyrMBMd9VPjISQKGmKprcIU
6KY79LTELse5lOHFmJWbELpUtyWOglWEpTlwWdr4AmJwcfS9po6FY2CLmDIi9Xgy
aLYJwLh4xllqSg1GCx44FIeOqIVjJYNbWxF1EHPk9ZCaRNemFV6PRns4ePCZnjtp
hZjSXxkIXsJfu5CHjn88OIEby0rxNCFREMZZ/o22UXI9Iuq72/SjS4nT5W7Wo/K3
m4YvrbfIBVxPTwoK4w5fLXJPa5rcPgRbtch3UQX6/gLY3akzfFH+mGlv2V06fxRx
hbrQIcZGyPf5TvugW0UIcFLv3RW1ScxQjcvajPDEZzpLArLiC/lnD3rmXUgau7SQ
4nUqHT/STL7KPDu0XMa7omSQfqljwed978Rg1gqNiKOB0WSu9+phGxLakad5/k+e
mbtLkG0RZiA5AgMBAAGjgaEwgZ4wDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU
E13a4pFoH0GGLDghCoqJ3N68kJIwbAYDVR0jBGUwY4AUE13a4pFoH0GGLDghCoqJ
3N68kJKhNaQzMDExCzAJBgNVBAYTAlVTMQ8wDQYDVQQKDAZTUElGRkUxETAPBgNV
BAMMCHBlcmZ0ZXN0ghRoKNdLarkXIHKL/8UlEihWjHA0ujANBgkqhkiG9w0BAQsF
AAOCAgEAh4rjrwnUI0gEtHgKXCnx5rcT2DBAy/9xAOxdsDOVqheiDWoC0NK9Iet8
m/01uwY5v+PJ4gmdVGO3V/FOvb3KOAiilZPFmI0jUYRpceUTh7FkRsZPLFeGYJ7K
OPJ6BzX1Y+aHcMK6jaTtHrgxS2gt1cWEB4nSYFQKKVqCmExrZpiFDw0ZY3RTFXSc
LA1jV1/1OD05G6UxdHSNbTzQRlNaLPeE+EL9eyiIF2RxvnQul6qZwed35nh1TqjN
n+nNSH8LSZxdwQLNEn0CYQ2LuUeAr6EdvfFibse2fyTU+HBSyuH4SRXjvFC4WuSr
pPtnGPtKwVVCNhCYiixOMPCTOL68ZWK7XC7AaHSX7pD7DuRZU2WfAJ4gSD/jUJel
ku2Kxl2NXeXrlkzMs6Ud022mLL/sFSd4WLuB/G+fk7wgwdezk4syISHSGQ0+Xech
QqpvPqj/+hi3C2yUxzZojxVSBJkB9+Z8LFrk8qpO0VREO5KD/9444Ric/CTgjQmg
qfGlCd4P+2YYEd8em7VIE5d/yDJ8rGLT45d7AwepWJzXEZBb1DMDcGVbSSISDyCh
ZAmmTw37cxEfp+7nKAGor64lX3DbRiPQV/MGv6MhNPNRyBPH7kkeZuE6ewh8bdfV
8YAhxXpLl11iLC4bZbj2AGlZ+Cr0WEVaX7g7S82cdrmc208zBR8=
-----END CERTIFICATE-----
3 changes: 3 additions & 0 deletions conf/harvester/harvester.conf
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ harvester {
# E.g: localhost:8085, my-upstream-server.com:4556, 192.168.1.125:4000
server_address = "localhost:8085"

# server_trust_bundle_path: Path to the Galadriel Server CA bundle.
server_trust_bundle_path = "./conf/harvester/dummy_root_ca.crt"

# bundle_updates_interval: Sets how often to check for bundle rotation.
# Typically this should be less than the CA TTL set in SPIRE.
# Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
Expand Down
5 changes: 5 additions & 0 deletions pkg/common/constants/constants.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package constants

const (
GaladrielServerName = "galadriel-server"
)
7 changes: 7 additions & 0 deletions pkg/common/x509ca/disk/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,13 @@ func (ca *X509CA) Configure(config *Config) error {
// IssueX509Certificate issues an X509 certificate using the disk-based private key and ROOT CA certificate. The certificate
// is bound to the given public key and subject.
func (ca *X509CA) IssueX509Certificate(ctx context.Context, params *x509ca.X509CertificateParams) ([]*x509.Certificate, error) {
if params.PublicKey == nil {
return nil, errors.New("public key is required")
}
if params.TTL == 0 {
return nil, errors.New("TTL is required")
}

template, err := cryptoutil.CreateX509Template(ca.clock, params.PublicKey, params.Subject, params.URIs, params.DNSNames, params.TTL)
if err != nil {
return nil, fmt.Errorf("failed to create template for Server certificate: %w", err)
Expand Down
53 changes: 45 additions & 8 deletions pkg/harvester/client/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,17 @@ package client
import (
"bytes"
"context"
"crypto/tls"
"crypto/x509"
"encoding/json"
"fmt"
"io"
"net"
"net/http"
"os"

"github.com/HewlettPackard/galadriel/pkg/common"
"github.com/HewlettPackard/galadriel/pkg/common/constants"
"github.com/HewlettPackard/galadriel/pkg/common/telemetry"
"github.com/sirupsen/logrus"
)
Expand All @@ -29,23 +34,31 @@ type GaladrielServerClient interface {
}

type client struct {
c http.Client
address string
c *http.Client
address *net.TCPAddr
token string
logger logrus.FieldLogger
}

func NewGaladrielServerClient(address, token string) (GaladrielServerClient, error) {
// NewGaladrielServerClient creates a new Galadriel Server client, using the given token to authenticate
// and the given trustBundlePath to validate the server certificate.
func NewGaladrielServerClient(address *net.TCPAddr, token string, trustBundlePath string) (GaladrielServerClient, error) {
c, err := createTLSClient(trustBundlePath)
if err != nil {
return nil, fmt.Errorf("failed to create TLS client: %w", err)
}

return &client{
c: *http.DefaultClient,
address: "http://" + address,
c: c,
address: address,
token: token,
logger: logrus.WithField(telemetry.SubsystemName, telemetry.GaladrielServerClient),
}, nil
}

func (c *client) Connect(ctx context.Context, token string) error {
url := c.address + onboardPath
url := fmt.Sprintf("https://%s%s", c.address.String(), onboardPath)

req, err := http.NewRequestWithContext(ctx, http.MethodConnect, url, nil)
if err != nil {
return err
Expand Down Expand Up @@ -77,7 +90,7 @@ func (c *client) SyncFederatedBundles(ctx context.Context, req *common.SyncBundl
}

c.logger.Debugf("Sending post federated bundles updates:\n%s", b)
url := c.address + postBundleSyncPath
url := c.address.String() + postBundleSyncPath
r, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(b))
if err != nil {
return nil, fmt.Errorf("failed to create request: %v", err)
Expand Down Expand Up @@ -117,7 +130,7 @@ func (c *client) PostBundle(ctx context.Context, req *common.PostBundleRequest)
return fmt.Errorf("failed to marshal push bundle request: %v", err)
}

url := c.address + postBundlePath
url := c.address.String() + postBundlePath

r, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(b))
if err != nil {
Expand Down Expand Up @@ -147,6 +160,30 @@ func (c *client) PostBundle(ctx context.Context, req *common.PostBundleRequest)
return nil
}

func createTLSClient(trustBundlePath string) (*http.Client, error) {
caCert, err := os.ReadFile(trustBundlePath)
if err != nil {
return nil, err
}

caCertPool := x509.NewCertPool()
ok := caCertPool.AppendCertsFromPEM(caCert)
if !ok {
return nil, fmt.Errorf("failed to append CA certificates")
}

tr := &http.Transport{
TLSClientConfig: &tls.Config{
RootCAs: caCertPool,
ServerName: constants.GaladrielServerName,
},
}

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

func readBody(resp *http.Response) (string, error) {
bodyBytes, err := io.ReadAll(resp.Body)
if err != nil {
Expand Down
5 changes: 4 additions & 1 deletion pkg/harvester/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ type Config struct {
LocalAddress net.Addr

// Address of Galadriel server
ServerAddress string
ServerAddress *net.TCPAddr

// Address of SPIRE Server
SpireAddress net.Addr
Expand All @@ -30,5 +30,8 @@ type Config struct {
// Directory to store runtime data
DataDir string

// Path to the trust bundle for the Galadriel Server
ServerTrustBundlePath string

Logger logrus.FieldLogger
}
8 changes: 2 additions & 6 deletions pkg/harvester/controller/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,23 +26,19 @@ type HarvesterController struct {

// Config represents the configurations for the Harvester Controller
type Config struct {
ServerAddress string
SpireSocketPath net.Addr
AccessToken string
BundleUpdatesInterval time.Duration
Logger logrus.FieldLogger
GaladrielServerClient client.GaladrielServerClient
}

func NewHarvesterController(ctx context.Context, config *Config) (*HarvesterController, error) {
sc := spire.NewLocalSpireServer(ctx, config.SpireSocketPath)
gc, err := client.NewGaladrielServerClient(config.ServerAddress, config.AccessToken)
if err != nil {
return nil, err
}

return &HarvesterController{
spire: sc,
server: gc,
server: config.GaladrielServerClient,
config: config,
logger: logrus.WithField(telemetry.SubsystemName, telemetry.HarvesterController),
}, nil
Expand Down
10 changes: 6 additions & 4 deletions pkg/harvester/harvester.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package harvester
import (
"context"
"errors"
"fmt"

"github.com/HewlettPackard/galadriel/pkg/common/telemetry"
"github.com/HewlettPackard/galadriel/pkg/common/util"
Expand Down Expand Up @@ -31,18 +32,18 @@ func (h *Harvester) Run(ctx context.Context) error {
return errors.New("token is required to connect the Harvester to the Galadriel Server")
}

galadrielClient, err := client.NewGaladrielServerClient(h.config.ServerAddress, h.config.JoinToken)
galadrielClient, err := client.NewGaladrielServerClient(h.config.ServerAddress, h.config.JoinToken, h.config.ServerTrustBundlePath)
if err != nil {
return err
return fmt.Errorf("failed to create Galadriel Server client: %w", err)
}

err = galadrielClient.Connect(ctx, h.config.JoinToken)
if err != nil {
return err
return fmt.Errorf("failed to connect to Galadriel Server: %w", err)
}

config := &controller.Config{
ServerAddress: h.config.ServerAddress,
GaladrielServerClient: galadrielClient,
SpireSocketPath: h.config.SpireAddress,
AccessToken: h.config.JoinToken,
BundleUpdatesInterval: h.config.BundleUpdatesInterval,
Expand All @@ -60,6 +61,7 @@ func (h *Harvester) Run(ctx context.Context) error {
return err
}

// TODO: implement this or remove it
func (h *Harvester) Stop() {
// unload and cleanup stuff
}
4 changes: 2 additions & 2 deletions pkg/server/endpoints/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package endpoints

import (
"github.com/HewlettPackard/galadriel/pkg/server/catalog"
"github.com/HewlettPackard/galadriel/pkg/server/datastore"
"net"

"github.com/sirupsen/logrus"
Expand All @@ -15,8 +16,7 @@ type Config struct {
// LocalAddress is the local address to bind the listener to.
LocalAddress net.Addr

// Postgres connection string
DatastoreConnString string
Datastore datastore.Datastore

Logger logrus.FieldLogger

Expand Down
Loading

0 comments on commit cf8281a

Please sign in to comment.