Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/OctoPrint/OctoPrint.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGina Häußge <gina@octoprint.org>2022-09-27 18:52:49 +0300
committerGina Häußge <gina@octoprint.org>2022-09-27 18:52:49 +0300
commitc33f8c2d08b9b10d026b8c114df95d702f89b3c7 (patch)
tree54a8bff0bb4c6e5c1ddef1b7c14890fa8b6e53a2
parentc76fee7804c3d6920c10b33eabe9f2dfbd3be525 (diff)
parentcf25c523ea556b92c19e113e05a49c210f9265e0 (diff)
Merge branch 'maintenance' into devel
-rw-r--r--.versioneer-lookup6
-rw-r--r--docs/api/push.rst2
-rw-r--r--src/octoprint/access/users.py33
-rw-r--r--src/octoprint/plugins/errortracking/__init__.py4
-rw-r--r--src/octoprint/schema/config/server.py4
-rw-r--r--src/octoprint/server/util/flask.py26
-rw-r--r--src/octoprint/server/util/sockjs.py1
-rw-r--r--src/octoprint/server/views.py20
-rw-r--r--src/octoprint/static/js/app/client/base.js10
-rw-r--r--src/octoprint/static/js/app/viewmodels/loginstate.js2
-rw-r--r--src/octoprint/static/js/reverse_proxy_test/reverse_proxy_test.js43
-rw-r--r--src/octoprint/templates/reverse_proxy_test.jinja2130
12 files changed, 252 insertions, 29 deletions
diff --git a/.versioneer-lookup b/.versioneer-lookup
index 561f90b8c..dbd4b7952 100644
--- a/.versioneer-lookup
+++ b/.versioneer-lookup
@@ -24,10 +24,10 @@ maintenance 1.9.0 fa2c4680cca0d1d6b46e6c1ed68ceea4d9b79afb pep440-dev
fix/.* 1.9.0 fa2c4680cca0d1d6b46e6c1ed68ceea4d9b79afb pep440-dev
improve/.* 1.9.0 fa2c4680cca0d1d6b46e6c1ed68ceea4d9b79afb pep440-dev
-# staging/bugfix is the branch for preparation of the 1.8.4 bugfix release
+# staging/bugfix is the branch for preparation of the 1.8.5 bugfix release
# so are any bug/... branches
-staging/bugfix 1.8.4 d0d226f3e0ad0a6436e4f32519bf38a82aa576ca pep440-dev
-bug/.* 1.8.4 d0d226f3e0ad0a6436e4f32519bf38a82aa576ca pep440-dev
+staging/bugfix 1.8.5 27a576e352a9b3d9abee89ab92cd02012c0e39dd pep440-dev
+bug/.* 1.8.5 27a576e352a9b3d9abee89ab92cd02012c0e39dd pep440-dev
# staging/maintenance is currently the branch for preparation of 1.8.0rc6
# so is regressionfix/...
diff --git a/docs/api/push.rst b/docs/api/push.rst
index a9796b663..158fb3984 100644
--- a/docs/api/push.rst
+++ b/docs/api/push.rst
@@ -30,7 +30,7 @@ following message types are currently available for usage by 3rd party clients:
* ``connected``: Initial connection information, sent only right after establishing the socket connection. See
:ref:`the payload data model <sec-api-push-datamodel-connected>`.
* ``reauthRequired``: A reauthentication of the current login session is required. The ``reason`` parameter in the
- payload defines whether a full active login is necessary (values ``logout`` and ``removed``) or a simple passive
+ payload defines whether a full active login is necessary (values ``logout``, ``stale``, ``removed``) or a simple passive
login will suffice (all other values).
* ``current``: Rate limited general state update, payload contains information about the printer's state, job progress,
accumulated temperature points and log lines since last update. OctoPrint will send these updates when new information
diff --git a/src/octoprint/access/users.py b/src/octoprint/access/users.py
index ec7c424fd..f3e96ee09 100644
--- a/src/octoprint/access/users.py
+++ b/src/octoprint/access/users.py
@@ -10,6 +10,7 @@ import uuid
import wrapt
from flask_login import AnonymousUserMixin, UserMixin
+from passlib.hash import pbkdf2_sha256
from werkzeug.local import LocalProxy
from octoprint.access.groups import Group, GroupChangeListener
@@ -19,10 +20,22 @@ from octoprint.util import atomic_write, deprecated, generate_api_key
from octoprint.util import get_fully_qualified_classname as fqcn
from octoprint.util import to_bytes, yaml
+password_hashers = []
+
try:
- from passlib.hash import argon2 as passwordhash
-except ImportError:
- from passlib.hash import pbkdf2_sha256 as passwordhash
+ from passlib.hash import argon2
+
+ # test if we can actually hash and verify, if not we won't use this backend
+ hash = argon2.hash("test")
+ argon2.verify("test", hash)
+
+ password_hashers.append(argon2)
+except Exception:
+ logging.getLogger(__name__).warning(
+ "Argon2 passlib backend is not available, not using it for password hashing"
+ )
+
+password_hashers.append(pbkdf2_sha256)
class UserManager(GroupChangeListener):
@@ -143,7 +156,7 @@ class UserManager(GroupChangeListener):
@staticmethod
def create_password_hash(password, *args, **kwargs):
- return passwordhash.hash(password)
+ return password_hashers[0].hash(password)
@staticmethod
def create_legacy_password_hash(password, salt):
@@ -1170,10 +1183,14 @@ class User(UserMixin):
if legacy:
return self._passwordHash == password
- try:
- return passwordhash.verify(password, self._passwordHash)
- except ValueError:
- return False
+ for password_hash in password_hashers:
+ if password_hash.identify(self._passwordHash):
+ try:
+ return password_hash.verify(password, self._passwordHash)
+ except ValueError:
+ pass
+
+ return False
def get_id(self):
return self.get_name()
diff --git a/src/octoprint/plugins/errortracking/__init__.py b/src/octoprint/plugins/errortracking/__init__.py
index 0c1ab7565..a9c4295b7 100644
--- a/src/octoprint/plugins/errortracking/__init__.py
+++ b/src/octoprint/plugins/errortracking/__init__.py
@@ -18,10 +18,10 @@ from octoprint.util.version import (
)
SENTRY_URL_SERVER = (
- "https://af30d06358144fb8af076ffd8984136d@o118517.ingest.sentry.io/1373987"
+ "https://289cde39ce7d410f82280cb5b0bb62e8@o118517.ingest.sentry.io/1373987"
)
SENTRY_URL_COREUI = (
- "https://e1d6b5d760f241408382d62a3e7fb416@o118517.ingest.sentry.io/1374096"
+ "https://af2c0c034c9044889b8cf06163ea00fc@o118517.ingest.sentry.io/1374096"
)
SETTINGS_DEFAULTS = {
diff --git a/src/octoprint/schema/config/server.py b/src/octoprint/schema/config/server.py
index 4a575abb0..4ac86204b 100644
--- a/src/octoprint/schema/config/server.py
+++ b/src/octoprint/schema/config/server.py
@@ -141,8 +141,8 @@ class CookiesConfig(BaseModel):
secure: bool = False
"""Whether to set the `Secure` flag to true on cookies. Only set to true if you are running OctoPrint behind a reverse proxy taking care of SSL termination."""
- samesite: Optional[SameSiteEnum] = "Strict"
- """`SameSite` setting to use on the cookies. Possible values are `None`, `Lax` and `Strict`. Defaults to `Strict`. Be advised that if forced unset, this has security implications as many browsers now default to `Lax` unless you configure cookies to be set with `Secure` flag set, explicitly set `SameSite` setting here and also serve OctoPrint over https. The `Lax` setting is known to cause with embedding OctoPrint in frames. See also ["Feature: Cookies default to SameSite=Lax"](https://www.chromestatus.com/feature/5088147346030592), ["Feature: Reject insecure SameSite=None cookies"](https://www.chromestatus.com/feature/5633521622188032) and [issue #3482](https://github.com/OctoPrint/OctoPrint/issues/3482)."""
+ samesite: Optional[SameSiteEnum] = SameSiteEnum.lax
+ """`SameSite` setting to use on the cookies. Possible values are `None`, `Lax` and `Strict`. Defaults to `Lax`. Be advised that if forced unset, this has security implications as many browsers now default to `Lax` unless you configure cookies to be set with `Secure` flag set, explicitly set `SameSite` setting here and also serve OctoPrint over https. The `Lax` setting is known to cause with embedding OctoPrint in frames. See also ["Feature: Cookies default to SameSite=Lax"](https://www.chromestatus.com/feature/5088147346030592), ["Feature: Reject insecure SameSite=None cookies"](https://www.chromestatus.com/feature/5633521622188032) and [issue #3482](https://github.com/OctoPrint/OctoPrint/issues/3482)."""
@with_attrs_docs
diff --git a/src/octoprint/server/util/flask.py b/src/octoprint/server/util/flask.py
index 60cc55260..d2b41ef5f 100644
--- a/src/octoprint/server/util/flask.py
+++ b/src/octoprint/server/util/flask.py
@@ -505,6 +505,20 @@ def decode_remember_me_cookie(value):
raise ValueError("Invalid remember me cookie")
+def get_cookie_suffix(request):
+ """
+ Request specific suffix for set and read cookies
+
+ We need this because cookies are not port-specific and we don't want to overwrite our
+ session and other cookies from one OctoPrint instance on our machine with those of another
+ one who happens to listen on the same address albeit a different port or script root.
+ """
+ result = "_P" + request.server_port
+ if request.script_root:
+ return result + "_R" + request.script_root.replace("/", "|")
+ return result
+
+
class OctoPrintFlaskRequest(flask.Request):
environment_wrapper = staticmethod(lambda x: x)
@@ -553,17 +567,7 @@ class OctoPrintFlaskRequest(flask.Request):
@cached_property
def cookie_suffix(self):
- """
- Request specific suffix for set and read cookies
-
- We need this because cookies are not port-specific and we don't want to overwrite our
- session and other cookies from one OctoPrint instance on our machine with those of another
- one who happens to listen on the same address albeit a different port or script root.
- """
- result = "_P" + self.server_port
- if self.script_root:
- return result + "_R" + self.script_root.replace("/", "|")
- return result
+ return get_cookie_suffix(self)
class OctoPrintFlaskResponse(flask.Response):
diff --git a/src/octoprint/server/util/sockjs.py b/src/octoprint/server/util/sockjs.py
index 0bc529f56..f6aa9a971 100644
--- a/src/octoprint/server/util/sockjs.py
+++ b/src/octoprint/server/util/sockjs.py
@@ -305,6 +305,7 @@ class PrinterStateConnection(
f"Unknown user/session combo: {user_id}:{user_session}"
)
self._on_logout()
+ self._sendReauthRequired("stale")
self._register()
diff --git a/src/octoprint/server/views.py b/src/octoprint/server/views.py
index 5c208105f..d836c3f38 100644
--- a/src/octoprint/server/views.py
+++ b/src/octoprint/server/views.py
@@ -331,6 +331,26 @@ def in_cache():
return abort(404)
+@app.route("/reverse_proxy_test")
+@app.route("/reverse_proxy_test/")
+def reverse_proxy_test():
+ from octoprint.server.util.flask import get_cookie_suffix, get_remote_address
+
+ remote_address = get_remote_address(request)
+ cookie_suffix = get_cookie_suffix(request)
+
+ return render_template(
+ "reverse_proxy_test.jinja2",
+ theming=[],
+ client_ip=remote_address,
+ server_protocol=request.environ.get("wsgi.url_scheme"),
+ server_name=request.environ.get("SERVER_NAME"),
+ server_port=request.environ.get("SERVER_PORT"),
+ server_path=request.script_root if request.script_root else "/",
+ cookie_suffix=cookie_suffix,
+ )
+
+
@app.route("/")
def index():
from octoprint.server import connectivityChecker, printer
diff --git a/src/octoprint/static/js/app/client/base.js b/src/octoprint/static/js/app/client/base.js
index 1cbf2e871..79870fd86 100644
--- a/src/octoprint/static/js/app/client/base.js
+++ b/src/octoprint/static/js/app/client/base.js
@@ -107,7 +107,7 @@
return url;
};
- OctoPrintClient.prototype.getCookieSuffix = function () {
+ OctoPrintClient.prototype.getParsedBaseUrl = function () {
if (!this.options.baseurl) return "";
try {
@@ -121,6 +121,14 @@
var url = new URL(parsed.protocol + "//" + parsed.host + path);
}
+ return url;
+ };
+
+ OctoPrintClient.prototype.getCookieSuffix = function () {
+ if (!this.options.baseurl) return "";
+
+ var url = this.getParsedBaseUrl();
+
var port = url.port || (url.protocol === "https:" ? 443 : 80);
if (url.pathname && url.pathname !== "/") {
var path = url.pathname;
diff --git a/src/octoprint/static/js/app/viewmodels/loginstate.js b/src/octoprint/static/js/app/viewmodels/loginstate.js
index e78f71cc2..6bfb286fb 100644
--- a/src/octoprint/static/js/app/viewmodels/loginstate.js
+++ b/src/octoprint/static/js/app/viewmodels/loginstate.js
@@ -308,7 +308,7 @@ $(function () {
};
self.onDataUpdaterReauthRequired = function (reason) {
- if (reason === "logout" || reason === "removed") {
+ if (reason === "logout" || reason === "stale" || reason === "removed") {
self.logout();
} else {
self.requestData();
diff --git a/src/octoprint/static/js/reverse_proxy_test/reverse_proxy_test.js b/src/octoprint/static/js/reverse_proxy_test/reverse_proxy_test.js
new file mode 100644
index 000000000..fcb991d3d
--- /dev/null
+++ b/src/octoprint/static/js/reverse_proxy_test/reverse_proxy_test.js
@@ -0,0 +1,43 @@
+$(function () {
+ //~~ OctoPrint client setup
+
+ var OctoPrint = window.OctoPrint;
+ OctoPrint.options.baseurl = BASE_URL;
+
+ //~~ Lodash setup
+
+ _.mixin({sprintf: sprintf, vsprintf: vsprintf});
+
+ //~~ View Model
+
+ function ReverseProxyTestViewModel() {
+ var self = this;
+
+ var url = OctoPrint.getParsedBaseUrl();
+ var cookieSuffix = OctoPrint.getCookieSuffix();
+ var protocol = url.protocol;
+ var path = url.pathname || "/";
+
+ if (path && path !== "/") {
+ if (path.endsWith("/")) {
+ path = path.substring(0, path.length - 1);
+ }
+ }
+
+ self.serverProtocol = protocol.substring(0, protocol.length - 1);
+ self.serverName = url.hostname;
+ self.serverPort = url.port || (url.protocol === "https:" ? 443 : 80);
+ self.serverPath = path;
+ self.cookieSuffix = cookieSuffix;
+
+ self.serverProtocolMatch = self.serverProtocol == SERVER_PROTOCOL;
+ self.serverNameMatch = self.serverName == SERVER_NAME;
+ self.serverPortMatch = self.serverPort == SERVER_PORT;
+ self.serverPathMatch = self.serverPath == SERVER_PATH;
+ self.cookieSuffixMatch = self.cookieSuffix == COOKIE_SUFFIX;
+ }
+
+ var viewModel = new ReverseProxyTestViewModel();
+
+ ko.applyBindings(viewModel, document.getElementById("reverse_proxy_test"));
+});
diff --git a/src/octoprint/templates/reverse_proxy_test.jinja2 b/src/octoprint/templates/reverse_proxy_test.jinja2
new file mode 100644
index 000000000..801038ec8
--- /dev/null
+++ b/src/octoprint/templates/reverse_proxy_test.jinja2
@@ -0,0 +1,130 @@
+<html>
+<head>
+ <meta name="viewport" content="width=device-width,initial-scale=1">
+ <title>OctoPrint Reverse Proxy Test</title>
+
+ <link rel="shortcut icon" href="{{ url_for('static', filename='img/tentacle-32x32.png') }}">
+ <link rel="mask-icon" href="{{ url_for('static', filename='img/mask.svg') }}" color="#56BE37">
+ <link rel="mask-icon-theme" href="{{ url_for('static', filename='img/mask-theme.svg') }}" color="#56BE37">
+ <link rel="apple-touch-icon" sizes="114x114" href="{{ url_for('static', filename='img/apple-touch-icon-114x114.png') }}">
+ <link rel="apple-touch-icon" sizes="144x144" href="{{ url_for('static', filename='img/apple-touch-icon-144x144.png') }}">
+
+ <!-- le CSS -->
+
+ <link rel="stylesheet" href="{{ url_for("static", filename="css/bootstrap.min.css") }}">
+ <link rel="stylesheet" href="{{ url_for("static", filename="css/bootstrap-responsive.min.css") }}">
+ <link rel="stylesheet" href="{{ url_for("static", filename="vendor/font-awesome-5.15.1/css/all.min.css") }}">
+ <link rel="stylesheet" href="{{ url_for("static", filename="vendor/font-awesome-5.15.1/css/v4-shims.min") }}">
+
+ {% for url in theming %}
+ <link rel="stylesheet" href="{{ url }}">
+ {% endfor %}
+
+ <!-- le javascript -->
+
+ <script>
+ var BASE_URL = "{{ url_for('index') }}";
+
+ var CLIENT_IP = "{{ client_ip }}";
+ var SERVER_PROTOCOL = "{{ server_protocol }}";
+ var SERVER_NAME = "{{ server_name }}";
+ var SERVER_PORT = {{ server_port }};
+ var SERVER_PATH = "{{ server_path }}";
+ var COOKIE_SUFFIX = "{{ cookie_suffix }}";
+ </script>
+ <script src="{{ url_for("static", filename="js/lib/jquery/jquery.min.js") }}"></script>
+ <script src="{{ url_for("static", filename="js/lib/knockout.js") }}"></script>
+ <script src="{{ url_for("static", filename="js/lib/bootstrap/bootstrap.js") }}"></script>
+ <script src="{{ url_for("static", filename="js/lib/lodash.min.js") }}"></script>
+ <script src="{{ url_for("static", filename="js/lib/sprintf.min.js") }}"></script>
+
+ {% assets "js_client" %}
+ <script type="text/javascript" src="{{ ASSET_URL }}"></script>
+ {% endassets %}
+
+ <script src="{{ url_for("static", filename="js/app/helpers.js") }}"></script>
+ <script src="{{ url_for("static", filename="js/reverse_proxy_test/reverse_proxy_test.js") }}"></script>
+</head>
+<body>
+ <div id="navbar" class="navbar navbar-inverse navbar-static-top">
+ <div class="navbar-inner">
+ <span class="brand">OctoPrint Reverse Proxy Test</span>
+ </div>
+ </div>
+ <div id="reverse_proxy_test" class="container" style="padding-top: 1em">
+ <div class="alert alert-error" data-bind="visible: !cookieSuffixMatch">
+ <strong>Warning!</strong> The used cookie suffix doesn't match between client and server. That means that OctoPrint's
+ UI won't work properly. Please check your configuration and fix it.
+ </div>
+
+ <p>
+ This page helps you identify issues in your reverse proxy configuration. It will show you information about
+ some of the variables that OctoPrint uses to set cookies and generate fully qualified URLs. If you see
+ any mismatches highlighted in red below, that means that something is not configured properly and you
+ need to fix it.
+ </p>
+
+ <p>
+ The table also shows you the client IP that your server saw you as. Make sure that this client IP is what
+ OctoPrint should be seeing, and not for example your reverse proxy itself.
+ </p>
+
+ <p>
+ For each variable, information is displayed what influences the value the server sees and thus where to
+ start debugging in case of any observed mismatches.
+ </p>
+
+ <table class="table table-bordered table-hover">
+ <thead>
+ <th>Variable</th>
+ <th>Source</th>
+ <th>Client</th>
+ <th>Server</th>
+ </thead>
+ <tr>
+ <td>Client IP</td>
+ <td><code>X-Forwarded-For</code></td>
+ <td>-</td>
+ <td>{{ client_ip }}</td>
+ </tr>
+ <tr data-bind="css: { success: serverProtocolMatch, error: !serverProtocolMatch }">
+ <td>Protocol</td>
+ <td><code>X-Forwarded-Protocol</code>, <code>X-Scheme</code> or config</td>
+ <td data-bind="text: serverProtocol"></td>
+ <td>{{ server_protocol }}</td>
+ </tr>
+ <tr data-bind="css: { success: serverNameMatch, error: !serverNameMatch }">
+ <td>Host</td>
+ <td><code>X-Forwarded-Host</code>, <code>Host</code>, <code>X-Forwarded-Server</code> or config</td>
+ <td data-bind="text: serverName"></td>
+ <td>{{ server_name }}</td>
+ </tr>
+ <tr data-bind="css: { success: serverPortMatch, error: !serverPortMatch }">
+ <td>Port</td>
+ <td><code>X-Forwarded-Host</code>, <code>Host</code>, <code>X-Forwarded-Port</code>, <code>X-Forwarded-Protocol</code>, <code>X-Scheme</code> or config</td>
+ <td data-bind="text: serverPort"></td>
+ <td>{{ server_port }}</td>
+ </tr>
+ <tr data-bind="css: { success: serverPathMatch, error: !serverPathMatch }">
+ <td>Path</td>
+ <td><code>X-Script-Name</code> or config</td>
+ <td data-bind="text: serverPath"></td>
+ <td>{{ server_path }}</td>
+ </tr>
+ <tr data-bind="css: { success: cookieSuffixMatch, error: !cookieSuffixMatch }">
+ <td>Cookie Suffix</td>
+ <td>Built from port & path</td>
+ <td data-bind="text: cookieSuffix"></td>
+ <td>{{ cookie_suffix }}</td>
+ </tr>
+ </table>
+
+ <p>
+ Please refer to <a href="https://faq.octoprint.org/reverse-proxy" target="_blank" rel="noopener noreferrer">the Reverse Proxy FAQ entry</a>
+ for information on correct configuration and examples.
+ </p>
+
+ </div>
+
+</body>
+</html>