Merge f5bf274a530430c075ab625e11e8b2a2ea87c81c into 749791cdf1316bc89415d27d503042d3f6b3f398

This commit is contained in:
Fijxu 2026-03-10 20:28:39 +08:00 committed by GitHub
commit c9098cabdb
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 58 additions and 7 deletions

View File

@ -151,6 +151,20 @@ db:
## ##
domain: domain:
##
## List of alternative domains where the invidious instance is being served.
## This needs to be set in order to be able to login and update user preferences
## when using a domain that is not the same as the `domain` configuration,
## like a .`onion` address, `.i2p` address, `.b32.i2p` address, etc.
##
## It will detect the alternative domain trough the `X-Forwarded-Host` header.
## https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host
##
## Accepted values: a list of fully qualified domain names (FQDN)
## Default: <none>
##
alternative_domains:
## ##
## Tell Invidious that it is behind a proxy that provides only ## Tell Invidious that it is behind a proxy that provides only
## HTTPS, so all links must use the https:// scheme. This ## HTTPS, so all links must use the https:// scheme. This

View File

@ -120,6 +120,8 @@ class Config
property hmac_key : String = "" property hmac_key : String = ""
# Domain to be used for links to resources on the site where an absolute URL is required # Domain to be used for links to resources on the site where an absolute URL is required
property domain : String? property domain : String?
# Additional domain list that is going to be used for cookie domain validation
property alternative_domains : Array(String) = [] of String
# Subscribe to channels using PubSubHubbub (requires domain, hmac_key) # Subscribe to channels using PubSubHubbub (requires domain, hmac_key)
property use_pubsub_feeds : Bool | Int32 = false property use_pubsub_feeds : Bool | Int32 = false
property popular_enabled : Bool = true property popular_enabled : Bool = true

View File

@ -32,6 +32,8 @@ module Invidious::Routes::BeforeAll
env.response.headers["X-XSS-Protection"] = "1; mode=block" env.response.headers["X-XSS-Protection"] = "1; mode=block"
env.response.headers["X-Content-Type-Options"] = "nosniff" env.response.headers["X-Content-Type-Options"] = "nosniff"
env.set "header_x-forwarded-host", env.request.headers["X-Forwarded-Host"]?
# Only allow the pages at /embed/* to be embedded # Only allow the pages at /embed/* to be embedded
if env.request.resource.starts_with?("/embed") if env.request.resource.starts_with?("/embed")
frame_ancestors = "'self' file: http: https:" frame_ancestors = "'self' file: http: https:"

View File

@ -26,6 +26,7 @@ module Invidious::Routes::Login
def self.login(env) def self.login(env)
locale = env.get("preferences").as(Preferences).locale locale = env.get("preferences").as(Preferences).locale
host = env.get("header_x-forwarded-host")
referer = get_referer(env, "/feed/subscriptions") referer = get_referer(env, "/feed/subscriptions")
@ -57,7 +58,11 @@ module Invidious::Routes::Login
sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32)) sid = Base64.urlsafe_encode(Random::Secure.random_bytes(32))
Invidious::Database::SessionIDs.insert(sid, email) Invidious::Database::SessionIDs.insert(sid, email)
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid) if alt = CONFIG.alternative_domains.index(host)
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.alternative_domains[alt], sid)
else
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
end
else else
return error_template(401, "Wrong username or password") return error_template(401, "Wrong username or password")
end end
@ -123,7 +128,11 @@ module Invidious::Routes::Login
view_name = "subscriptions_#{sha256(user.email)}" view_name = "subscriptions_#{sha256(user.email)}"
PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}") PG_DB.exec("CREATE MATERIALIZED VIEW #{view_name} AS #{MATERIALIZED_VIEW_SQL.call(user.email)}")
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid) if alt = CONFIG.alternative_domains.index(host)
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.alternative_domains[alt], sid)
else
env.response.cookies["SID"] = Invidious::User::Cookies.sid(CONFIG.domain, sid)
end
if env.request.cookies["PREFS"]? if env.request.cookies["PREFS"]?
user.preferences = env.get("preferences").as(Preferences) user.preferences = env.get("preferences").as(Preferences)

View File

@ -226,7 +226,12 @@ module Invidious::Routes::PreferencesRoute
File.write("config/config.yml", CONFIG.to_yaml) File.write("config/config.yml", CONFIG.to_yaml)
end end
else else
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.domain, preferences) host = env.get("header_x-forwarded-host")
if alt = CONFIG.alternative_domains.index(host)
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.alternative_domains[alt], preferences)
else
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.domain, preferences)
end
end end
env.redirect referer env.redirect referer
@ -261,7 +266,12 @@ module Invidious::Routes::PreferencesRoute
preferences.dark_mode = "dark" preferences.dark_mode = "dark"
end end
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.domain, preferences) host = env.get("header_x-forwarded-host")
if alt = CONFIG.alternative_domains.index(host)
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.alternative_domains[alt], preferences)
else
env.response.cookies["PREFS"] = Invidious::User::Cookies.prefs(CONFIG.domain, preferences)
end
end end
if redirect if redirect

View File

@ -6,17 +6,24 @@ struct Invidious::User
# Note: we use ternary operator because the two variables # Note: we use ternary operator because the two variables
# used in here are not booleans. # used in here are not booleans.
SECURE = (Kemal.config.ssl || CONFIG.https_only) ? true : false @@secure = (Kemal.config.ssl || CONFIG.https_only) ? true : false
# Session ID (SID) cookie # Session ID (SID) cookie
# Parameter "domain" comes from the global config # Parameter "domain" comes from the global config
def sid(domain : String?, sid) : HTTP::Cookie def sid(domain : String?, sid) : HTTP::Cookie
# Not secure if it's being accessed from I2P
# Browsers expect the domain to include https. On I2P there is no HTTPS
# Tor browser works fine with secure being true
if (domain.try &.split(".").last == "i2p") && @@secure
@@secure = false
end
return HTTP::Cookie.new( return HTTP::Cookie.new(
name: "SID", name: "SID",
domain: domain, domain: domain,
value: sid, value: sid,
expires: Time.utc + 2.years, expires: Time.utc + 2.years,
secure: SECURE, secure: @@secure,
http_only: true, http_only: true,
samesite: HTTP::Cookie::SameSite::Lax samesite: HTTP::Cookie::SameSite::Lax
) )
@ -25,12 +32,19 @@ struct Invidious::User
# Preferences (PREFS) cookie # Preferences (PREFS) cookie
# Parameter "domain" comes from the global config # Parameter "domain" comes from the global config
def prefs(domain : String?, preferences : Preferences) : HTTP::Cookie def prefs(domain : String?, preferences : Preferences) : HTTP::Cookie
# Not secure if it's being accessed from I2P
# Browsers expect the domain to include https. On I2P there is no HTTPS
# Tor browser works fine with secure being true
if (domain.try &.split(".").last == "i2p") && @@secure
@@secure = false
end
return HTTP::Cookie.new( return HTTP::Cookie.new(
name: "PREFS", name: "PREFS",
domain: domain, domain: domain,
value: URI.encode_www_form(preferences.to_json), value: URI.encode_www_form(preferences.to_json),
expires: Time.utc + 2.years, expires: Time.utc + 2.years,
secure: SECURE, secure: @@secure,
http_only: false, http_only: false,
samesite: HTTP::Cookie::SameSite::Lax samesite: HTTP::Cookie::SameSite::Lax
) )