Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add TLS communication between Galadriel Server and Harvester #146

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Did you considered moving some of the consts defined in our telemetry package here?


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