Skip to content

Commit

Permalink
Adding CRD config plugin section (#537)
Browse files Browse the repository at this point in the history
* removed handlers

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* nit spacing fixes

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* Nits

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* added plugin config parsing

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* Nits

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* Initialize Package

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* add initial documentation

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* added documentation

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* nit lint

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* nit

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* Add reference link

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* nit lints

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* fix key length check

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* nit

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

* nits

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>

---------

Signed-off-by: Maia Iyer <maia.raj.iyer@gmail.com>
  • Loading branch information
maia-iyer authored Oct 21, 2024
1 parent 952e302 commit 71a03a4
Show file tree
Hide file tree
Showing 7 changed files with 249 additions and 147 deletions.
62 changes: 55 additions & 7 deletions api/agent/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/spiffe/tornjak/pkg/agent/authentication/authenticator"
"github.com/spiffe/tornjak/pkg/agent/authorization"
agentdb "github.com/spiffe/tornjak/pkg/agent/db"
"github.com/spiffe/tornjak/pkg/agent/spirecrd"
)

func stringFromToken(keyToken token.Token) (string, error) {
Expand All @@ -34,9 +35,17 @@ func stringFromToken(keyToken token.Token) (string, error) {
// getPluginConfig returns first plugin configuration
func getPluginConfig(plugin *ast.ObjectItem) (string, ast.Node, error) {
// extract plugin name and value
pluginName, err := stringFromToken(plugin.Keys[1].Token)
if err != nil {
return "", nil, fmt.Errorf("invalid plugin type name %q: %w", plugin.Keys[1].Token.Text, err)
pluginKeys := plugin.Keys
var pluginName string
var err error

if len(pluginKeys) > 1 {
pluginName, err = stringFromToken(pluginKeys[1].Token)
if err != nil {
return "", nil, fmt.Errorf("invalid plugin type name %q: %w", pluginKeys[1].Token.Text, err)
}
} else {
pluginName = ""
}
// extract data
var hclPluginConfig hclPluginConfig
Expand Down Expand Up @@ -85,6 +94,30 @@ func NewAgentsDB(dbPlugin *ast.ObjectItem) (agentdb.AgentDB, error) {
}
}

// NewCRDManager returns ...
func NewCRDManager(crdPlugin *ast.ObjectItem) (spirecrd.CRDManager, error) {
_, data, _ := getPluginConfig(crdPlugin)

// check if data is defined
if data == nil {
return "", errors.New("SPIRECRDManager plugin ('config > plugins > SPIRECRDManager > plugin_data') not populated")
}
// decode config to struct
var config pluginControllerManager
if err := hcl.DecodeObject(&config, data); err != nil {
return "", errors.Errorf("Couldn't parse SPIREControllerManager config: %v", err)
}

fmt.Println("CRD Controller configured. WARNING: This is currently a no-op")

crdManager, err := spirecrd.NewSPIRECRDManager(config.Classname)
if err != nil {
return nil, errors.Errorf("Could not initialize CRD manager: %v", err)
}

return crdManager, nil
}

// NewAuthenticator returns a new Authenticator
func NewAuthenticator(authenticatorPlugin *ast.ObjectItem) (authenticator.Authenticator, error) {
key, data, _ := getPluginConfig(authenticatorPlugin)
Expand Down Expand Up @@ -225,10 +258,6 @@ func (s *Server) Configure() error {
// iterate over plugin list

for _, pluginObject := range pluginList.Items {
if len(pluginObject.Keys) != 2 {
return fmt.Errorf("plugin item expected to have two keys (type then name)")
}

pluginType, err := stringFromToken(pluginObject.Keys[0].Token)
if err != nil {
return fmt.Errorf("invalid plugin type key %q: %w", pluginObject.Keys[0].Token.Text, err)
Expand All @@ -238,18 +267,37 @@ func (s *Server) Configure() error {
switch pluginType {
// configure datastore
case "DataStore":
if len(pluginObject.Keys) != 2 {
return fmt.Errorf("plugin DataStore expected to have two keys (type then name)")
}
s.Db, err = NewAgentsDB(pluginObject)
if err != nil {
return errors.Errorf("Cannot configure datastore plugin: %v", err)
}
// configure controller maanger CRD management
case "SPIRECRDManager":
if len(pluginObject.Keys) != 1 {
return fmt.Errorf("plugin SPIRECRDManager expected to have one key (type)")
}

s.CRDManager, err = NewCRDManager(pluginObject)
if err != nil {
return errors.Errorf("Cannot configure CRD management plugin: %v", err)
}
// configure Authenticator
case "Authenticator":
if len(pluginObject.Keys) != 2 {
return fmt.Errorf("plugin Authenticator expected to have two keys (type then name)")
}
s.Authenticator, err = NewAuthenticator(pluginObject)
if err != nil {
return errors.Errorf("Cannot configure Authenticator plugin: %v", err)
}
// configure Authorizer
case "Authorizer":
if len(pluginObject.Keys) != 2 {
return fmt.Errorf("plugin Authorizer expected to have two keys (type then name)")
}
s.Authorizer, err = NewAuthorizer(pluginObject)
if err != nil {
return errors.Errorf("Cannot configure Authorizer plugin: %v", err)
Expand Down
2 changes: 2 additions & 0 deletions api/agent/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import (
"github.com/spiffe/tornjak/pkg/agent/authentication/authenticator"
"github.com/spiffe/tornjak/pkg/agent/authorization"
agentdb "github.com/spiffe/tornjak/pkg/agent/db"
"github.com/spiffe/tornjak/pkg/agent/spirecrd"
)

type Server struct {
Expand All @@ -32,6 +33,7 @@ type Server struct {

// Plugins
Db agentdb.AgentDB
CRDManager spirecrd.CRDManager
Authenticator authenticator.Authenticator
Authorizer authorization.Authorizer
}
Expand Down
268 changes: 136 additions & 132 deletions api/agent/types.go
Original file line number Diff line number Diff line change
@@ -1,132 +1,136 @@
package api

import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"

"github.com/hashicorp/hcl/hcl/ast"
)

// TornjakServerInfo provides insight into the configuration of the SPIRE server
// where the Tornjak Agent resides
type TornjakSpireServerInfo struct {
// Plugins is a map from plugin types to respective names of plugins configured
Plugins map[string][]string `json:"plugins"`
// TrustDomain specifies the trust domain of the SPIRE server configured with tornjak
TrustDomain string `json:"trustDomain"`
// Verbose config contains unstructure information on the config on the agent
VerboseConfig string `json:"verboseConfig"`
}

// pared down version of full Server Config type spire/cmd/spire-server/cli/run
// we curently need only extract the trust domain
type SpireServerConfig struct {
TrustDomain string `hcl:"trust_domain"`
}

type SPIREConfig struct {
Server *SpireServerConfig `hcl:"server"`
Plugins ast.Node `hcl:"plugins"`
}

type TornjakConfig struct {
Server *serverConfig `hcl:"server"`
Plugins *ast.Node `hcl:"plugins"`
}

/* Server configuration*/

type serverConfig struct {
SPIRESocket string `hcl:"spire_socket_path"`
HTTPConfig *HTTPConfig `hcl:"http"`
HTTPSConfig *HTTPSConfig `hcl:"https"`
}

type HTTPConfig struct {
ListenPort int `hcl:"port"`
}

type HTTPSConfig struct {
ListenPort int `hcl:"port"`
Cert string `hcl:"cert"`
Key string `hcl:"key"`
ClientCA string `hcl:"client_ca"`
}

func (h HTTPSConfig) Parse() (*tls.Config, error) {
serverCertPath := h.Cert
serverKeyPath := h.Key
clientCAPath := h.ClientCA

mtls := (clientCAPath != "")

if _, err := os.Stat(serverCertPath); os.IsNotExist(err) {
return nil, fmt.Errorf("server cert path '%s': %w", serverCertPath, err)
}
if _, err := os.Stat(serverKeyPath); os.IsNotExist(err) {
return nil, fmt.Errorf("server key path '%s': %w", serverKeyPath, err)
}

// Create a CA certificate pool and add cert.pem to it
serverCert, err := os.ReadFile(serverCertPath)
if err != nil {
return nil, fmt.Errorf("server ca pool error: %w", err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(serverCert)

if mtls {
// add mTLS CA path to cert pool as well
if _, err := os.Stat(clientCAPath); os.IsNotExist(err) {
return nil, fmt.Errorf("server file does not exist %s", clientCAPath)
}
clientCA, err := os.ReadFile(clientCAPath)
if err != nil {
return nil, fmt.Errorf("server: could not read file %s: %w", clientCAPath, err)
}
caCertPool.AppendCertsFromPEM(clientCA)
}

// Create the TLS Config with the CA pool and enable Client certificate validation
tlsConfig := &tls.Config{
ClientCAs: caCertPool,
}

if mtls {
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
//tlsConfig.BuildNameToCertificate()

return tlsConfig, nil
}

/* Plugin types */
type pluginDataStoreSQL struct {
Drivername string `hcl:"drivername"`
Filename string `hcl:"filename"`
}

type pluginAuthenticatorKeycloak struct {
IssuerURL string `hcl:"issuer"`
Audience string `hcl:"audience"`
}

type AuthRole struct {
Name string `hcl:",key"`
Desc string `hcl:"desc"`
}

type APIv1RoleMapping struct {
Name string `hcl:",key"`
Method string `hcl:"-"`
Path string `hcl:"-"`
AllowedRoles []string `hcl:"allowed_roles"`
}

type pluginAuthorizerRBAC struct {
Name string `hcl:"name"`
RoleList []*AuthRole `hcl:"role,block"`
APIv1RoleMappings []*APIv1RoleMapping `hcl:"APIv1,block"`
}
package api

import (
"crypto/tls"
"crypto/x509"
"fmt"
"os"

"github.com/hashicorp/hcl/hcl/ast"
)

// TornjakServerInfo provides insight into the configuration of the SPIRE server
// where the Tornjak Agent resides
type TornjakSpireServerInfo struct {
// Plugins is a map from plugin types to respective names of plugins configured
Plugins map[string][]string `json:"plugins"`
// TrustDomain specifies the trust domain of the SPIRE server configured with tornjak
TrustDomain string `json:"trustDomain"`
// Verbose config contains unstructure information on the config on the agent
VerboseConfig string `json:"verboseConfig"`
}

// pared down version of full Server Config type spire/cmd/spire-server/cli/run
// we curently need only extract the trust domain
type SpireServerConfig struct {
TrustDomain string `hcl:"trust_domain"`
}

type SPIREConfig struct {
Server *SpireServerConfig `hcl:"server"`
Plugins ast.Node `hcl:"plugins"`
}

type TornjakConfig struct {
Server *serverConfig `hcl:"server"`
Plugins *ast.Node `hcl:"plugins"`
}

/* Server configuration*/

type serverConfig struct {
SPIRESocket string `hcl:"spire_socket_path"`
HTTPConfig *HTTPConfig `hcl:"http"`
HTTPSConfig *HTTPSConfig `hcl:"https"`
}

type HTTPConfig struct {
ListenPort int `hcl:"port"`
}

type HTTPSConfig struct {
ListenPort int `hcl:"port"`
Cert string `hcl:"cert"`
Key string `hcl:"key"`
ClientCA string `hcl:"client_ca"`
}

func (h HTTPSConfig) Parse() (*tls.Config, error) {
serverCertPath := h.Cert
serverKeyPath := h.Key
clientCAPath := h.ClientCA

mtls := (clientCAPath != "")

if _, err := os.Stat(serverCertPath); os.IsNotExist(err) {
return nil, fmt.Errorf("server cert path '%s': %w", serverCertPath, err)
}
if _, err := os.Stat(serverKeyPath); os.IsNotExist(err) {
return nil, fmt.Errorf("server key path '%s': %w", serverKeyPath, err)
}

// Create a CA certificate pool and add cert.pem to it
serverCert, err := os.ReadFile(serverCertPath)
if err != nil {
return nil, fmt.Errorf("server ca pool error: %w", err)
}
caCertPool := x509.NewCertPool()
caCertPool.AppendCertsFromPEM(serverCert)

if mtls {
// add mTLS CA path to cert pool as well
if _, err := os.Stat(clientCAPath); os.IsNotExist(err) {
return nil, fmt.Errorf("server file does not exist %s", clientCAPath)
}
clientCA, err := os.ReadFile(clientCAPath)
if err != nil {
return nil, fmt.Errorf("server: could not read file %s: %w", clientCAPath, err)
}
caCertPool.AppendCertsFromPEM(clientCA)
}

// Create the TLS Config with the CA pool and enable Client certificate validation
tlsConfig := &tls.Config{
ClientCAs: caCertPool,
}

if mtls {
tlsConfig.ClientAuth = tls.RequireAndVerifyClientCert
}
//tlsConfig.BuildNameToCertificate()

return tlsConfig, nil
}

/* Plugin types */
type pluginDataStoreSQL struct {
Drivername string `hcl:"drivername"`
Filename string `hcl:"filename"`
}

type pluginControllerManager struct {
Classname string `hcl:"classname"`
}

type pluginAuthenticatorKeycloak struct {
IssuerURL string `hcl:"issuer"`
Audience string `hcl:"audience"`
}

type AuthRole struct {
Name string `hcl:",key"`
Desc string `hcl:"desc"`
}

type APIv1RoleMapping struct {
Name string `hcl:",key"`
Method string `hcl:"-"`
Path string `hcl:"-"`
AllowedRoles []string `hcl:"allowed_roles"`
}

type pluginAuthorizerRBAC struct {
Name string `hcl:"name"`
RoleList []*AuthRole `hcl:"role,block"`
APIv1RoleMappings []*APIv1RoleMapping `hcl:"APIv1,block"`
}
Loading

0 comments on commit 71a03a4

Please sign in to comment.