Security

For production use it is important to configure EventStoreDB security features to prevent unauthorised access to your data.

Security features of EventStoreDB include:

  • User management for allowing users with different roles to access the database
  • Access Control Lists to restrict access to specific event streams
  • Encryption in-flight using HTTPS and TLS

Protocol security

EventStoreDB supports the proprietary TCP protocol for high-throughput real-time communication, and the more traditional HTTP REST API for read and append operations, as well as for the management operations like scavenging, creating projections and so on. EventStoreDB also uses HTTP for the gossip seed endpoint, both internally for the cluster gossip, and internally for clients that connect to the cluster using discovery mode.

Both those protocols support encryption with TLS and SSL. Each protocol has its own security configuration, but you can only use one set of certificates for both TLS and HTTPS.

The process of creating certificates and instructing EventStoreDB to use them is different per platform. Please follow the platform-specific guidelines for setting up SSL and TLS:

These guidelines also include some platform-specific configuration settings, so you should be able to run a secure cluster after completing the steps described there.

Setting up SSL on Linux

TIP

This guide uses the latest Ubuntu LTS (18.04)

Generate a certificate

First, create a private key and a self-signed certificate request (only for testing purposes):

openssl req \
  -x509 -sha256 -nodes -days 365 -subj "/CN=eventstore.com" \
  -newkey rsa:2048 -keyout eventstore.pem -out eventstore.csr
1
2
3

Export the p12 file from the certificate request. You will need this file when starting EventStoreDB:

openssl pkcs12 -export -inkey eventstore.pem -in eventstore.csr -out eventstore.p12
1

Trust the certificate

You need to add the certificate to Ubuntu's trusted certificates. Copy the cert to the ca-certificates folder and update the certificates:

sudo cp eventstore.csr /usr/local/share/ca-certificates/eventstore.crt

sudo update-ca-certificates
1
2
3

The Mono framework has its own separate certificate store which you need to sync with the changes you made to Ubuntu certificates.

You first need to install mono-devel version 5.16.0 :

sudo apt install gnupg ca-certificates
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF
# Update "bionic" to match your Ubuntu version
echo "deb https://download.mono-project.com/repo/ubuntu stable-bionic/snapshots/5.16.0 main" | sudo tee /etc/apt/sources.list.d/mono-official-stable.list
sudo apt update

sudo apt-get install mono-devel
1
2
3
4
5
6
7

This process installs cert-sync, the tool you need for updating Mono certificate store with the new certificate:

sudo cert-sync eventstore.csr
1

Configure the server

Start EventStoreDB with the following configuration in the eventstore.conf file:

CertificateFile: eventstore.p12
ExtSecureTcpPort: 1115
1
2

Read more about server security settings on this page.

Connect to secure node

When connecting to the secure node, you need to tell the client to use the secure connection.

var settings = ConnectionSettings
    .Create()
    .UseSslConnection("eventstore.com", true);

using var conn = EventStoreConnection
    .Create(settings, new IPEndPoint(IPAddress.Loopback, 1115));

await conn.ConnectAsync();
1
2
3
4
5
6
7
8

Setting up SSL on Windows

Follow this guide to add a self-signed certificate to EventStoreDB.

Generate a certificate

First, create a certificate using PowerShell, and copy the thumbprint from the output:

New-SelfSignedCertificate -DnsName eventstore.org, localhost -CertStoreLocation cert:\CurrentUser\My
1

The cert:\CurrentUser\My certificate location contains values to be used for CertificateStoreLocation (CurrentUser) and CertificateStoreName (My) settings.

The eventstore.org value should be used for the CertificateSubjectName setting, and localhost is the default value for the SslTargetHost setting.

Trust the certificate

To trust the new certificate, you have to import the certificate into the Trusted Root Certification Authorities:

  1. Press WindowsKey + R, and enter certmgr.msc.

Open certmgr.msc

  1. Navigate to Certificates -> Current User -> Personal -> Certificates.

  2. Locate the certificate 'eventstore.com'.

Find certificate

  1. Right click on the certificate and click on All Tasks -> Export. Follow the prompts.

Export certificate

  1. Navigate to Certificates -> Current User -> Trusted Root Certification Authorities -> Certificates.
  2. Right click on the Certificates folder menu item and click All Tasks -> Import. Follow the prompts.

Find certificate

Configure the server

Start EventStoreDB with the following configuration in the eventstore.conf file:

CertificateStoreLocation: CurrentUser
CertificateStoreName: My
CertificateThumbPrint: {Insert Thumb Print from Step 1}
CertificateSubjectName: CN=eventstore.com
ExtSecureTcpPort: 1115
1
2
3
4
5

Read more about server security settings on this page.

Connect to secure node

When connecting to the secure node, you need to tell the client to use the secure connection.

var settings = ConnectionSettings
    .Create()
    .UseSslConnection("eventstore.com", true);

using var conn = EventStoreConnection
    .Create(settings, new IPEndPoint(IPAddress.Loopback, 1115));
await conn.ConnectAsync();
1
2
3
4
5
6
7

Setting up SSL for Docker

This example shows how to build own Docker image based on the original EventStoreDB image with a built-in self-signed certificate. The example should be modified before going to production by using real certificate.

Create file Dockerfile with following content:

FROM eventstore/eventstore:release-5.0.11
RUN apt-get update -y \
  && apt-get install -y openssl \
  && openssl req -x509 -sha256 -nodes -days 3650 -subj "/CN=eventstore.org" -newkey rsa:2048 -keyout eventstore.pem -out eventstore.csr \
  && openssl pkcs12 -export -inkey eventstore.pem -in eventstore.csr -out eventstore.p12 -passout pass: \
  && openssl pkcs12 -export -inkey eventstore.pem -in eventstore.csr -out eventstore.pfx -passout pass: \
  && mkdir -p /usr/local/share/ca-certificates \
  && cp eventstore.csr /usr/local/share/ca-certificates/eventstore.crt \
  && update-ca-certificates \
  && apt-get autoremove \
  && apt-get clean \
  && rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
1
2
3
4
5
6
7
8
9
10
11
12

Build the image:

docker build -t eventstore/eventstore:with-cert-local --no-cache .
1

Run the container:

docker run --name eventstore-node -it -p 1113:1113 -p 1115:1115 -p 2113:2113 -e EVENTSTORE_CERTIFICATE_FILE=eventstore.p12 -e EVENTSTORE_EXT_SECURE_TCP_PORT=1115 eventstore/eventstore:with-cert-local
1

Notice we use port 1115 instead of default 1113.

Use the following connection string for the TCP client:

"ConnectTo=tcp://localhost:1115;DefaultUserCredentials=admin:changeit;UseSslConnection=true;TargetHost=eventstore.org;ValidateServer=false",
1

Notice ValidateServer is set to false because we use a self-signed certificate. For production always use ValidateServer=true.

Authentication

EventStoreDB supports authentication based on usernames and passwords out of the box. The Enterprise version also supports LDAP as the authentication source.

Authentication is applied to all HTTP endpoints, except /info, /ping, /stats, /elections (only GET), /gossip (only GET) and static web content.

Default users

EventStoreDB provides two default users, $ops and $admin.

$admin has full access to everything in EventStoreDB. It can read and write to protected streams, which is any stream that starts with $, such as $projections-master. Protected streams are usually system streams, for example, $projections-master manages some of the projections' states. The $admin user can also run operational commands, such as scavenges and shutdowns on EventStoreDB.

An $ops user can do everything that an $admin can do except manage users and read from system streams (except for $scavenges and $scavenges-streams).

New users

New users created in EventStoreDB are standard non-authenticated users. Non-authenticated users are allowed GET access to the /info, /ping, /stats, /elections, and /gossip system streams.

POST access to the /elections and /gossip system streams is only allowed on the internal HTTP service.

By default, any user can read any non-protected stream unless there is an ACL preventing that.

Externalised authentication

You can also use the trusted intermediary header for externalized authentication that allows you to integrate almost any authentication system with EventStoreDB. Read more about the trusted intermediary header.

Disable HTTP authentication

It is possible to disable authentication on all protected HTTP endpoints by setting the DisableFirstLevelHttpAuthorization setting to true. The setting is set to false by default. When enabled, the setting will force EventStoreDB to use the supplied credentials only to check the stream access using ACLs.

Access Control Lists

By default, authenticated users have access to the whole EventStoreDB database. In addition to that, it allows you to use Access Control Lists (ACLs) to set up more granular access control. In fact, the default access level is also controlled by a special ACL, which is called the default ACL.

Stream ACL

EventStoreDB keeps the ACL of a stream in the streams metadata as JSON with the below definition:

{
  "$acl": {
    "$w": "$admins",
    "$r": "$all",
    "$d": "$admins",
    "$mw": "$admins",
    "$mr": "$admins"
  }
}
1
2
3
4
5
6
7
8
9

These fields represent the following:

  • $w The permission to write to this stream.
  • $r The permission to read from this stream.
  • $d The permission to delete this stream.
  • $mw The permission to write the metadata associated with this stream.
  • $mr The permission to read the metadata associated with this stream.

You can update these fields with either a single string or an array of strings representing users or groups ($admins, $all, or custom groups). It's possible to put an empty array into one of these fields, and this has the effect of removing all users from that permission.

TIP

We recommend you don't give people access to $mw as then they can then change the ACL.

Default ACL

The $settings stream has a special ACL used as the default ACL. This stream controls the default ACL for streams without an ACL and also controls who can create streams in the system, the default state of these is shown below:

{
  "$userStreamAcl": {
    "$r": "$all",
    "$w": "$all",
    "$d": "$all",
    "$mr": "$all",
    "$mw": "$all"
  },
  "$systemStreamAcl": {
    "$r": "$admins",
    "$w": "$admins",
    "$d": "$admins",
    "$mr": "$admins",
    "$mw": "$admins"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

You can rewrite these to the $settings stream with the following request:

curl -i "http://127.0.0.1:2113/streams/%24settings" \
    --user admin:changeit \
    -H "Content-Type: application/vnd.eventstore.events+json" \
    -d #39;[{
        "eventId": "7c314750-05e1-439f-b2eb-f5b0e019be72",
        "eventType": "update-default-acl",
        "data": {
            "$userStreamAcl" : {
                "$r"  : ["$admin", "$ops", "service-a", "service-b"],
                "$w"  : ["$admin", "$ops", "service-a", "service-b"],
                "$d"  : ["$admin", "$ops"],
                "$mr" : ["$admin", "$ops"],
                "$mw" : ["$admin", "$ops"]
            },
            "$systemStreamAcl" : {
                "$r"  : "$admins",
                "$w"  : "$admins",
                "$d"  : "$admins",
                "$mr" : "$admins",
                "$mw" : "$admins"
            }
        }
    }]'
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

The $userStreamAcl controls the default ACL for user streams, while all system streams use the $systemStreamAcl as the default.

TIP

The $w in $userStreamAcl also applies to the ability to create a stream. Members of $admins always have access to everything, you cannot remove this permission.

When you set a permission on a stream, it overrides the default values. However, it's not necessary to specify all permissions on a stream. It's only necessary to specify those which differ from the default.

Here is an example of the default ACL that has been changed:

{
  "$userStreamAcl": {
    "$r": "$all",
    "$w": "ouro",
    "$d": "ouro",
    "$mr": "ouro",
    "$mw": "ouro"
  },
  "$systemStreamAcl": {
    "$r": "$admins",
    "$w": "$admins",
    "$d": "$admins",
    "$mr": "$admins",
    "$mw": "$admins"
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

This default ACL gives ouro and $admins create and write permissions on all streams, while everyone else can read from them. Be careful allowing default access to system streams to non-admins as they would also have access to $settings unless you specifically override it.

Refer to the documentation of the HTTP API or SDK of your choice for more information about changing ACLs programmatically.

Trusted intermediary

The trusted intermediary header helps EventStoreDB to support a common security architecture. There are thousands of possible methods for handling authentication and it is impossible for us to support them all. The header allows you to configure a trusted intermediary to handle the authentication instead of EventStoreDB.

A sample configuration is to enable OAuth2 with the following steps:

  • Configure EventStoreDB to run on the local loopback.
  • Configure nginx to handle OAuth2 authentication.
  • After authenticating the user, nginx rewrites the request and forwards it to the loopback to EventStoreDB that serves the request.

The header has the form of {user}; group, group1 and the EventStoreDB ACLs use the information to handle security.

ES-TrustedAuth: "root; admin, other"
1

Use the following option to enable this feature:

FormatSyntax
Command line--enable-trusted-auth
YAMLEnableTrustedAuth
Environment variableEVENTSTORE_ENABLE_TRUSTED_AUTH

Default: none

Security options

On this page you find all the configuration options related to in-flight security of EventStoreDB.

Certificates configuration

SSL certificates can be created with a common name that includes the machine DNS name or IP address. You can instruct EventStoreDB to check the incoming connection certificate to have such a common name, so the node can be sure that it's another cluster node, which is trying to connect.

FormatSyntax
Command line--ssl-target-host
YAMLSslTargetHost
Environment variableEVENTSTORE_SSL_TARGET_HOST

Default: n/a

The certificate validation setting tells EventStoreDB to ensure that the incoming connection uses the trusted certificate. This setting should normally be set to true for production environments. It will only work if the cluster nodes use certificated signed by the trusted public CA. In case of self-signed certificates, the CA certificate needs to be added to the trusted certificate store of the machine.

FormatSyntax
Command line--ssl-validate-server
YAMLSslValidateServer
Environment variableEVENTSTORE_SSL_VALIDATE_SERVER

Default: true, so the server will only accept connection that use trusted certificates.

It is possible to configure cluster nodes to check if the connecting party is another node by setting the expected common name of the certificate. When the EventStoreDB server receives the incoming connection with a matching CN, it will assume it is another cluster node. Omitting this setting increases the risk of malicious actors connecting to the cluster and pretending to be cluster nodes.

FormatSyntax
Command line--certificate-subject-name
YAMLCertificateSubjectName
Environment variableEVENTSTORE_CERTIFICATE_SUBJECT_NAME

Other certificate settings determine the location of the certificate to be used by the cluster node. Configuration for the certificate location is platform-specific.

Windows

Check the SSL on Windows guidelines for detailed instructions.

The certificate store location is the location of the Windows certificate store, for example CurrentUser.

FormatSyntax
Command line--certificate-store-location
YAMLCertificateStoreLocation
Environment variableEVENTSTORE_CERTIFICATE_STORE_LOCATION

The certificate store name is the name of the Windows certificate store, for example My.

FormatSyntax
Command line--certificate-store-name
YAMLCertificateStoreName
Environment variableEVENTSTORE_CERTIFICATE_STORE_NAME

You need to add the certificate thumbprint setting on Windows so the server can ensure that it's using the correct certificate found in the certificates store.

FormatSyntax
Command line--certificate-thumbprint
YAMLCertificateThumbprint
Environment variableEVENTSTORE_CERTIFICATE_THUMBPRINT

Linux

Check the SSL on Linux guidelines for detailed instructions.

The CertificateFile setting needs to point to the certificate file, which will be used by the cluster node.

FormatSyntax
Command line--certificate-file
YAMLCertificateFile
Environment variableEVENTSTORE_CERTIFICATE_FILE

If the certificate file is protected by password, you'd need to set the CertificatePassword value accordingly, so the server can load the certificate.

FormatSyntax
Command line--certificate-password
YAMLCertificatePassword
Environment variableEVENTSTORE_CERTIFICATE_PASSWORD

Protocol configuration

EventStoreDB supports encryption for both TCP and HTTP. Each protocol can be made secure on its own.

Unlike HTTP, which will use the internal HTTP port setting for SSL connections, secure TCP port needs to be specified explicitly.

FormatSyntax
Command line--int-secure-tcp-port
YAMLIntSecureTcpPort
Environment variableEVENTSTORE_INT_SECURE_TCP_PORT

Default: 0, you need to specify the port for secure TCP to work.

If you use some kind of port mapping between the actual internal TCP port and the port, which is exposed to other machines in the subnet, you can use the IntSecureTcpPortAdvertiseAs setting to instruct EventStoreDB cluster node to use a different port value for the internal gossip.

FormatSyntax
Command line--int-secure-tcp-port-advertise-as
YAMLIntSecureTcpPortAdvertiseAs
Environment variableEVENTSTORE_INT_SECURE_TCP_PORT_ADVERTISE_AS

Default: 0, the internal secure TCP port will be advertised using the value of the IntSecureTcpPort setting.

For external clients to connect over the secure TLS channel, you'd need to set the ExtSecureTcpPort setting. Use the port value in the connection settings of your applications.

FormatSyntax
Command line--ext-secure-tcp-port
YAMLExtSecureTcpPort
Environment variableEVENTSTORE_EXT_SECURE_TCP_PORT

Default: 0, the setting must be set when enabling TLS.

As for the InternalTcpPortAdvertiseAs, the same can be done for the external TCP port if the exposed port on the machine doesn't match the port exposed to the outside world. The setting value will be used for the gossip endpoint response for connecting clients.

FormatSyntax
Command line--ext-securte-tcp-port-advertise-as
YAMLExtSecureTcpPortAdvertiseAs
Environment variableEVENTSTORE_EXT_SECURE_TCP_PORT_ADVERTISE_AS

Default: 0, so the external gossip endpoint will advertise the value specified in the ExtSecureTcpPort

You can fully disable insecure TCP connection by setting DisableInsecureTCP to false. By default, even if TLS is enabled, it would be still possible to connect via TCP without using TLS, unless this setting is changed.

FormatSyntax
Command line--disable-insecure-tcp=VALUE
YAMLDisableInsecureTCP
Environment variableEVENTSTORE_DISABLE_INSECURE_TCP

Default: false, so clients can connect via insecure TCP.

Internal secure communication

By default, cluster nodes use internal HTTP and TCP ports and communicate without applying any transport security. This can be changed by setting the UseInternalSsl setting to true.

FormatSyntax
Command line--use-internal-ssl
YAMLUseInternalSsl
Environment variableEVENTSTORE_USE_INTERNAL_SSL

Default: false, nodes within the cluster communicate over plain HTTP and TCP