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

Separate TLS config for AcraConnector and DB #419

Merged
merged 12 commits into from Sep 28, 2020
7 changes: 7 additions & 0 deletions CHANGELOG_DEV.md
@@ -1,3 +1,10 @@
## 0.85.0 - 2020-09-28

### Added

- New CA configuration options: `tls_ca_client` and `tls_ca_database` (more specific than `tls_ca`)
- New TLS auth configuration options: `tls_auth_client` and `tls_auth_database` (more specific than `tls_auth`)

## 0.85.0 - 2020-04-02
### Added
- Support of RHEL >= 7
Expand Down
43 changes: 34 additions & 9 deletions cmd/acra-server/acra-server.go
Expand Up @@ -73,6 +73,8 @@ const (
// defaultConfigPath relative path to config which will be parsed as default
var defaultConfigPath = utils.GetConfigPathByName(ServiceName)

const tlsAuthNotSet = -1

func main() {
loggingFormat := flag.String("logging_format", "plaintext", "Logging format: plaintext, json or CEF")
dbHost := flag.String("db_host", "", "Host to db")
Expand Down Expand Up @@ -108,9 +110,13 @@ func main() {
useTLS := flag.Bool("acraconnector_tls_transport_enable", false, "Use tls to encrypt transport between AcraServer and AcraConnector/client")
tlsKey := flag.String("tls_key", "", "Path to private key that will be used in AcraServer's TLS handshake with AcraConnector as server's key and database as client's key")
tlsCert := flag.String("tls_cert", "", "Path to tls certificate")
tlsCA := flag.String("tls_ca", "", "Path to root certificate which will be used with system root certificates to validate Postgresql's and AcraConnector's certificate")
tlsCA := flag.String("tls_ca", "", "Path to additional CA certificate for AcraConnector and database certificate validation")
tlsClientCA := flag.String("tls_ca_client", "", "Path to additional CA certificate for AcraConnector certificate validation (if different from database CA)")
ilammy marked this conversation as resolved.
Show resolved Hide resolved
tlsDbCA := flag.String("tls_ca_database", "", "Path to additional CA certificate for database certificate validation (if different from AcraConnector CA)")
ilammy marked this conversation as resolved.
Show resolved Hide resolved
tlsDbSNI := flag.String("tls_db_sni", "", "Expected Server Name (SNI) from database")
tlsAuthType := flag.Int("tls_auth", int(tls.RequireAndVerifyClientCert), "Set authentication mode that will be used in TLS connection with AcraConnector. Values in range 0-4 that set auth type (https://golang.org/pkg/crypto/tls/#ClientAuthType). Default is tls.RequireAndVerifyClientCert")
tlsAuthType := flag.Int("tls_auth", int(tls.RequireAndVerifyClientCert), "Set authentication mode that will be used in TLS connection with AcraConnector and database. Values in range 0-4 that set auth type (https://golang.org/pkg/crypto/tls/#ClientAuthType). Default is tls.RequireAndVerifyClientCert")
tlsClientAuthType := flag.Int("tls_auth_client", tlsAuthNotSet, "Set authentication mode that will be used in TLS connection with AcraConnector. Overrides the \"tls_auth\" setting.")
tlsDbAuthType := flag.Int("tls_auth_database", tlsAuthNotSet, "Set authentication mode that will be used in TLS connection with database. Overrides the \"tls_auth\" setting.")
noEncryptionTransport := flag.Bool("acraconnector_transport_encryption_disable", false, "Use raw transport (tcp/unix socket) between AcraServer and AcraConnector/client (don't use this flag if you not connect to database with SSL/TLS")
clientID := flag.String("client_id", "", "Expected client ID of AcraConnector in mode without encryption")
acraConnectionString := flag.String("incoming_connection_string", network.BuildConnectionString(cmd.DefaultAcraServerConnectionProtocol, cmd.DefaultAcraServerHost, cmd.DefaultAcraServerPort, ""), "Connection string like tcp://x.x.x.x:yyyy or unix:///path/to/socket")
Expand Down Expand Up @@ -215,19 +221,38 @@ func main() {
log.Infof("Keystore init OK")

log.Infof("Configuring transport...")
var tlsConfig *tls.Config
var clientTLSConfig, dbTLSConfig *tls.Config
if *useTLS || *tlsKey != "" {
tlsConfig, err = network.NewTLSConfig(network.SNIOrHostname(*tlsDbSNI, *dbHost), *tlsCA, *tlsKey, *tlsCert, tls.ClientAuthType(*tlsAuthType))
// Use common CA and auth settings, unless the user requests specific ones
if *tlsClientCA == "" {
*tlsClientCA = *tlsCA
}
if *tlsDbCA == "" {
*tlsDbCA = *tlsCA
}
if *tlsClientAuthType == tlsAuthNotSet {
*tlsClientAuthType = *tlsAuthType
}
if *tlsDbAuthType == tlsAuthNotSet {
*tlsDbAuthType = *tlsAuthType
}
clientTLSConfig, err = network.NewTLSConfig(network.SNIOrHostname(*tlsDbSNI, *dbHost), *tlsClientCA, *tlsKey, *tlsCert, tls.ClientAuthType(*tlsClientAuthType))
if err != nil {
log.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTransportConfiguration).
Errorln("Configuration error: can't create AcraConnector TLS config")
os.Exit(1)
}
dbTLSConfig, err = network.NewTLSConfig(network.SNIOrHostname(*tlsDbSNI, *dbHost), *tlsDbCA, *tlsKey, *tlsCert, tls.ClientAuthType(*tlsDbAuthType))
if err != nil {
log.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTransportConfiguration).
Errorln("Configuration error: can't get config for TLS")
Errorln("Configuration error: can't create database TLS config")
os.Exit(1)
}
log.Infoln("Loaded tls config")
log.Infoln("Loaded TLS configuration")
}
if *useTLS {
log.Println("Selecting transport: use TLS transport wrapper")
config.ConnectionWrapper, err = network.NewTLSConnectionWrapper([]byte(*clientID), tlsConfig)
config.ConnectionWrapper, err = network.NewTLSConnectionWrapper([]byte(*clientID), clientTLSConfig)
if err != nil {
log.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorTransportConfiguration).
Errorln("Configuration error: can't initialise TLS connection wrapper")
Expand Down Expand Up @@ -285,15 +310,15 @@ func main() {
var proxyFactory base.ProxyFactory
if *useMysql {
decryptorFactory = mysql.NewMysqlDecryptorFactory(decryptorSetting)
proxyFactory, err = mysql.NewProxyFactory(base.NewProxySetting(decryptorFactory, config.GetTableSchema(), keyStore, tlsConfig, config.GetCensor()))
proxyFactory, err = mysql.NewProxyFactory(base.NewProxySetting(decryptorFactory, config.GetTableSchema(), keyStore, clientTLSConfig, dbTLSConfig, config.GetCensor()))
if err != nil {
log.WithError(err).Errorln("Can't initialize proxy for connections")
os.Exit(1)
}
sqlparser.SetDefaultDialect(mysqlDialect.NewMySQLDialect())
} else {
decryptorFactory = postgresql.NewDecryptorFactory(decryptorSetting)
proxyFactory, err = postgresql.NewProxyFactory(base.NewProxySetting(decryptorFactory, config.GetTableSchema(), keyStore, tlsConfig, config.GetCensor()))
proxyFactory, err = postgresql.NewProxyFactory(base.NewProxySetting(decryptorFactory, config.GetTableSchema(), keyStore, clientTLSConfig, dbTLSConfig, config.GetCensor()))
if err != nil {
log.WithError(err).Errorln("Can't initialize proxy for connections")
os.Exit(1)
Expand Down
16 changes: 14 additions & 2 deletions configs/acra-server.yaml
Expand Up @@ -113,12 +113,24 @@ postgresql_enable: false
# Id that will be sent in secure session
securesession_id: acra_server

# Set authentication mode that will be used in TLS connection with AcraConnector. Values in range 0-4 that set auth type (https://golang.org/pkg/crypto/tls/#ClientAuthType). Default is tls.RequireAndVerifyClientCert
# Set authentication mode that will be used in TLS connection with AcraConnector and database. Values in range 0-4 that set auth type (https://golang.org/pkg/crypto/tls/#ClientAuthType). Default is tls.RequireAndVerifyClientCert
tls_auth: 4

# Path to root certificate which will be used with system root certificates to validate Postgresql's and AcraConnector's certificate
# Set authentication mode that will be used in TLS connection with AcraConnector. Overrides the "tls_auth" setting.
tls_auth_client: -1

# Set authentication mode that will be used in TLS connection with database. Overrides the "tls_auth" setting.
tls_auth_database: -1

# Path to additional CA certificate for AcraConnector and database certificate validation
tls_ca:

# Path to additional CA certificate for AcraConnector certificate validation (if different from database CA)
tls_ca_client:

# Path to additional CA certificate for database certificate validation (if different from AcraConnector CA)
tls_ca_database:

# Path to tls certificate
tls_cert:

Expand Down
21 changes: 14 additions & 7 deletions decryptor/base/proxy.go
Expand Up @@ -30,15 +30,17 @@ import (
type ProxySetting interface {
KeyStore() keystore.DecryptionKeyStore
TableSchemaStore() config.TableSchemaStore
TLSConfig() *tls.Config
ClientTLSConfig() *tls.Config
DatabaseTLSConfig() *tls.Config
Censor() acracensor.AcraCensorInterface
DecryptorFactory() DecryptorFactory
}

type proxySetting struct {
keystore keystore.DecryptionKeyStore
tableSchemaStore config.TableSchemaStore
tlsConfig *tls.Config
clientTLSConfig *tls.Config
dbTLSConfig *tls.Config
censor acracensor.AcraCensorInterface
decryptorFactory DecryptorFactory
}
Expand All @@ -53,9 +55,14 @@ func (p *proxySetting) Censor() acracensor.AcraCensorInterface {
return p.censor
}

// TLSConfig return tls.Config
func (p *proxySetting) TLSConfig() *tls.Config {
return p.tlsConfig
// ClientTLSConfig return tls.Config to use for database connection, if any.
func (p *proxySetting) ClientTLSConfig() *tls.Config {
return p.clientTLSConfig
}

// DatabaseTLSConfig return tls.Config to use for database connection, if any.
func (p *proxySetting) DatabaseTLSConfig() *tls.Config {
return p.dbTLSConfig
}

// TableSchemaStore return table schema store
Expand All @@ -69,8 +76,8 @@ func (p *proxySetting) KeyStore() keystore.DecryptionKeyStore {
}

// NewProxySetting return new ProxySetting implementation with data from params
func NewProxySetting(decryptorFactory DecryptorFactory, tableSchema config.TableSchemaStore, keystore keystore.DecryptionKeyStore, tlsConfig *tls.Config, censor acracensor.AcraCensorInterface) ProxySetting {
return &proxySetting{keystore: keystore, tableSchemaStore: tableSchema, tlsConfig: tlsConfig, censor: censor, decryptorFactory: decryptorFactory}
func NewProxySetting(decryptorFactory DecryptorFactory, tableSchema config.TableSchemaStore, keystore keystore.DecryptionKeyStore, clientTLSConfig, dbTLSConfig *tls.Config, censor acracensor.AcraCensorInterface) ProxySetting {
return &proxySetting{keystore: keystore, tableSchemaStore: tableSchema, clientTLSConfig: clientTLSConfig, dbTLSConfig: dbTLSConfig, censor: censor, decryptorFactory: decryptorFactory}
}

// Proxy interface to process client's requests to database and responses
Expand Down
2 changes: 1 addition & 1 deletion decryptor/mysql/proxy.go
Expand Up @@ -46,7 +46,7 @@ func (factory *proxyFactory) New(ctx context.Context, clientID []byte, dbConnect
if err != nil {
return nil, err
}
proxy, err := NewMysqlProxy(ctx, decryptor, dbConnection, clientConnection, factory.setting.TLSConfig(), factory.setting.Censor())
proxy, err := NewMysqlProxy(ctx, decryptor, dbConnection, clientConnection, factory.setting)
if err != nil {
return nil, err
}
Expand Down
4 changes: 2 additions & 2 deletions decryptor/mysql/proxy_test.go
Expand Up @@ -26,7 +26,7 @@ func (store *tableSchemaStore) IsEmpty() bool {
func TestEncryptorTurnOnOff(t *testing.T) {
emptyStore := &tableSchemaStore{true}
nonEmptyStore := &tableSchemaStore{false}
setting := base.NewProxySetting(&decryptorFactory{}, emptyStore, nil, nil, nil)
setting := base.NewProxySetting(&decryptorFactory{}, emptyStore, nil, nil, nil, nil)
proxyFactory, err := NewProxyFactory(setting)
if err != nil {
t.Fatal(setting)
Expand All @@ -39,7 +39,7 @@ func TestEncryptorTurnOnOff(t *testing.T) {
t.Fatal("Unexpected observers count")
}

setting = base.NewProxySetting(&decryptorFactory{}, nonEmptyStore, nil, nil, nil)
setting = base.NewProxySetting(&decryptorFactory{}, nonEmptyStore, nil, nil, nil, nil)
proxyFactory, err = NewProxyFactory(setting)
if err != nil {
t.Fatal(setting)
Expand Down
31 changes: 18 additions & 13 deletions decryptor/mysql/response_proxy.go
Expand Up @@ -151,21 +151,16 @@ type Handler struct {
dbTLSHandshakeFinished chan bool
clientConnection net.Conn
dbConnection net.Conn
tlsConfig *tls.Config
clientTLSConfig *tls.Config
dbTLSConfig *tls.Config
logger *logrus.Entry
ctx context.Context
queryObserverManager base.QueryObserverManager
decryptionObserver base.ColumnDecryptionObserver
}

// NewMysqlProxy returns new Handler
func NewMysqlProxy(ctx context.Context, decryptor base.Decryptor, dbConnection, clientConnection net.Conn, tlsConfig *tls.Config, censor acracensor.AcraCensorInterface) (*Handler, error) {
var newTLSConfig *tls.Config
if tlsConfig != nil {
// use less secure protocol versions because some drivers and db images doesn't support secure and modern options
newTLSConfig = tlsConfig.Clone()
network.SetMySQLCompatibleTLSSettings(newTLSConfig)
}
func NewMysqlProxy(ctx context.Context, decryptor base.Decryptor, dbConnection, clientConnection net.Conn, setting base.ProxySetting) (*Handler, error) {
observerManager, err := base.NewArrayQueryObserverableManager(ctx)
if err != nil {
return nil, err
Expand All @@ -176,17 +171,27 @@ func NewMysqlProxy(ctx context.Context, decryptor base.Decryptor, dbConnection,
clientDeprecateEOF: false,
decryptor: decryptor,
responseHandler: defaultResponseHandler,
acracensor: censor,
acracensor: setting.Censor(),
clientConnection: clientConnection,
dbConnection: dbConnection,
tlsConfig: newTLSConfig,
clientTLSConfig: tweakTLSConfigForMySQL(setting.ClientTLSConfig()),
dbTLSConfig: tweakTLSConfigForMySQL(setting.DatabaseTLSConfig()),
ctx: ctx,
logger: logging.GetLoggerFromContext(ctx),
queryObserverManager: observerManager,
decryptionObserver: base.NewColumnDecryptionObserver(),
}, nil
}

func tweakTLSConfigForMySQL(config *tls.Config) *tls.Config {
vixentael marked this conversation as resolved.
Show resolved Hide resolved
if config != nil {
// use less secure protocol versions because some drivers and db images doesn't support secure and modern options
config = config.Clone()
network.SetMySQLCompatibleTLSSettings(config)
}
return config
}

// SubscribeOnColumnDecryption subscribes for OnColumn notifications about the column, indexed from left to right starting with zero.
func (handler *Handler) SubscribeOnColumnDecryption(i int, subscriber base.DecryptionSubscriber) {
handler.decryptionObserver.SubscribeOnColumnDecryption(i, subscriber)
Expand Down Expand Up @@ -273,7 +278,7 @@ func (handler *Handler) ProxyClientConnection(errCh chan<- error) {
handler.clientDeprecateEOF = packet.IsClientDeprecateEOF()
clientLog = clientLog.WithField("deprecate_eof", handler.clientDeprecateEOF)
if packet.IsSSLRequest() {
if handler.tlsConfig == nil {
if handler.clientTLSConfig == nil {
handler.logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantInitializeTLS).Errorln("To support TLS connections you must pass TLS key and certificate for AcraServer that will be used " +
"for connections AcraServer->Database and CA certificate which will be used to verify certificate " +
"from database")
Expand All @@ -287,7 +292,7 @@ func (handler *Handler) ProxyClientConnection(errCh chan<- error) {
errCh <- network.ErrEmptyTLSConfig
return
}
tlsConnection := tls.Server(handler.clientConnection, handler.tlsConfig)
tlsConnection := tls.Server(handler.clientConnection, handler.clientTLSConfig)
if err := tlsConnection.Handshake(); err != nil {
handler.logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantInitializeTLS).
Errorln("Error in tls handshake with client")
Expand Down Expand Up @@ -871,7 +876,7 @@ func (handler *Handler) ProxyDatabaseConnection(errCh chan<- error) {
if netErr.Timeout() && handler.isTLSHandshake {
// reset deadline
handler.dbConnection.SetReadDeadline(time.Time{})
tlsConnection := tls.Client(handler.dbConnection, handler.tlsConfig)
tlsConnection := tls.Client(handler.dbConnection, handler.dbTLSConfig)
if err = tlsConnection.Handshake(); err != nil {
handler.logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantInitializeTLS).
Errorln("Error in tls handshake with db")
Expand Down
20 changes: 11 additions & 9 deletions decryptor/postgresql/pg_decryptor.go
Expand Up @@ -93,15 +93,16 @@ type PgProxy struct {
TLSCh chan bool
ctx context.Context
queryObserverManager base.QueryObserverManager
tlsConfig *tls.Config
clientTLSConfig *tls.Config
dbTLSConfig *tls.Config
censor acracensor.AcraCensorInterface
decryptor base.Decryptor
tlsSwitch bool
decryptionObserver base.ColumnDecryptionObserver
}

// NewPgProxy returns new PgProxy
func NewPgProxy(ctx context.Context, decryptor base.Decryptor, dbConnection, clientConnection net.Conn, tlsConfig *tls.Config, censor acracensor.AcraCensorInterface) (*PgProxy, error) {
func NewPgProxy(ctx context.Context, decryptor base.Decryptor, dbConnection, clientConnection net.Conn, setting base.ProxySetting) (*PgProxy, error) {
observerManager, err := base.NewArrayQueryObserverableManager(ctx)
if err != nil {
return nil, err
Expand All @@ -112,8 +113,9 @@ func NewPgProxy(ctx context.Context, decryptor base.Decryptor, dbConnection, cli
TLSCh: make(chan bool),
ctx: ctx,
queryObserverManager: observerManager,
tlsConfig: tlsConfig,
censor: censor,
clientTLSConfig: setting.ClientTLSConfig(),
dbTLSConfig: setting.DatabaseTLSConfig(),
censor: setting.Censor(),
decryptor: decryptor,
decryptionObserver: base.NewColumnDecryptionObserver(),
}, nil
Expand Down Expand Up @@ -346,9 +348,9 @@ func checkWholePoisonRecord(block []byte, decryptor base.Decryptor, logger *log.
}

// handleSSLRequest return wrapped with tls (client's, db's connections, nil) or (nil, nil, error)
func (proxy *PgProxy) handleSSLRequest(packet *PacketHandler, tlsConfig *tls.Config, logger *log.Entry) (net.Conn, net.Conn, error) {
func (proxy *PgProxy) handleSSLRequest(packet *PacketHandler, logger *log.Entry) (net.Conn, net.Conn, error) {
// if server allow SSLRequest than we wrap our connections with tls
if tlsConfig == nil {
if proxy.dbTLSConfig == nil {
logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantInitializeTLS).Errorln("To support TLS connections you must pass TLS key and certificate for AcraServer that will be used " +
"for connections AcraServer->Database and CA certificate which will be used to verify certificate " +
"from database")
Expand Down Expand Up @@ -378,7 +380,7 @@ func (proxy *PgProxy) handleSSLRequest(packet *PacketHandler, tlsConfig *tls.Con
}
logger.Debugln("Init tls with client")
// convert to tls connection
tlsClientConnection := tls.Server(proxy.clientConnection, tlsConfig)
tlsClientConnection := tls.Server(proxy.clientConnection, proxy.clientTLSConfig)

// send server's response only after successful interrupting background goroutine that process client's connection
// to take control over connection and avoid two places that communicate with one connection
Expand All @@ -394,7 +396,7 @@ func (proxy *PgProxy) handleSSLRequest(packet *PacketHandler, tlsConfig *tls.Con
}

logger.Debugln("Init tls with db")
dbTLSConnection := tls.Client(proxy.dbConnection, tlsConfig)
dbTLSConnection := tls.Client(proxy.dbConnection, proxy.dbTLSConfig)
if err := dbTLSConnection.Handshake(); err != nil {
logger.WithError(err).WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantInitializeTLS).
Errorln("Can't initialize tls connection with db")
Expand Down Expand Up @@ -467,7 +469,7 @@ func (proxy *PgProxy) ProxyDatabaseConnection(errCh chan<- error) {
//firstByte = true
continue
} else if packetHandler.IsSSLRequestAllowed() {
tlsClientConnection, dbTLSConnection, err := proxy.handleSSLRequest(packetHandler, proxy.tlsConfig, logger)
tlsClientConnection, dbTLSConnection, err := proxy.handleSSLRequest(packetHandler, logger)
if err != nil {
logger.WithField(logging.FieldKeyEventCode, logging.EventCodeErrorDecryptorCantInitializeTLS).WithError(err).Errorln("Can't process SSL request")
errCh <- err
Expand Down
2 changes: 1 addition & 1 deletion decryptor/postgresql/proxy.go
Expand Up @@ -42,7 +42,7 @@ func (factory *proxyFactory) New(ctx context.Context, clientID []byte, dbConnect
return nil, err
}
decryptor.SetDataProcessor(base.DecryptProcessor{})
proxy, err := NewPgProxy(ctx, decryptor, dbConnection, clientConnection, factory.setting.TLSConfig(), factory.setting.Censor())
proxy, err := NewPgProxy(ctx, decryptor, dbConnection, clientConnection, factory.setting)
if err != nil {
return nil, err
}
Expand Down