Skip to content

[poc] SSL/TLS for standalone server #2598

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 24 commits into
base: master
Choose a base branch
from

Conversation

dare3path
Copy link

@dare3path dare3path commented Apr 12, 2025

EDIT: future updates of this patch will be here: https://github.com/dare3path/spacetimedb-tls-patch because it's becoming too dependent on locally cloned modified/patched repos(for crates like rustls-pki-types, hyper-util) and these shouldn't have to be like a requirement for this PR to work.

Description of Changes

(description is a bit outdated)

This is just a proof-of-concept patch to add TLS (and mTLS) support for the standalone server and thus addresses issue #1076
It's here in the hope the it might be useful for others, to give an idea, or show it's possible this way, but it's not intended to be merged (it's not of expected quality, it was made with grok3 and grok2 's help), it's just what I intend to use locally for myself before delving more deeply into SpacetimeDB. Thus, this PR is submitted as draft.

How to use after patched:

  • generate server private and public keys (maybe sign by your own local CA), can use this helper scripts for that https://github.com/dare3path/spacetimedb-cert-gen or just make sure you have the certificate (contains public key) server.crt and private key server.key and the certificate(contains public key) of the CA that signed the server's certificate as ca.crt or if it's a self-signed server certificate then use serverSS.crt instead(but you can't use server.crt(instead of ca.crt) because it was signed by a CA thus it's not self-signed) in all places where --cert is used (so where ca.crt and server.crt is used).
  • start the spacetimedb standalone server in TLS mode:
    spacetime start --edition standalone --listen-addr 127.0.0.1:3000 --ssl --cert ../spacetimedb-cert-gen/server.crt --key ../spacetimedb-cert-gen/server.key
    • can use --ssl, --tls, --https or --secure, they're aliases of the same thing.
  • start a rust client from a different terminal and connect to the server in TLS mode:
    cd ./crates/sdk/ && cargo run --example quickstart-chat -- --cert ../../../spacetimedb-cert-gen/ca.crt
    this will 404 if you haven't published it yet(see below)
  • use cli commands:
    spacetime version list
    spacetime version use 1.1.0
    spacetime server list
    spacetime server add --url https://127.0.0.1:3000 slocal --no-fingerprint
    spacetime server set-default slocal (can use https://127.0.0.1:3000 instead of slocal here)
    spacetime server list, should show this:
WARNING: This command is UNSTABLE and subject to breaking changes.

 DEFAULT  HOSTNAME                   PROTOCOL  NICKNAME  
          maincloud.spacetimedb.com  https     maincloud 
          127.0.0.1:3000             http      local     
     ***  127.0.0.1:3000             https     slocal    

assuming you're still inside ./crates/sdk/:

$ spacetime login --server-issued-login slocal --cert ../../../my/spacetimedb-cert-gen/ca.crt
WARNING: the server that you specified here as 'slocal' isn't the one that will be used by commands like 'spacetime publish' but instead it's the one listed on 'spacetime server list' as the default (3 stars) that will be used, eg. 127.0.0.1:3000 if you haven't manually added any via 'spacetime server add'.

We will log in directly to your target server.
We have logged in directly to your target server.
WARNING: This login will NOT work for any other servers.
Saving config to /home/user/.config/spacetime/cli.toml.
$ spacetime publish -p ../../modules/quickstart-chat/ --cert ../../../my/spacetimedb-cert-gen/ca.crt quickstart-chat
    Finished `release` profile [optimized] target(s) in 0.22s
Optimising module with wasm-opt...
Build finished successfully.
Uploading to slocal => https://127.0.0.1:3000
Publishing module...
Created new database with name: quickstart-chat, identity: c200ba48494b35ab616cea275571f895d0ac6bf6e16b391238bf59ec65d1410d
$ cargo run --example quickstart-chat -- --cert ../../../my/spacetimedb-cert-gen/ca.crt    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.48s
     Running `/home/user/SOURCE/SpacetimeDB/target/debug/examples/quickstart-chat --cert ../../../my/spacetimedb-cert-gen/ca.crt`
Fully connected and all subscriptions applied.
Use /name to set your name, or type a message!
User c2001e6a388eaebd connected.
message here
c2001e6a388eaebd: message here
message2 here
c2001e6a388eaebd: message2 here

(Ctrl+D(+Z on Windows) to exit, or Ctrl+C)

  • other cli commands you can run, in another terminal:
$ spacetime logs quickstart-chat --cert ../../../my/spacetimedb-cert-gen/ca.crt --follow
2025-04-13T05:10:45.901825Z  INFO: spacetimedb: Creating table `message`
2025-04-13T05:10:45.902506Z  INFO: spacetimedb: Creating table `user`
2025-04-13T05:10:45.904265Z  INFO: spacetimedb: Invoking `init` reducer
2025-04-13T05:10:45.906885Z  INFO: spacetimedb: Database initialized
$ spacetime server ping slocal --cert ../../../my/spacetimedb-cert-gen/ca.crt
WARNING: This command is UNSTABLE and subject to breaking changes.

Server is online: https://127.0.0.1:3000
$ spacetime server fingerprint slocal --cert ../../../my/spacetimedb-cert-gen/ca.crt
WARNING: This command is UNSTABLE and subject to breaking changes.

Fingerprint is unchanged for server slocal:
-----BEGIN PUBLIC KEY-----
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEfwAqvSbxMUZKdCsvzr9tdQJOhuG/
24jZ+oMN18FS6Py9CDNzGeAzu2kqjlybtpYRqkchYqv8o44khX9TZlhyWA==
-----END PUBLIC KEY-----

Can use all relevant cli commands (that I'm aware of) that require server access by passing args --cert ca.crt(or --cert serverSS.crt if server was started with --cert serverSS.crt --key server.key --ssl), otherwise, without --cert, you'd have to have the public certificate of the CA(certificate authority) that signed the server's public key in your cert root store (eg. somewhere in /etc/ssl/ on Linux).

API and ABI breaking changes

TODO: evaluate this.

Expected complexity level and risk

3

Testing

TODO:

  • zeroize private keys, on drop. (needs patched rustls-pki-types crate)
  • fix load_root_cert's bad logic for the 1MiB limit read file(ie. this is wrong if reader.limit() == 0 && reader.get_ref().get_ref().metadata().await.is_ok() {), just like read_file_limit got fixed already .
  • possibly other fixes seen in the location where this actually gets updated from now on.

@bfops
Copy link
Collaborator

bfops commented Apr 14, 2025

Thank you for putting this together as an example!

it applies to these subcommands:

spacetime server set-default
spacetime server add
spacetime server remove
spacetime server add
spacetime server edit
spacetime server fingerprint
spacetime server ping

and ensure they work correctly with a list of servers where protocol is
the only thing that differentiates them, eg.:
$ spacetime server list
WARNING: This command is UNSTABLE and subject to breaking changes.

 DEFAULT  HOSTNAME                   PROTOCOL  NICKNAME
          maincloud.spacetimedb.com  https     maincloud
          127.0.0.1:3000             http      local
          127.0.0.1:3000             https     slocal
Fix `spacetime server fingerprint` to handle both URLs (e.g., `https://127.0.0.1:3000`) and nicknames (e.g., `slocal`) without ambiguity errors.

Previously:
$ spacetime server fingerprint slocal --cert ../../../my/spacetimedb-cert-gen/ca.crt
WARNING: This command is UNSTABLE and subject to breaking changes.

Error: Protocol not specified and server slocal is ambiguous
and subscribe but it's broken, will fix in next commit.

also dedup build_client()
it makes 3 connections, each needed the cert from --cert
and make websocket.rs use it from lib
will thus avoid running out of memory via --cert /dev/zero
it wasn't using the 1MiB cert cap before.

Dedup cert loading in spacetime_server_fingerprint by reusing configure_tls
it's less confusing to align with the file naming from
spacetimedb-cert-gen repo
also restricts, for the server, the file size of certs to max 1MiB
not a bundle of them in the file(s)
mTLS=mutual TLS, client needs to send cert to be authenticated by the
server
mTLS is currently optional, depending on if the client args for client certs
are specified. Everything should work as before otherwise.

Improved the description of some connection errors, like when client cert
is required but you didn't specify the file args for it.
otherwise:

error[E0432]: unresolved import `crate::sys::IoSourceState`
  --> /home/user/.cargo/registry/src/index.crates.io-1949cf8c6b5b557f/mio-1.0.3/src/io_source.rs:14:5
   |
14 | use crate::sys::IoSourceState;
   |     ^^^^^^^^^^^^^^^^^^^^^^^^^ no `IoSourceState` in `sys`

tokio-rs/mio#1850
tokio-rs/mio#1681
tokio-rs/mio#1856

and something like this when openssl is also included:
  DEBUG: CMAKECONFIGDIR = /home/user/SOURCE/my/quickstart-chat/server/target/wasm32-unknown-unknown/release/build/openssl-sys-756c4c8b4d61ba02/out/openssl-build/install/lib/cmake/OpenSSL => CMAKECONFIGDIR = /home/user/SOURCE/my/quickstart-chat/server/target/wasm32-unknown-unknown/release/build/openssl-sys-756c4c8b4d61ba02/out/openssl-build/install/lib/cmake/OpenSSL, CMAKECONFIGDIR_REL_LIBDIR = cmake/OpenSSL
  In file included from apps/lib/app_provider.c:10:
  In file included from apps/include/apps.h:13:
  include/internal/common.h:14:11: fatal error: 'stdlib.h' file not found
     14 | # include <stdlib.h>
        |           ^~~~~~~~~~
  In file included from apps/lib/app_libctx.c:9:
  In file included from apps/include/app_libctx.h:13:
  In file included from include/openssl/types.h:32:
  include/openssl/e_os2.h:203:12: fatal error: 'sys/types.h' file not found
    203 | #  include <sys/types.h>
        |            ^~~~~~~~~~~~~
  apps/lib/app_x509.cIn file included from :apps/lib/app_params.c10::1010:
  :In file included from  apps/include/apps.hfatal error: :'string.h' file not found13
  :
  include/internal/common.h:14:11   :10  | fatal error: #'stdlib.h' file not foundi
  nclude    14< | s#t riinncgl.uhd>e
         <| s         ^~~~~~~~~~t
  dlib.h>
        |           ^~~~~~~~~~
  In file included from apps/lib/app_rand.c:10:
  In file included from include/internal/e_os.h:16:
  include/openssl/e_os2.h:203:12: fatal error: 'sys/types.h' file not found
    203 | #  include <sys/types.h>
        |            ^~~~~~~~~~~~~
  In file included from apps/lib/apps.c:21:
  In file included from include/openssl/engine.h:24:
  In file included from include/openssl/bn.h:20:
  include/openssl/e_os2.h:203:12: fatal error: 'sys/types.h' file not found
    203 | #  include <sys/types.h>
        |            ^~~~~~~~~~~~~
  1 error generated.
  make[1]: *** [Makefile:4000: apps/lib/libapps-lib-app_libctx.o] Error 1
  make[1]: *** Waiting for unfinished jobs....
  1 error generated.
  1 error generated.
  1 error generated.
  make[1]: *** [Makefile:4024: apps/lib/libapps-lib-app_rand.o] Error 1
  1 error generated.
  make[1]: *** [Makefile:4016: apps/lib/libapps-lib-app_provider.o] Error 1
  make[1]: *** [Makefile:4008: apps/lib/libapps-lib-app_params.o] Error 1
  make[1]: *** [Makefile:4032: apps/lib/libapps-lib-app_x509.o] Error 1
  1 error generated.
  make[1]: *** [Makefile:4040: apps/lib/libapps-lib-apps.o] Error 1
  make: *** [Makefile:2459: build_libs] Error 2

  Error building OpenSSL:
      'make' reported failure with exit status: 2
      Command failed: cd "/home/user/SOURCE/my/quickstart-chat/server/target/wasm32-unknown-unknown/release/build/openssl-sys-756c4c8b4d61ba02/out/openssl-build/build/src" && MAKEFLAGS="-j --jobserver-fds=8,9 --jobserver-auth=8,9" "make" "build_libs"

Error: command ["cargo", "build", "--config=net.git-fetch-with-cli=true", "--target=wasm32-unknown-unknown", "--release", "--message-format=json-render-diagnostics"] exited with code 101
@dare3path dare3path force-pushed the ssl_for_standalone_server branch from fec8a00 to be19440 Compare April 24, 2025 17:39
@CLAassistant
Copy link

CLAassistant commented May 3, 2025

CLA assistant check
All committers have signed the CLA.

@dare3path
Copy link
Author

dare3path commented May 3, 2025

erm, this isn't meant to be merged, it's just a proof-of-concept, also it's outdated and some things remained unfixed. I stopped updating it here(in this PR) because I wanted to modify some dep crates (eg. ring,pki-types) (for the purpose of zeroize-ing the private keys) and thus I've had to use modified forks of them which shouldn't be in this PR via [patch.crates-io] section.

Did anyone only want to run the CI on it? I should probably rebase it first, but please let me know.

EDIT: Ah that comment must just be because you've just added CLAassistant and it's auto-commenting with that on all PRs. Carry on:)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants