Skip to content

Commit

Permalink
Mutual TLS (#445)
Browse files Browse the repository at this point in the history
  • Loading branch information
stefano-ottolenghi authored Nov 21, 2024
1 parent b3d4f38 commit b711d15
Show file tree
Hide file tree
Showing 4 changed files with 297 additions and 0 deletions.
75 changes: 75 additions & 0 deletions go-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,81 @@ You can switch users at both xref:query-simple.adoc#impersonation[query level] a
`TokenManager` implementations and providers must not interact with the driver in any way, as this can cause deadlocks and undefined behavior.


[#mtls]
[role=label--new-5.27 label--not-on-aura]
== Mutual TLS (client-side certificates as 2FA)

Mutual TLS (mTLS) allows you to use a client certificate as second factor for authenticating with the server.
The certificate can only be used together with an authentication token and is not a replacement of regular authentication, unless authentication is disabled on the server.

The client's certificate and public key must be placed in the server's `<NEO4J_HOME>/certificates/bolt/trusted` directory. For more information on server setup, see link:https://neo4j.com/docs/operations-manual/current/security/ssl-framework/#ssl-bolt-config[Configure SSL over Bolt].

[NOTE]
For mTLS to work, the driver's connection with the server must be encrypted, i.e. the xref:_connection_protocols_and_security[connection URI scheme] must be either `+s` or `+ssc` (ex. `neo4j+s://example.com:7687`).

[.tabbed-example]
=====
[.include-with-static-certificate]
======
Use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#NewStaticClientCertificateProvider[`auth.NewStaticClientCertificateProvider()`] for static certificates. +
The method takes a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#ClientCertificate[`ClientCertificate`] instance.

[source, go, test-skip]
----
certProvider, err := auth.NewStaticClientCertificateProvider(auth.ClientCertificate {
CertFile: "path/to/cert.pem",
KeyFile: "path/to/key.pem",
Password: "theCertPassword",
})
if err != nil {
log.Fatalf("Failed to load certificate: %v", err)
}
_, _ = neo4j.NewDriverWithContext(dbUri, neo4j.BasicAuth(dbUser, dbPassword, ""), func(config *config.Config) {
config.ClientCertificateProvider = certProvider
})
----

======
[.include-with-rotating-certificate]
======

Use link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#NewRotatingClientCertificateProvider[`auth.NewRotatingClientCertificateProvider()`] for rotating certificates. +
The method takes a link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#ClientCertificate[`ClientCertificate`] instance.

[source, go, test-skip]
----
password := "theCertPassword"
certProvider, err := auth.NewRotatingClientCertificateProvider(auth.ClientCertificate {
CertFile: "path/to/cert.pem",
KeyFile: "path/to/key.pem",
Password: &password,
})
if err != nil {
log.Fatalf("Failed to load certificate: %v", err)
}
_, _ = neo4j.NewDriverWithContext(dbUri, neo4j.BasicAuth(dbUser, dbPassword, ""), func(config *config.Config) {
config.ClientCertificateProvider = certProvider
})
// use the driver a bit...
// when it's time to rotate the certificate...
err = provider.UpdateCertificate(auth.ClientCertificate {
CertFile: "path/to/new_cert.pem",
KeyFile: "path/to/new_key.pem",
Password: &password,
})
if err != nil {
log.Fatalf("Failed to update certificate: %v", err)
}
// use the driver again...
----

======
=====


For more information, see link:https://pkg.go.dev/github.com/neo4j/neo4j-go-driver/v5/neo4j/auth#ClientCertificateProvider[API docs -> `ClientCertificateProvider`].


== Custom address resolver

When creating a `DriverWithContext` object, you can specify a _resolver_ function to resolve the connection address the driver is initialized with.
Expand Down
62 changes: 62 additions & 0 deletions java-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,68 @@ You can switch users at both xref:query-simple.adoc#impersonation[query level] a
`AuthTokenManager` objects must not interact with the driver in any way, as this can cause deadlocks and undefined behavior.


[#mtls]
[role=label--new-5.27 label--not-on-aura]
== Mutual TLS (client-side certificates as 2FA)

Mutual TLS (mTLS) allows you to use a client certificate as second factor for authenticating with the server.
The certificate can only be used together with an authentication token and is not a replacement of regular authentication, unless authentication is disabled on the server.

The client's certificate and public key must be placed in the server's `<NEO4J_HOME>/certificates/bolt/trusted` directory. For more information on server setup, see link:https://neo4j.com/docs/operations-manual/current/security/ssl-framework/#ssl-bolt-config[Configure SSL over Bolt].

[NOTE]
For mTLS to work, the driver's connection with the server must be encrypted, i.e. the xref:_connection_protocols_and_security[connection URI scheme] must be either `+s` or `+ssc` (ex. `neo4j+s://example.com:7687`).

Use link:https://neo4j.com/docs/api/java-driver/current/org.neo4j.driver/org/neo4j/driver/ClientCertificateManagers.html[`ClientCertificateManagers.rotating()`] for both static and rotating certificates. +
The method takes a link:https://neo4j.com/docs/api/java-driver/current/org.neo4j.driver/org/neo4j/driver/ClientCertificates.html[`ClientCertificate`] instance. +
For rotating certificates, use the `.rotate()` method; static certificates don't need to be updated.

[.tabbed-example]
=====
[.include-with-static-certificate]
======

[source, java, test-skip]
----
var certificateFile = new File("/path/to/cert.pem");
var privateKeyFile = new File("/path/to/key.pem");
var keyPassword = "password"; // optional
var certificate = ClientCertificates.of(certificateFile, privateKeyFile, keyPassword);
var certificateManager = ClientCertificateManagers.rotating(certificate);
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword), certificateManager)) {
// use the driver
}
----

======
[.include-with-rotating-certificate]
======

[source, java, test-skip]
----
var certificateFile = new File("/path/to/cert.pem");
var privateKeyFile = new File("/path/to/key.pem");
var keyPassword = "password"; // optional
var certificate = ClientCertificates.of(certificateFile, privateKeyFile, keyPassword);
// instantiate the rotating certificate with an initial one
var certificateManager = ClientCertificateManagers.rotating(certificate);
try (var driver = GraphDatabase.driver(dbUri, AuthTokens.basic(dbUser, dbPassword), certificateManager)) {
// use the driver...
// ... until it's time to rotate the certificate
var updatedCertificate = ClientCertificates.of(
new File("/path/to/new/cert.pem")
new File("/path/to/new/key.pem"),
"newPassword" // optional
);
certificateManager.rotate(updatedCertificate);
// use the driver some more - new connections are opened with the new certificate
}
----

======
=====


== Logging

By default, the driver logs `INFO` messages through the Java logging framework `java.util.logging`.
Expand Down
62 changes: 62 additions & 0 deletions javascript-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,68 @@ You can switch users at both xref:query-simple.adoc#impersonation[query level] a
`AuthManagers` (including provider functions passed to `expirationBasedAuthTokenManager()`) must not interact with the driver in any way, as this can cause deadlocks and undefined behavior.


[#mtls]
[role=label--new-5.27 label--not-on-aura]
== Mutual TLS (client-side certificates as 2FA)

Mutual TLS (mTLS) allows you to use a client certificate as second factor for authenticating with the server.
The certificate can only be used together with an authentication token and is not a replacement of regular authentication, unless authentication is disabled on the server.

The client's certificate and public key must be placed in the server's `<NEO4J_HOME>/certificates/bolt/trusted` directory. For more information on server setup, see link:https://neo4j.com/docs/operations-manual/current/security/ssl-framework/#ssl-bolt-config[Configure SSL over Bolt].

[NOTE]
For mTLS to work, the driver's connection with the server must be encrypted, i.e. the xref:_connection_protocols_and_security[connection URI scheme] must be either `+s` or `+ssc` (ex. `neo4j+s://example.com:7687`).

[.tabbed-example]
=====
[.include-with-static-certificate]
======
Use the driver configuration option link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/types.js~Config.html#instance-member-clientCertificate[`clientCertificate`] to provide the certificate information as an object.

[source, javascript, test-skip]
----
const driver = neo4j.driver(URI, neo4j.auth.basic(USER, PASSWORD), {
clientCertificate: {
certfile: '/path/to/cert.cert',
keyfile: '/path/to/cert.pem',
password: 'the_key_password' // optional
}
})
----

======
[.include-with-rotating-certificate]
======

Instantiate the certificate object via link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/client-certificate.js~ClientCertificateProviders.html[`clientCertificateProviders.rotating`] and provide it when instantiating the driver via the configuration option link:https://neo4j.com/docs/api/javascript-driver/current/class/lib6/types.js~Config.html#instance-member-clientCertificate[`clientCertificate`].

[source, javascript, test-skip]
----
const initialClientCertificate: {
certfile: '/path/to/cert.cert',
keyfile: '/path/to/cert.pem',
password: 'the_key_password' // optional
}
const clientCertificateProvider = neo4j.clientCertificateProviders.rotating({
initialCertificate: initialClientCertificate
})
const driver = neo4j.driver(URI, MY_CREDENTIALS, {
clientCertificate: clientCertificateProvider
})
// use the driver...
// ... until it's time to update the certificate
clientCertificateProvider.updateCertificate({
certfile: '/path/to/new_cert.cert',
keyfile: '/path/to/new_cert.pem',
password: 'the_new_key_password' // optional
})
// use the driver some more
----

======
=====


== Custom address resolver

When creating a `Driver` object, you can specify a _resolver_ function to resolve the connection address the driver is initialized with.
Expand Down
98 changes: 98 additions & 0 deletions python-manual/modules/ROOT/pages/connect-advanced.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,104 @@ You can switch users at both xref:query-simple.adoc#impersonation[query level] a
`AuthManagers` (including provider functions passed to `AuthManagers.expiration_based()`) must not interact with the driver in any way, as this can cause deadlocks and undefined behavior.


[#mtls]
[role=label--new-5.27 label--not-on-aura]
== Mutual TLS (client-side certificates as 2FA)

Mutual TLS (mTLS) allows you to use a client certificate as second factor for authenticating with the server.
The certificate can only be used together with an authentication token and is not a replacement of regular authentication, unless authentication is disabled on the server.

The client's certificate and public key must be placed in the server's `<NEO4J_HOME>/certificates/bolt/trusted` directory. For more information on server setup, see link:https://neo4j.com/docs/operations-manual/current/security/ssl-framework/#ssl-bolt-config[Configure SSL over Bolt].

[NOTE]
For mTLS to work, the driver's connection with the server must be encrypted, i.e. the xref:_connection_protocols_and_security[connection URI scheme] must be either `+s` or `+ssc` (ex. `neo4j+s://example.com:7687`).

[.tabbed-example]
=====
[.include-with-static-certificate]
======
Use link:https://neo4j.com/docs/api/python-driver/current/api.html#neo4j.auth_management.ClientCertificateProviders.static[`ClientCertificateProviders.static()`] for static certificates. +
The method takes a link:https://neo4j.com/docs/api/python-driver/current/api.html#neo4j.auth_management.ClientCertificate[`ClientCertificate`] instance, which takes the same parameters as Python's link:https://docs.python.org/3/library/ssl.html#ssl.SSLContext.load_cert_chain[`ssl.SSLContext.load_cert_chain()`].

[source, python, test-skip]
----
import neo4j
from neo4j.auth_management import (
ClientCertificate,
ClientCertificateProviders,
)
URI = "<URI for Neo4j database>"
AUTH = ("<Username>", "<Password>")
cert_provider = ClientCertificateProviders.static(
ClientCertificate(
# path to certificate
"path/to/cert.pem",
# path to private key (optional; needed if certificate does not contain private key too)
"path/to/key.pem",
# password to decrypt private key (can be function or string) (optional)
lambda: "password",
)
)
with neo4j.GraphDatabase.driver(
URI,
auth=AUTH,
client_certificate=cert_provider,
) as driver:
...
----

======
[.include-with-rotating-certificate]
======

Use link:https://neo4j.com/docs/api/python-driver/current/api.html#neo4j.auth_management.ClientCertificateProviders.rotating[`ClientCertificateProviders.rotating()`] for rotating certificates. +
The method takes a link:https://neo4j.com/docs/api/python-driver/current/api.html#neo4j.auth_management.ClientCertificate[`ClientCertificate`] instance.

[source, python, test-skip]
----
import neo4j
from neo4j.auth_management import (
ClientCertificate,
ClientCertificateProviders,
)
URI = "<URI for Neo4j database>"
AUTH = ("<Username>", "<Password>")
cert_provider = ClientCertificateProviders.rotating(
ClientCertificate(
# path to public certificate to load
"path/to/cert.pem",
# path to private key to load
"path/to/key.pem",
# password to decrypt private key (can be a function or string)
# see also Python's ssl.SSLContext.load_cert_chain()
lambda: "password",
)
)
driver = neo4j.GraphDatabase.driver(
URI
auth=(USERNAME, PASSWORD),
client_certificate=cert_provider
)
# use the driver...
# ... until the certificate needs to be rotated
cert_provider.update_certificate(
ClientCertificate(
certfile="path/to/new/cert.pem",
keyfile="path/to/new/key.pem",
password=lambda: "new_super_secret_password"
)
)
# use the driver again, until the certificate needs to be
# rotated again
# ...
----

======
=====

For more information, see link:https://neo4j.com/docs/api/python-driver/current/api.html#neo4j.auth_management.ClientCertificateProvider[API docs -> `ClientCertificateProvider`].


== Custom address resolver

When creating a `Driver` object, you can specify a _resolver_ function to resolve any addresses the driver receives ahead of DNS resolution.
Expand Down

0 comments on commit b711d15

Please sign in to comment.