Encrypt connector sensitive data

Add database field level encryption level to sensitive data to reduce
the risk in term of data leak. I dedice to have only one key for now in
a near future I may move to one master key and one encryption key per
organization to make rotation easiest.

I don't use built-in pg_crypto function to have clear seperation and
avoid any encryption key leak.

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2025-04-20 11:11:39 -07:00
parent 34de088ec7
commit 44a05be1af
9 changed files with 225 additions and 89 deletions

View File

@@ -23,26 +23,26 @@ import (
type (
connectorConfig struct {
Name string `json:"name"`
Type string `json:"type"`
Config connector.Connector `json:"-"`
Name string `json:"name"`
Type connector.ProtocolType `json:"type"`
Config connector.Connector `json:"-"`
}
connectorOAuth2Config struct {
connectorConfigOAuth2 struct {
ClientID string `json:"client-id"`
ClientSecret string `json:"client-secret"`
RedirectURI string `json:"redirect-uri"`
Scopes []string `json:"scopes"`
AuthURL string `json:"auth-url"`
TokenURL string `json:"token-url"`
Scopes []string `json:"scopes"`
}
)
func (c *connectorConfig) UnmarshalJSON(data []byte) error {
var tmp struct {
Name string `json:"name"`
Type string `json:"type"`
RawConfig json.RawMessage `json:"config"`
Name string `json:"name"`
Type connector.ProtocolType `json:"type"`
RawConfig json.RawMessage `json:"config"`
}
if err := json.Unmarshal(data, &tmp); err != nil {
@@ -53,26 +53,24 @@ func (c *connectorConfig) UnmarshalJSON(data []byte) error {
c.Type = tmp.Type
switch tmp.Type {
case "oauth2":
var cfg connectorOAuth2Config
if err := json.Unmarshal(tmp.RawConfig, &cfg); err != nil {
return fmt.Errorf("cannot unmarshal oauth2 config: %w", err)
case connector.ProtocolOAuth2:
var config connectorConfigOAuth2
if err := json.Unmarshal(tmp.RawConfig, &config); err != nil {
return fmt.Errorf("cannot unmarshal oauth2 connector config: %w", err)
}
if cfg.ClientID == "" || cfg.ClientSecret == "" || cfg.AuthURL == "" || cfg.TokenURL == "" || cfg.RedirectURI == "" {
return fmt.Errorf("oauth2 config: client-id, client-secret, auth-url, token-url and redirect-uri are required")
oauth2Connector := connector.OAuth2Connector{
ClientID: config.ClientID,
ClientSecret: config.ClientSecret,
RedirectURI: config.RedirectURI,
AuthURL: config.AuthURL,
TokenURL: config.TokenURL,
Scopes: config.Scopes,
}
c.Config = &connector.OAuth2Connector{
ClientID: cfg.ClientID,
ClientSecret: cfg.ClientSecret,
RedirectURI: cfg.RedirectURI,
Scopes: cfg.Scopes,
AuthURL: cfg.AuthURL,
TokenURL: cfg.TokenURL,
}
c.Config = &oauth2Connector
default:
return fmt.Errorf("unknown %q connector type: %s", tmp.Name, tmp.Type)
return fmt.Errorf("unknown connector type: %q", tmp.Type)
}
return nil

View File

@@ -27,6 +27,7 @@ import (
"github.com/getprobo/probo/pkg/awsconfig"
"github.com/getprobo/probo/pkg/connector"
"github.com/getprobo/probo/pkg/coredata"
"github.com/getprobo/probo/pkg/crypto/cipher"
"github.com/getprobo/probo/pkg/crypto/passwdhash"
"github.com/getprobo/probo/pkg/mailer"
"github.com/getprobo/probo/pkg/probo"
@@ -49,13 +50,14 @@ type (
}
config struct {
Hostname string `json:"hostname"`
Pg pgConfig `json:"pg"`
Api apiConfig `json:"api"`
Auth authConfig `json:"auth"`
AWS awsConfig `json:"aws"`
Mailer mailerConfig `json:"mailer"`
Connectors []connectorConfig `json:"connectors"`
Hostname string `json:"hostname"`
EncryptionKey cipher.EncryptionKey `json:"encryption-key"`
Pg pgConfig `json:"pg"`
Api apiConfig `json:"api"`
Auth authConfig `json:"auth"`
AWS awsConfig `json:"aws"`
Mailer mailerConfig `json:"mailer"`
Connectors []connectorConfig `json:"connectors"`
}
)
@@ -142,7 +144,6 @@ func (impl *Implm) Run(
return fmt.Errorf("cannot get pepper bytes: %w", err)
}
// Validate cookie secret
_, err = impl.cfg.Auth.GetCookieSecretBytes()
if err != nil {
rootSpan.RecordError(err)
@@ -195,7 +196,7 @@ func (impl *Implm) Run(
return fmt.Errorf("cannot create usrmgr service: %w", err)
}
proboService, err := probo.NewService(ctx, pgClient, s3Client, impl.cfg.AWS.Bucket)
proboService, err := probo.NewService(ctx, impl.cfg.EncryptionKey, pgClient, s3Client, impl.cfg.AWS.Bucket)
if err != nil {
return fmt.Errorf("cannot create probo service: %w", err)
}