Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 10 additions & 8 deletions deploy/nginx/check-config.sh
Original file line number Diff line number Diff line change
Expand Up @@ -721,13 +721,14 @@ for probe in "GET /ru/get-started?a=1" "POST /snapshots" "GET /api/v1/wizard/gk7
fail=1
fi
done
# On port 80 only ipctool's downloads go, to the mirror's own port 80; the
# report upload stays, since ipctool follows no redirect.
# On port 80 ipctool's downloads and its report go to the mirror's own port
# 80 -- the report with a 307, so the POST follows as it is.
plain_blocked() {
curl -sS -o /dev/null -w '%{http_code} %{redirect_url}' --max-time 5 --interface 127.0.0.3 \
--resolve "openipc.org:80:127.0.0.1" "$@" 2>/dev/null
}
for probe in "302 GET /ipctool http://openipc.ru/ipctool" "302 GET /ipctool-mips32 http://openipc.ru/ipctool-mips32"; do
for probe in "302 GET /ipctool http://openipc.ru/ipctool" "302 GET /ipctool-mips32 http://openipc.ru/ipctool-mips32" \
"307 POST /api/v1/reports http://openipc.ru/api/v1/reports"; do
set -- $probe
got=$(plain_blocked -X "$2" "http://openipc.org$3")
if [ "$got" = "$1 $4" ]; then
Expand All @@ -737,14 +738,15 @@ for probe in "302 GET /ipctool http://openipc.ru/ipctool" "302 GET /ipctool-mips
fail=1
fi
done
# The report has to reach the service (the stub answers 200, with the
# location's X-Served-By): a redirect, a 5xx or no answer means it does not.
# Identify stays: it is a few kilobytes, which the freeze lets through. It
# has to reach the service (the stub answers 200, with the location's
# X-Served-By): a redirect, a 5xx or no answer means it does not.
got=$(curl -sS -o /dev/null -D - --max-time 5 --interface 127.0.0.3 --resolve "openipc.org:80:127.0.0.1" \
-X POST "http://openipc.org/api/v1/reports" 2>/dev/null | tr -d '\r' | awk 'NR==1{s=$2} tolower($1)=="x-served-by:"{b=$2} END{print s" "b}')
-X POST "http://openipc.org/api/v1/boards/identify" 2>/dev/null | tr -d '\r' | awk 'NR==1{s=$2} tolower($1)=="x-served-by:"{b=$2} END{print s" "b}')
if [ "$got" = "200 go" ]; then
printf ' %-32s %-5s (POST over HTTP from a blocked network, reaches the service)\n' /api/v1/reports 200
printf ' %-32s %-5s (POST over HTTP from a blocked network, reaches the service)\n' /api/v1/boards/identify 200
else
printf ' %-32s %-5s MISMATCH: a report over HTTP from a blocked network does not reach the service\n' /api/v1/reports "$got"
printf ' %-32s %-5s MISMATCH: identify over HTTP from a blocked network does not reach the service\n' /api/v1/boards/identify "$got"
fail=1
fi
expect $FW 200 go hsts
Expand Down
12 changes: 7 additions & 5 deletions deploy/nginx/conf.d/openipc-blocked-nets.conf
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
# fails if an address in set_real_ip_from is missing here.
#
# Applied in sites-available/org.openipc's HTTPS server, and on port 80 to
# the ipctool downloads alone, which go to http://openipc.ru: stock camera
# firmware has no TLS, and openipc.ru's port 80 proxies them from here
# (mirrors/ru.openipc.snippet). Port 80's report upload is left alone, because
# ipctool follows no redirect: on openipc.ru the report page tells it
# `--host openipc.ru`, and a report alone is small enough to get through.
# ipctool's downloads (302) and its report upload (307, so the POST and its
# body follow), which go to http://openipc.ru: stock camera firmware has no
# TLS, and openipc.ru's port 80 proxies them from here
# (mirrors/ru.openipc.snippet). ipctool since OpenIPC/ipctool#233 reads the
# 307 before sending the body and follows it; an older one reports it and
# stops, and on openipc.ru the report page tells it `--host openipc.ru`.
# Identify is not redirected: it is a few kilobytes, which get through.
geo $realip_remote_addr $openipc_blocked_net {
default 0;
include /etc/nginx/conf.d/openipc-blocked-nets.list;
Expand Down
11 changes: 11 additions & 0 deletions deploy/nginx/sites-available/org.openipc
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,18 @@ server {
# ipctool uploads a report over plain HTTP: stock camera firmware has no
# TLS. Only the upload and identify are answered here; everything else
# redirects.
#
# A network the TSPU cuts off is sent to openipc.ru's port 80, which
# proxies the report back here: a flash backup never gets through the
# freeze, and the upload sat at "Sending 0%" until it timed out
# (ipctool#232). 307, so the POST and its body go as they are. ipctool
# since that issue waits a moment for an answer to the headers before it
# sends the body, and follows a plain-HTTP 307; an older one reports the
# 307 and stops, which is quicker than the freeze.
location = /api/v1/reports {
if ($openipc_blocked_net) {
return 307 http://openipc.ru$request_uri;
Comment thread
qodo-free-for-open-source-projects[bot] marked this conversation as resolved.
}
client_max_body_size 300m;
client_body_timeout 300;
proxy_request_buffering off;
Expand Down
10 changes: 7 additions & 3 deletions service/deploytest/blocked_nets_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ func TestBlockedNetsGoToTheMirror(t *testing.T) {
}
})

t.Run("the redirect is the HTTPS vhost's, and on port 80 only ipctool's", func(t *testing.T) {
t.Run("the redirect is the HTTPS vhost's, and on port 80 only ipctool's and its report's", func(t *testing.T) {
servers := strings.Split("\n"+directives(v), "\nserver {")
if len(servers) != 3 {
t.Fatalf("expected two server blocks in org.openipc, found %d", len(servers)-1)
Expand All @@ -87,8 +87,12 @@ func TestBlockedNetsGoToTheMirror(t *testing.T) {
tools := block(plain, "location ~ ^/(ipctool|ipctool-mips32|ipctool-arm64)$ {")
mustMatch(t, `if \(\$openipc_blocked_net\) \{\s*return 302 http://openipc\.ru\$request_uri;`, tools,
"a cut-off network fetching ipctool is not sent to the mirror's plain HTTP, where it gets through")
if strings.Count(plain, "openipc_blocked_net") != 1 {
t.Error("port 80 redirects more than ipctool: stock firmware sends its report there and follows no redirect")
// A 307, not a 302: the report is a POST and its body has to follow.
report := block(plain, "location = /api/v1/reports {")
mustMatch(t, `if \(\$openipc_blocked_net\) \{\s*return 307 http://openipc\.ru\$request_uri;`, report,
"a cut-off network's report is not sent to the mirror's plain HTTP, and freezes at its first kilobytes")
if strings.Count(plain, "openipc_blocked_net") != 2 {
t.Error("port 80 redirects more than ipctool and its report")
}
mustNotContain(t, directives(vhost(t, "org.openipc.dev")), "openipc_blocked_net",
"dev would send its testers to the production mirror")
Expand Down
Loading