LDAP: Allow setting minimum TLS version and accepted ciphers (#63646)
* update ldap library and use go module path * add TLS min version and accepted min TLS version * set default min ver to library default * set default min ver to library default * add cipher list to toml * Update pkg/services/ldap/settings.go Co-authored-by: Karl Persson <kalle.persson@grafana.com> * Apply suggestions from code review Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com> * lint --------- Co-authored-by: Karl Persson <kalle.persson@grafana.com> Co-authored-by: Christopher Moyer <35463610+chri2547@users.noreply.github.com>
This commit is contained in:
@@ -3,7 +3,7 @@ package ldap
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"gopkg.in/ldap.v3"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
)
|
||||
|
||||
func IsMemberOf(memberOf []string, group string) bool {
|
||||
|
||||
@@ -12,7 +12,7 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"gopkg.in/ldap.v3"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
@@ -130,6 +130,8 @@ func (server *Server) Dial() error {
|
||||
InsecureSkipVerify: server.Config.SkipVerifySSL,
|
||||
ServerName: host,
|
||||
RootCAs: certPool,
|
||||
MinVersion: server.Config.minTLSVersion,
|
||||
CipherSuites: server.Config.tlsCiphers,
|
||||
}
|
||||
if len(clientCert.Certificate) > 0 {
|
||||
tlsCfg.Certificates = append(tlsCfg.Certificates, clientCert)
|
||||
|
||||
@@ -4,8 +4,8 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"gopkg.in/ldap.v3"
|
||||
)
|
||||
|
||||
func TestIsMemberOf(t *testing.T) {
|
||||
|
||||
@@ -4,9 +4,9 @@ import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/ldap.v3"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
|
||||
@@ -3,9 +3,9 @@ package ldap
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/ldap.v3"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/services/login"
|
||||
|
||||
@@ -5,9 +5,9 @@ import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gopkg.in/ldap.v3"
|
||||
|
||||
"github.com/grafana/grafana/pkg/infra/log"
|
||||
"github.com/grafana/grafana/pkg/models/roletype"
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
@@ -21,18 +23,24 @@ type Config struct {
|
||||
|
||||
// ServerConfig holds connection data to LDAP
|
||||
type ServerConfig struct {
|
||||
Host string `toml:"host"`
|
||||
Port int `toml:"port"`
|
||||
UseSSL bool `toml:"use_ssl"`
|
||||
StartTLS bool `toml:"start_tls"`
|
||||
SkipVerifySSL bool `toml:"ssl_skip_verify"`
|
||||
RootCACert string `toml:"root_ca_cert"`
|
||||
ClientCert string `toml:"client_cert"`
|
||||
ClientKey string `toml:"client_key"`
|
||||
BindDN string `toml:"bind_dn"`
|
||||
BindPassword string `toml:"bind_password"`
|
||||
Timeout int `toml:"timeout"`
|
||||
Attr AttributeMap `toml:"attributes"`
|
||||
Host string `toml:"host"`
|
||||
Port int `toml:"port"`
|
||||
|
||||
UseSSL bool `toml:"use_ssl"`
|
||||
StartTLS bool `toml:"start_tls"`
|
||||
SkipVerifySSL bool `toml:"ssl_skip_verify"`
|
||||
MinTLSVersion string `toml:"min_tls_version"`
|
||||
minTLSVersion uint16 `toml:"-"`
|
||||
TLSCiphers []string `toml:"tls_ciphers"`
|
||||
tlsCiphers []uint16 `toml:"-"`
|
||||
|
||||
RootCACert string `toml:"root_ca_cert"`
|
||||
ClientCert string `toml:"client_cert"`
|
||||
ClientKey string `toml:"client_key"`
|
||||
BindDN string `toml:"bind_dn"`
|
||||
BindPassword string `toml:"bind_password"`
|
||||
Timeout int `toml:"timeout"`
|
||||
Attr AttributeMap `toml:"attributes"`
|
||||
|
||||
SearchFilter string `toml:"search_filter"`
|
||||
SearchBaseDNs []string `toml:"search_base_dns"`
|
||||
@@ -135,6 +143,20 @@ func readConfig(configFile string) (*Config, error) {
|
||||
return nil, fmt.Errorf("%v: %w", "Failed to validate SearchBaseDNs section", err)
|
||||
}
|
||||
|
||||
if server.MinTLSVersion != "" {
|
||||
server.minTLSVersion, err = tlsNameToVersion(server.MinTLSVersion)
|
||||
if err != nil {
|
||||
logger.Error("Failed to set min TLS version. Ignoring", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
if len(server.TLSCiphers) > 0 {
|
||||
server.tlsCiphers, err = tlsCiphersToIDs(server.TLSCiphers)
|
||||
if err != nil {
|
||||
logger.Error("Unrecognized TLS Cipher(s). Ignoring", "err", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, groupMap := range server.Groups {
|
||||
if groupMap.OrgRole == "" && groupMap.IsGrafanaAdmin == nil {
|
||||
return nil, fmt.Errorf("LDAP group mapping: organization role or grafana admin status is required")
|
||||
@@ -169,3 +191,53 @@ func assertNotEmptyCfg(val interface{}, propName string) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// tlsNameToVersion converts a string to a tls version
|
||||
func tlsNameToVersion(name string) (uint16, error) {
|
||||
name = strings.ToUpper(name)
|
||||
switch name {
|
||||
case "TLS1.0":
|
||||
return tls.VersionTLS10, nil
|
||||
case "TLS1.1":
|
||||
return tls.VersionTLS11, nil
|
||||
case "TLS1.2":
|
||||
return tls.VersionTLS12, nil
|
||||
case "TLS1.3":
|
||||
return tls.VersionTLS13, nil
|
||||
}
|
||||
|
||||
return 0, fmt.Errorf("unknown tls version: %q", name)
|
||||
}
|
||||
|
||||
// Cipher strings https://go.dev/src/crypto/tls/cipher_suites.go
|
||||
// Ex: "TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256" or "TLS_RSA_WITH_AES_128_CBC_SHA"
|
||||
func tlsCiphersToIDs(names []string) ([]uint16, error) {
|
||||
if len(names) == 0 || names == nil {
|
||||
// no ciphers specified, use defaults
|
||||
return nil, nil
|
||||
}
|
||||
var ids []uint16
|
||||
var missing []string
|
||||
|
||||
ciphers := tls.CipherSuites()
|
||||
var cipherMap = make(map[string]uint16, len(ciphers))
|
||||
for _, cipher := range ciphers {
|
||||
cipherMap[cipher.Name] = cipher.ID
|
||||
}
|
||||
|
||||
for _, name := range names {
|
||||
name = strings.ToUpper(name)
|
||||
id, ok := cipherMap[name]
|
||||
if !ok {
|
||||
missing = append(missing, name)
|
||||
continue
|
||||
}
|
||||
ids = append(ids, id)
|
||||
}
|
||||
|
||||
if len(missing) > 0 {
|
||||
return ids, fmt.Errorf("unknown ciphers: %v", missing)
|
||||
}
|
||||
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package ldap
|
||||
|
||||
import (
|
||||
"os"
|
||||
"crypto/tls"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
@@ -12,11 +12,14 @@ func TestReadingLDAPSettings(t *testing.T) {
|
||||
config, err := readConfig("testdata/ldap.toml")
|
||||
assert.Nil(t, err, "No error when reading ldap config")
|
||||
assert.EqualValues(t, "127.0.0.1", config.Servers[0].Host)
|
||||
assert.EqualValues(t, "tls1.3", config.Servers[0].MinTLSVersion)
|
||||
assert.EqualValues(t, uint16(tls.VersionTLS13), config.Servers[0].minTLSVersion)
|
||||
assert.EqualValues(t, []string{"TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_128_GCM_SHA256"}, config.Servers[0].TLSCiphers)
|
||||
assert.ElementsMatch(t, []uint16{tls.TLS_CHACHA20_POLY1305_SHA256, tls.TLS_AES_128_GCM_SHA256}, config.Servers[0].tlsCiphers)
|
||||
}
|
||||
|
||||
func TestReadingLDAPSettingsWithEnvVariable(t *testing.T) {
|
||||
err := os.Setenv("ENV_PASSWORD", "MySecret")
|
||||
require.NoError(t, err)
|
||||
t.Setenv("ENV_PASSWORD", "MySecret")
|
||||
|
||||
config, err := readConfig("testdata/ldap.toml")
|
||||
require.NoError(t, err)
|
||||
|
||||
Vendored
+2
-1
@@ -4,6 +4,8 @@ port = 389
|
||||
use_ssl = false
|
||||
start_tls = false
|
||||
ssl_skip_verify = false
|
||||
tls_ciphers = ["TLS_CHACHA20_POLY1305_SHA256", "TLS_AES_128_GCM_SHA256"]
|
||||
min_tls_version = "tls1.3"
|
||||
bind_dn = "cn=admin,dc=grafana,dc=org"
|
||||
bind_password = '${ENV_PASSWORD}'
|
||||
search_filter = "(cn=%s)"
|
||||
@@ -24,4 +26,3 @@ grafana_admin = true
|
||||
[[servers.group_mappings]]
|
||||
group_dn = "cn=users,ou=groups,dc=grafana,dc=org"
|
||||
org_role = "Editor"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ package ldap
|
||||
import (
|
||||
"crypto/tls"
|
||||
|
||||
"gopkg.in/ldap.v3"
|
||||
"github.com/go-ldap/ldap/v3"
|
||||
|
||||
//TODO(sh0rez): remove once import cycle resolved
|
||||
_ "github.com/grafana/grafana/pkg/api/response"
|
||||
|
||||
Reference in New Issue
Block a user