Skip to content

buildTLSConfig Shared + Conditional CA Validation #123

Description

@rosanemelo

T-1: buildTLSConfig Shared + Conditional CA Validation

Description

Create shared function for building tls.Config from in-memory CA PEM and make CA validation conditional in the connection model. This function will be the reusable base for all factories (MySQL, SQL Server, Oracle).

Delivered Value

Reusable base for all factories + users are no longer required to provide CA when unnecessary.

Files to Create/Modify

File Action Description
pkg/datasource/tlsutil/tlsconfig.go Create Shared BuildTLSConfig function
pkg/datasource/tlsutil/tlsconfig_test.go Create Unit tests
pkg/model/connection.go Modify Conditional CA validation
pkg/model/connection_test.go Modify Tests for conditional validation

Implementation

tlsconfig.go:

package tlsutil

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

// BuildTLSConfig cria tls.Config a partir de PEM strings in-memory.
// caCertPEM: CA certificate (obrigatorio).
// serverName: hostname para validacao (pode ser vazio).
// clientCertPEM, clientKeyPEM: client certificate para mTLS (opcionais, devem ser fornecidos juntos).
func BuildTLSConfig(caCertPEM string, serverName string, clientCertPEM string, clientKeyPEM string) (*tls.Config, error) {
    if caCertPEM == "" {
        return nil, fmt.Errorf("CA certificate PEM content is empty")
    }

    certPool := x509.NewCertPool()
    if ok := certPool.AppendCertsFromPEM([]byte(caCertPEM)); !ok {
        return nil, fmt.Errorf("failed to parse CA certificate PEM (length=%d)", len(caCertPEM))
    }

    tlsConfig := &tls.Config{
        RootCAs:    certPool,
        MinVersion: tls.VersionTLS12,
    }

    if serverName != "" {
        tlsConfig.ServerName = serverName
    }

    // mTLS: client certificate (Cert e Key devem ser fornecidos juntos)
    if clientCertPEM != "" || clientKeyPEM != "" {
        if clientCertPEM == "" || clientKeyPEM == "" {
            return nil, fmt.Errorf("client certificate and key must be provided together for mTLS")
        }

        clientCert, err := tls.X509KeyPair([]byte(clientCertPEM), []byte(clientKeyPEM))
        if err != nil {
            return nil, fmt.Errorf("failed to parse client certificate/key PEM: %w", err)
        }

        tlsConfig.Certificates = []tls.Certificate{clientCert}
    }

    return tlsConfig, nil
}

Conditional validation in connection.go:

func (conn *Connection) validateSSLRequiredFields(requiredFields map[string]string) {
    if conn.SSL == nil {
        return
    }

    if conn.SSL.Mode == "" {
        requiredFields["ssl.mode"] = "SSL mode is required"
    }

    // CA e opcional - quando fornecido, e utilizado pela factory.
    // Quando nao fornecido, factory usa comportamento default do driver
    // (system CA store ou skip verify dependendo do modo).

    // mTLS: Cert e Key devem ser fornecidos juntos
    hasCert := conn.SSL.Cert != nil && *conn.SSL.Cert != ""
    hasKey := conn.SSL.Key != nil && *conn.SSL.Key != ""
    if hasCert && !hasKey {
        requiredFields["ssl.key"] = "SSL key is required when cert is provided (mTLS)"
    }
    if hasKey && !hasCert {
        requiredFields["ssl.cert"] = "SSL cert is required when key is provided (mTLS)"
    }
}

Dependencies

  • None (first task)

Estimate

  • Size: S (small)
  • Complexity: Low

Acceptance Criteria

  • BuildTLSConfig creates valid tls.Config from CA PEM string
  • BuildTLSConfig returns descriptive error for invalid PEM
  • BuildTLSConfig sets MinVersion: tls.VersionTLS12
  • BuildTLSConfig with Cert+Key: includes client certificate in tls.Config.Certificates (mTLS)
  • BuildTLSConfig with Cert without Key (or vice-versa): returns "must be provided together" error
  • BuildTLSConfig with invalid Cert+Key: returns descriptive error
  • CA validation removed as mandatory in validateSSLRequiredFields
  • Validation: if Cert provided, Key mandatory (and vice-versa)
  • Existing connection tests still pass (backward compatibility)
  • Unit tests cover: valid CA, invalid CA, empty CA, with/without serverName, valid mTLS, incomplete mTLS

Test Strategy

  • Unit tests for BuildTLSConfig
  • Updated unit tests for conditional validation
  • Verify existing connection_test.go tests don't break

Phase 2 - Implementation (parallel)


Task gerada pelo Floki


Jira: FET-144
Link: https://lerian.atlassian.net/browse/FET-144

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions