Merge branch 'master' into neilalexander/aswarn

This commit is contained in:
Neil Alexander 2021-03-08 13:32:31 +00:00 committed by GitHub
commit aad59cb31a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 55 additions and 33 deletions

View file

@ -24,6 +24,7 @@ import (
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"github.com/matrix-org/dendrite/userapi/storage/accounts" "github.com/matrix-org/dendrite/userapi/storage/accounts"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"
) )
const usage = `Usage: %s const usage = `Usage: %s
@ -57,7 +58,7 @@ func main() {
accountDB, err := accounts.NewDatabase(&config.DatabaseOptions{ accountDB, err := accounts.NewDatabase(&config.DatabaseOptions{
ConnectionString: cfg.UserAPI.AccountDatabase.ConnectionString, ConnectionString: cfg.UserAPI.AccountDatabase.ConnectionString,
}, cfg.Global.ServerName) }, cfg.Global.ServerName, bcrypt.DefaultCost)
if err != nil { if err != nil {
logrus.Fatalln("Failed to connect to the database:", err.Error()) logrus.Fatalln("Failed to connect to the database:", err.Error())
} }

View file

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"github.com/matrix-org/dendrite/setup/config" "github.com/matrix-org/dendrite/setup/config"
"golang.org/x/crypto/bcrypt"
"gopkg.in/yaml.v2" "gopkg.in/yaml.v2"
) )
@ -68,6 +69,7 @@ func main() {
cfg.Logging[0].Level = "trace" cfg.Logging[0].Level = "trace"
// don't hit matrix.org when running tests!!! // don't hit matrix.org when running tests!!!
cfg.SigningKeyServer.KeyPerspectives = config.KeyPerspectives{} cfg.SigningKeyServer.KeyPerspectives = config.KeyPerspectives{}
cfg.UserAPI.BCryptCost = bcrypt.MinCost
} }
j, err := yaml.Marshal(cfg) j, err := yaml.Marshal(cfg)

View file

@ -240,7 +240,7 @@ media_api:
listen: http://[::]:8074 listen: http://[::]:8074
database: database:
connection_string: file:mediaapi.db connection_string: file:mediaapi.db
max_open_conns: 10 max_open_conns: 5
max_idle_conns: 2 max_idle_conns: 2
conn_max_lifetime: -1 conn_max_lifetime: -1
@ -278,7 +278,7 @@ mscs:
mscs: [] mscs: []
database: database:
connection_string: file:mscs.db connection_string: file:mscs.db
max_open_conns: 10 max_open_conns: 5
max_idle_conns: 2 max_idle_conns: 2
conn_max_lifetime: -1 conn_max_lifetime: -1
@ -340,6 +340,13 @@ sync_api:
# Configuration for the User API. # Configuration for the User API.
user_api: user_api:
# The cost when hashing passwords on registration/login. Default: 10. Min: 4, Max: 31
# See https://pkg.go.dev/golang.org/x/crypto/bcrypt for more information.
# Setting this lower makes registration/login consume less CPU resources at the cost of security
# should the database be compromised. Setting this higher makes registration/login consume more
# CPU resources but makes it harder to brute force password hashes.
# This value can be low if performing tests or on embedded Dendrite instances (e.g WASM builds)
# bcrypt_cost: 10
internal_api: internal_api:
listen: http://localhost:7781 listen: http://localhost:7781
connect: http://localhost:7781 connect: http://localhost:7781

View file

@ -263,7 +263,7 @@ func (b *BaseDendrite) KeyServerHTTPClient() keyserverAPI.KeyInternalAPI {
// CreateAccountsDB creates a new instance of the accounts database. Should only // CreateAccountsDB creates a new instance of the accounts database. Should only
// be called once per component. // be called once per component.
func (b *BaseDendrite) CreateAccountsDB() accounts.Database { func (b *BaseDendrite) CreateAccountsDB() accounts.Database {
db, err := accounts.NewDatabase(&b.Cfg.UserAPI.AccountDatabase, b.Cfg.Global.ServerName) db, err := accounts.NewDatabase(&b.Cfg.UserAPI.AccountDatabase, b.Cfg.Global.ServerName, b.Cfg.UserAPI.BCryptCost)
if err != nil { if err != nil {
logrus.WithError(err).Panicf("failed to connect to accounts db") logrus.WithError(err).Panicf("failed to connect to accounts db")
} }

View file

@ -43,7 +43,7 @@ type AppServiceAPI struct {
func (c *AppServiceAPI) Defaults() { func (c *AppServiceAPI) Defaults() {
c.InternalAPI.Listen = "http://localhost:7777" c.InternalAPI.Listen = "http://localhost:7777"
c.InternalAPI.Connect = "http://localhost:7777" c.InternalAPI.Connect = "http://localhost:7777"
c.Database.Defaults() c.Database.Defaults(10)
c.Database.ConnectionString = "file:appservice.db" c.Database.ConnectionString = "file:appservice.db"
} }

View file

@ -25,7 +25,7 @@ type FederationSender struct {
func (c *FederationSender) Defaults() { func (c *FederationSender) Defaults() {
c.InternalAPI.Listen = "http://localhost:7775" c.InternalAPI.Listen = "http://localhost:7775"
c.InternalAPI.Connect = "http://localhost:7775" c.InternalAPI.Connect = "http://localhost:7775"
c.Database.Defaults() c.Database.Defaults(10)
c.Database.ConnectionString = "file:federationsender.db" c.Database.ConnectionString = "file:federationsender.db"
c.FederationMaxRetries = 16 c.FederationMaxRetries = 16

View file

@ -122,8 +122,8 @@ type DatabaseOptions struct {
ConnMaxLifetimeSeconds int `yaml:"conn_max_lifetime"` ConnMaxLifetimeSeconds int `yaml:"conn_max_lifetime"`
} }
func (c *DatabaseOptions) Defaults() { func (c *DatabaseOptions) Defaults(conns int) {
c.MaxOpenConnections = 100 c.MaxOpenConnections = conns
c.MaxIdleConnections = 2 c.MaxIdleConnections = 2
c.ConnMaxLifetimeSeconds = -1 c.ConnMaxLifetimeSeconds = -1
} }

View file

@ -36,7 +36,7 @@ func (k *Kafka) TopicFor(name string) string {
func (c *Kafka) Defaults() { func (c *Kafka) Defaults() {
c.UseNaffka = true c.UseNaffka = true
c.Database.Defaults() c.Database.Defaults(10)
c.Addresses = []string{"localhost:2181"} c.Addresses = []string{"localhost:2181"}
c.Database.ConnectionString = DataSource("file:naffka.db") c.Database.ConnectionString = DataSource("file:naffka.db")
c.TopicPrefix = "Dendrite" c.TopicPrefix = "Dendrite"

View file

@ -11,7 +11,7 @@ type KeyServer struct {
func (c *KeyServer) Defaults() { func (c *KeyServer) Defaults() {
c.InternalAPI.Listen = "http://localhost:7779" c.InternalAPI.Listen = "http://localhost:7779"
c.InternalAPI.Connect = "http://localhost:7779" c.InternalAPI.Connect = "http://localhost:7779"
c.Database.Defaults() c.Database.Defaults(10)
c.Database.ConnectionString = "file:keyserver.db" c.Database.ConnectionString = "file:keyserver.db"
} }

View file

@ -39,7 +39,7 @@ func (c *MediaAPI) Defaults() {
c.InternalAPI.Listen = "http://localhost:7774" c.InternalAPI.Listen = "http://localhost:7774"
c.InternalAPI.Connect = "http://localhost:7774" c.InternalAPI.Connect = "http://localhost:7774"
c.ExternalAPI.Listen = "http://[::]:8074" c.ExternalAPI.Listen = "http://[::]:8074"
c.Database.Defaults() c.Database.Defaults(5)
c.Database.ConnectionString = "file:mediaapi.db" c.Database.ConnectionString = "file:mediaapi.db"
defaultMaxFileSizeBytes := FileSizeBytes(10485760) defaultMaxFileSizeBytes := FileSizeBytes(10485760)

View file

@ -14,7 +14,7 @@ type MSCs struct {
} }
func (c *MSCs) Defaults() { func (c *MSCs) Defaults() {
c.Database.Defaults() c.Database.Defaults(5)
c.Database.ConnectionString = "file:mscs.db" c.Database.ConnectionString = "file:mscs.db"
} }

View file

@ -11,7 +11,7 @@ type RoomServer struct {
func (c *RoomServer) Defaults() { func (c *RoomServer) Defaults() {
c.InternalAPI.Listen = "http://localhost:7770" c.InternalAPI.Listen = "http://localhost:7770"
c.InternalAPI.Connect = "http://localhost:7770" c.InternalAPI.Connect = "http://localhost:7770"
c.Database.Defaults() c.Database.Defaults(10)
c.Database.ConnectionString = "file:roomserver.db" c.Database.ConnectionString = "file:roomserver.db"
} }

View file

@ -22,7 +22,7 @@ type SigningKeyServer struct {
func (c *SigningKeyServer) Defaults() { func (c *SigningKeyServer) Defaults() {
c.InternalAPI.Listen = "http://localhost:7780" c.InternalAPI.Listen = "http://localhost:7780"
c.InternalAPI.Connect = "http://localhost:7780" c.InternalAPI.Connect = "http://localhost:7780"
c.Database.Defaults() c.Database.Defaults(10)
c.Database.ConnectionString = "file:signingkeyserver.db" c.Database.ConnectionString = "file:signingkeyserver.db"
} }

View file

@ -15,7 +15,7 @@ func (c *SyncAPI) Defaults() {
c.InternalAPI.Listen = "http://localhost:7773" c.InternalAPI.Listen = "http://localhost:7773"
c.InternalAPI.Connect = "http://localhost:7773" c.InternalAPI.Connect = "http://localhost:7773"
c.ExternalAPI.Listen = "http://localhost:8073" c.ExternalAPI.Listen = "http://localhost:8073"
c.Database.Defaults() c.Database.Defaults(10)
c.Database.ConnectionString = "file:syncapi.db" c.Database.ConnectionString = "file:syncapi.db"
} }

View file

@ -1,10 +1,15 @@
package config package config
import "golang.org/x/crypto/bcrypt"
type UserAPI struct { type UserAPI struct {
Matrix *Global `yaml:"-"` Matrix *Global `yaml:"-"`
InternalAPI InternalAPIOptions `yaml:"internal_api"` InternalAPI InternalAPIOptions `yaml:"internal_api"`
// The cost when hashing passwords.
BCryptCost int `yaml:"bcrypt_cost"`
// The Account database stores the login details and account information // The Account database stores the login details and account information
// for local users. It is accessed by the UserAPI. // for local users. It is accessed by the UserAPI.
AccountDatabase DatabaseOptions `yaml:"account_database"` AccountDatabase DatabaseOptions `yaml:"account_database"`
@ -16,10 +21,11 @@ type UserAPI struct {
func (c *UserAPI) Defaults() { func (c *UserAPI) Defaults() {
c.InternalAPI.Listen = "http://localhost:7781" c.InternalAPI.Listen = "http://localhost:7781"
c.InternalAPI.Connect = "http://localhost:7781" c.InternalAPI.Connect = "http://localhost:7781"
c.AccountDatabase.Defaults() c.AccountDatabase.Defaults(10)
c.DeviceDatabase.Defaults() c.DeviceDatabase.Defaults(10)
c.AccountDatabase.ConnectionString = "file:userapi_accounts.db" c.AccountDatabase.ConnectionString = "file:userapi_accounts.db"
c.DeviceDatabase.ConnectionString = "file:userapi_devices.db" c.DeviceDatabase.ConnectionString = "file:userapi_devices.db"
c.BCryptCost = bcrypt.DefaultCost
} }
func (c *UserAPI) Verify(configErrs *ConfigErrors, isMonolith bool) { func (c *UserAPI) Verify(configErrs *ConfigErrors, isMonolith bool) {

View file

@ -516,3 +516,4 @@ AS can create a user with inhibit_login
AS can set avatar for ghosted users AS can set avatar for ghosted users
AS can set displayname for ghosted users AS can set displayname for ghosted users
Ghost user must register before joining room Ghost user must register before joining room
Inviting an AS-hosted user asks the AS server

View file

@ -44,10 +44,11 @@ type Database struct {
accountDatas accountDataStatements accountDatas accountDataStatements
threepids threepidStatements threepids threepidStatements
serverName gomatrixserverlib.ServerName serverName gomatrixserverlib.ServerName
bcryptCost int
} }
// NewDatabase creates a new accounts and profiles database // NewDatabase creates a new accounts and profiles database
func NewDatabase(dbProperties *config.DatabaseOptions, serverName gomatrixserverlib.ServerName) (*Database, error) { func NewDatabase(dbProperties *config.DatabaseOptions, serverName gomatrixserverlib.ServerName, bcryptCost int) (*Database, error) {
db, err := sqlutil.Open(dbProperties) db, err := sqlutil.Open(dbProperties)
if err != nil { if err != nil {
return nil, err return nil, err
@ -56,6 +57,7 @@ func NewDatabase(dbProperties *config.DatabaseOptions, serverName gomatrixserver
serverName: serverName, serverName: serverName,
db: db, db: db,
writer: sqlutil.NewDummyWriter(), writer: sqlutil.NewDummyWriter(),
bcryptCost: bcryptCost,
} }
// Create tables before executing migrations so we don't fail if the table is missing, // Create tables before executing migrations so we don't fail if the table is missing,
@ -131,7 +133,7 @@ func (d *Database) SetDisplayName(
func (d *Database) SetPassword( func (d *Database) SetPassword(
ctx context.Context, localpart, plaintextPassword string, ctx context.Context, localpart, plaintextPassword string,
) error { ) error {
hash, err := hashPassword(plaintextPassword) hash, err := d.hashPassword(plaintextPassword)
if err != nil { if err != nil {
return err return err
} }
@ -175,7 +177,7 @@ func (d *Database) createAccount(
// Generate a password hash if this is not a password-less user // Generate a password hash if this is not a password-less user
hash := "" hash := ""
if plaintextPassword != "" { if plaintextPassword != "" {
hash, err = hashPassword(plaintextPassword) hash, err = d.hashPassword(plaintextPassword)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -246,8 +248,8 @@ func (d *Database) GetNewNumericLocalpart(
return d.accounts.selectNewNumericLocalpart(ctx, nil) return d.accounts.selectNewNumericLocalpart(ctx, nil)
} }
func hashPassword(plaintext string) (hash string, err error) { func (d *Database) hashPassword(plaintext string) (hash string, err error) {
hashBytes, err := bcrypt.GenerateFromPassword([]byte(plaintext), bcrypt.DefaultCost) hashBytes, err := bcrypt.GenerateFromPassword([]byte(plaintext), d.bcryptCost)
return string(hashBytes), err return string(hashBytes), err
} }

View file

@ -42,6 +42,7 @@ type Database struct {
accountDatas accountDataStatements accountDatas accountDataStatements
threepids threepidStatements threepids threepidStatements
serverName gomatrixserverlib.ServerName serverName gomatrixserverlib.ServerName
bcryptCost int
accountsMu sync.Mutex accountsMu sync.Mutex
profilesMu sync.Mutex profilesMu sync.Mutex
@ -50,7 +51,7 @@ type Database struct {
} }
// NewDatabase creates a new accounts and profiles database // NewDatabase creates a new accounts and profiles database
func NewDatabase(dbProperties *config.DatabaseOptions, serverName gomatrixserverlib.ServerName) (*Database, error) { func NewDatabase(dbProperties *config.DatabaseOptions, serverName gomatrixserverlib.ServerName, bcryptCost int) (*Database, error) {
db, err := sqlutil.Open(dbProperties) db, err := sqlutil.Open(dbProperties)
if err != nil { if err != nil {
return nil, err return nil, err
@ -59,6 +60,7 @@ func NewDatabase(dbProperties *config.DatabaseOptions, serverName gomatrixserver
serverName: serverName, serverName: serverName,
db: db, db: db,
writer: sqlutil.NewExclusiveWriter(), writer: sqlutil.NewExclusiveWriter(),
bcryptCost: bcryptCost,
} }
// Create tables before executing migrations so we don't fail if the table is missing, // Create tables before executing migrations so we don't fail if the table is missing,
@ -143,7 +145,7 @@ func (d *Database) SetDisplayName(
func (d *Database) SetPassword( func (d *Database) SetPassword(
ctx context.Context, localpart, plaintextPassword string, ctx context.Context, localpart, plaintextPassword string,
) error { ) error {
hash, err := hashPassword(plaintextPassword) hash, err := d.hashPassword(plaintextPassword)
if err != nil { if err != nil {
return err return err
} }
@ -208,7 +210,7 @@ func (d *Database) createAccount(
// Generate a password hash if this is not a password-less user // Generate a password hash if this is not a password-less user
hash := "" hash := ""
if plaintextPassword != "" { if plaintextPassword != "" {
hash, err = hashPassword(plaintextPassword) hash, err = d.hashPassword(plaintextPassword)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -278,8 +280,8 @@ func (d *Database) GetNewNumericLocalpart(
return d.accounts.selectNewNumericLocalpart(ctx, nil) return d.accounts.selectNewNumericLocalpart(ctx, nil)
} }
func hashPassword(plaintext string) (hash string, err error) { func (d *Database) hashPassword(plaintext string) (hash string, err error) {
hashBytes, err := bcrypt.GenerateFromPassword([]byte(plaintext), bcrypt.DefaultCost) hashBytes, err := bcrypt.GenerateFromPassword([]byte(plaintext), d.bcryptCost)
return string(hashBytes), err return string(hashBytes), err
} }

View file

@ -27,12 +27,12 @@ import (
// NewDatabase opens a new Postgres or Sqlite database (based on dataSourceName scheme) // NewDatabase opens a new Postgres or Sqlite database (based on dataSourceName scheme)
// and sets postgres connection parameters // and sets postgres connection parameters
func NewDatabase(dbProperties *config.DatabaseOptions, serverName gomatrixserverlib.ServerName) (Database, error) { func NewDatabase(dbProperties *config.DatabaseOptions, serverName gomatrixserverlib.ServerName, bcryptCost int) (Database, error) {
switch { switch {
case dbProperties.ConnectionString.IsSQLite(): case dbProperties.ConnectionString.IsSQLite():
return sqlite3.NewDatabase(dbProperties, serverName) return sqlite3.NewDatabase(dbProperties, serverName, bcryptCost)
case dbProperties.ConnectionString.IsPostgres(): case dbProperties.ConnectionString.IsPostgres():
return postgres.NewDatabase(dbProperties, serverName) return postgres.NewDatabase(dbProperties, serverName, bcryptCost)
default: default:
return nil, fmt.Errorf("unexpected database type") return nil, fmt.Errorf("unexpected database type")
} }

View file

@ -25,10 +25,11 @@ import (
func NewDatabase( func NewDatabase(
dbProperties *config.DatabaseOptions, dbProperties *config.DatabaseOptions,
serverName gomatrixserverlib.ServerName, serverName gomatrixserverlib.ServerName,
bcryptCost int,
) (Database, error) { ) (Database, error) {
switch { switch {
case dbProperties.ConnectionString.IsSQLite(): case dbProperties.ConnectionString.IsSQLite():
return sqlite3.NewDatabase(dbProperties, serverName) return sqlite3.NewDatabase(dbProperties, serverName, bcryptCost)
case dbProperties.ConnectionString.IsPostgres(): case dbProperties.ConnectionString.IsPostgres():
return nil, fmt.Errorf("can't use Postgres implementation") return nil, fmt.Errorf("can't use Postgres implementation")
default: default:

View file

@ -37,7 +37,6 @@ func AddInternalRoutes(router *mux.Router, intAPI api.UserInternalAPI) {
func NewInternalAPI( func NewInternalAPI(
accountDB accounts.Database, cfg *config.UserAPI, appServices []config.ApplicationService, keyAPI keyapi.KeyInternalAPI, accountDB accounts.Database, cfg *config.UserAPI, appServices []config.ApplicationService, keyAPI keyapi.KeyInternalAPI,
) api.UserInternalAPI { ) api.UserInternalAPI {
deviceDB, err := devices.NewDatabase(&cfg.DeviceDatabase, cfg.Matrix.ServerName) deviceDB, err := devices.NewDatabase(&cfg.DeviceDatabase, cfg.Matrix.ServerName)
if err != nil { if err != nil {
logrus.WithError(err).Panicf("failed to connect to device db") logrus.WithError(err).Panicf("failed to connect to device db")

View file

@ -16,6 +16,7 @@ import (
"github.com/matrix-org/dendrite/userapi/inthttp" "github.com/matrix-org/dendrite/userapi/inthttp"
"github.com/matrix-org/dendrite/userapi/storage/accounts" "github.com/matrix-org/dendrite/userapi/storage/accounts"
"github.com/matrix-org/gomatrixserverlib" "github.com/matrix-org/gomatrixserverlib"
"golang.org/x/crypto/bcrypt"
) )
const ( const (
@ -25,7 +26,7 @@ const (
func MustMakeInternalAPI(t *testing.T) (api.UserInternalAPI, accounts.Database) { func MustMakeInternalAPI(t *testing.T) (api.UserInternalAPI, accounts.Database) {
accountDB, err := accounts.NewDatabase(&config.DatabaseOptions{ accountDB, err := accounts.NewDatabase(&config.DatabaseOptions{
ConnectionString: "file::memory:", ConnectionString: "file::memory:",
}, serverName) }, serverName, bcrypt.MinCost)
if err != nil { if err != nil {
t.Fatalf("failed to create account DB: %s", err) t.Fatalf("failed to create account DB: %s", err)
} }