Skip to content

feat(cloud_server): implement SSL secure websocket from semver2 into semver3 - #723

Open
Boss-1s wants to merge 21 commits into
TimMcCool:mainfrom
Boss-1s:semver3-secure-ws
Open

feat(cloud_server): implement SSL secure websocket from semver2 into semver3#723
Boss-1s wants to merge 21 commits into
TimMcCool:mainfrom
Boss-1s:semver3-secure-ws

Conversation

@Boss-1s

@Boss-1s Boss-1s commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Resolves nothing

Changes

  • Implemented SSL secure ws server (fix cloud server #658) from semver2 into semver 3
  • New class _SaWebSockectServer BaseWebSocketServer in sa.eventhandlers._base to keep things pythonic and organized

Tests

First, start up an insecure server to ensure it works:

import scratchattach as sa
from scratchattach.cloud.cloud import CustomCloud
import logging
from rich.traceback import install

install(show_locals=True)

# Init Server & Cloud Events

server_ip = '127.0.0.1'
server_port = 8080
project_id="1321224718"

server = sa.init_cloud_server("127.0.0.1",
                              server_port,
                              length_limit=65536,
                              allow_non_numeric=True,
                              whitelisted_projects=[project_id],
                              allow_nonscratch_names=False,
                              blocked_ips=["127.0.0.2"],
                              sync_players=True,
                              log_var_sets=True
                              )
server.start()


banned_cloud = CustomCloud(project_id='1234567',
                    cloud_host=f"ws://{server_ip}:{server_port}",
                    username = "Boss_1s",
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

bad_user_name_cloud = CustomCloud(project_id=project_id,
                    cloud_host=f"ws://{server_ip}:{server_port}",
                    username = "Boss_asdfghjkl",
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

cloud = CustomCloud(project_id=project_id,
                    cloud_host=f"ws://{server_ip}:{server_port}",
                    username = "Boss_1s",
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

no_user_cloud = CustomCloud(project_id=project_id,
                    cloud_host=f"ws://{server_ip}:{server_port}",
                    username = None,
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

no_project_cloud = CustomCloud(project_id=None,
                    cloud_host=f"ws://{server_ip}:{server_port}",
                    username="Boss_1s",
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

banned_cloud_events=banned_cloud.events()
bad_user_name_cloud_events=bad_user_name_cloud.events()
cloud_events=cloud.events()
no_user_cloud_events=no_user_cloud.events()
no_project_cloud_events=no_project_cloud.events()

While the above script is running, run websocat ws://127.0.0.1:8080 to interact with the websocket.

Once you have determined that this works, replace the above script with the one below:

import scratchattach as sa
from scratchattach.cloud.cloud import CustomCloud
import logging
from rich.traceback import install

install(show_locals=True)

# Init Server & Cloud Events

server_ip = '127.0.0.1'
server_port = 8080
project_id="1321224718"

# Test with ssl and make sure it works

server = sa.init_ssl_cloud_server("127.0.0.1",
                              server_port,
                              length_limit=65536,
                              allow_non_numeric=True,
                              whitelisted_projects=[project_id],
                              allow_nonscratch_names=False,
                              blocked_ips=["127.0.0.2"],
                              sync_players=True,
                              log_var_sets=True,
                              certfile="certfile.pem",
                              keyfile="keyfile.pem"
                              )
server.start()


banned_cloud = CustomCloud(project_id='1234567',
                    cloud_host=f"wss://{server_ip}:{server_port}",
                    username = "Boss_1s",
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

bad_user_name_cloud = CustomCloud(project_id=project_id,
                    cloud_host=f"wss://{server_ip}:{server_port}",
                    username = "Boss_asdfghjkl",
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

cloud = CustomCloud(project_id=project_id,
                    cloud_host=f"wss://{server_ip}:{server_port}",
                    username = "Boss_1s",
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

no_user_cloud = CustomCloud(project_id=project_id,
                    cloud_host=f"wss://{server_ip}:{server_port}",
                    username = None,
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

no_project_cloud = CustomCloud(project_id=None,
                    cloud_host=f"wss://{server_ip}:{server_port}",
                    username="Boss_1s",
                    length_limit = None,
                    allow_non_numeric = True,
                    _session = None,
                    header = None,
                    cookie = None,
                    origin = None,
                    print_connect_messages = True
                    )

banned_cloud_events=banned_cloud.events()
bad_user_name_cloud_events=bad_user_name_cloud.events()
cloud_events=cloud.events()
no_user_cloud_events=no_user_cloud.events()
no_project_cloud_events=no_project_cloud.events()

Then, generate an OpenSSL certificate:

openssl req -x509 -newkey rsa:4096 -keyout keyfile.pem -out certfile.pem -days 365 -nodes

Finally, run the server script, and interact with the WebSocket with websocat -k wss://127.0.0.1:8080

Notes

might wanna have #722 fixed before merging this, idk

…semver3

Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: GitHub <noreply@github.com>
- Moved `BaseCloudServer` (Formerly `_SaCloudServer`) to sa.eventhandlers._base

Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: GitHub <noreply@github.com>
@Boss-1s

Boss-1s commented Aug 30, 2026

Copy link
Copy Markdown
Contributor Author

@TheCommCraft

Comment thread scratchattach/eventhandlers/_base.py Outdated
Comment thread scratchattach/eventhandlers/_base.py Outdated
Comment thread scratchattach/eventhandlers/_base.py
Comment thread scratchattach/eventhandlers/cloud_server.py
Comment thread scratchattach/eventhandlers/cloud_server.py
Comment thread scratchattach/eventhandlers/cloud_server.py
Comment thread scratchattach/eventhandlers/cloud_server.py Outdated
Comment thread scratchattach/__init__.py Outdated
Comment thread scratchattach/eventhandlers/cloud_server.py
@TheCommCraft

Copy link
Copy Markdown
Collaborator

Tell me if you require help in applying these changes

(cherry picked from commit 8d30697)
Signed-off-by: GitHub <noreply@github.com>
### sa.eventhandlers._base

- changed list comp to generator comp in set_project_vars()  and set_var()
- reimplemented attribute type hints into BaseCloudServer. since type hints are inhierted, there is no need to restate them in child classes of BaseCloudServer.

### sa.eventhandlers.cloud_server

- added type hitns to __init__ of BaseCloudServer child classes
- added type hints to init_cloud_server and init_ssl_cloud_server
- revert changing warnings.warn to print in 9863d50

Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: GitHub <noreply@github.com>
Comment thread scratchattach/eventhandlers/cloud_server.py Outdated
Comment thread scratchattach/eventhandlers/_base.py Outdated
Comment thread scratchattach/eventhandlers/_base.py Outdated
Co-authored-by: TheCommCraft <79996518+TheCommCraft@users.noreply.github.com>
Signed-off-by: Boss_1s <95505913+Boss-1s@users.noreply.github.com>
Signed-off-by: Boss_1s <95505913+Boss-1s@users.noreply.github.com>
Signed-off-by: Boss_1s <95505913+Boss-1s@users.noreply.github.com>
Comment thread scratchattach/eventhandlers/_base.py Outdated
Comment thread scratchattach/eventhandlers/_base.py Outdated
Comment thread scratchattach/eventhandlers/cloud_server.py Outdated
Comment thread scratchattach/eventhandlers/_base.py Outdated
Comment thread scratchattach/eventhandlers/_base.py Outdated
Comment thread scratchattach/eventhandlers/cloud_server.py Outdated
Comment thread scratchattach/eventhandlers/cloud_server.py Outdated
Comment thread scratchattach/eventhandlers/_base.py Outdated
)
)

def set_var(self, project_id, var_name, value, *, user="@server", skip_forward=None):

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe we should change the server to store the variables with their actual names and then add the option to skip adding the cloud prefix

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Further examination is required to determine whether the current implementation works. I might have time to do this later but at the moment I am busy.

Comment thread scratchattach/eventhandlers/_base.py Outdated
Comment thread scratchattach/eventhandlers/_base.py Outdated
@TheCommCraft

Copy link
Copy Markdown
Collaborator

I'm just going to try and apply these changes myself

.
Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: Boss_1s <95505913+Boss-1s@users.noreply.github.com>
*,
user: str = "@server",
skip_forward=None,
skip_broadcast_for: WebSocket | None = None,

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add documentation or something of the sort for this argument?

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.

2 participants