Skip to content

Commit 94d3c05

Browse files
Merge upstream develop
2 parents 9e8f209 + 06cafc8 commit 94d3c05

8 files changed

Lines changed: 31 additions & 15 deletions

File tree

.gitlab/ci/deploy_services.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
- crane auth login $CI_REGISTRY -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD
88
- ENVIRONMENT_TAG="${CI_ENVIRONMENT_NAME%%/*}"
99
- crane tag $CI_REGISTRY_IMAGE/${SERVICE_NAME}:$CI_COMMIT_SHA $ENVIRONMENT_TAG
10+
- |
11+
if [ "$CI_COMMIT_BRANCH" = "$CI_DEFAULT_BRANCH" ]; then
12+
crane tag $CI_REGISTRY_IMAGE/${SERVICE_NAME}:latest $ENVIRONMENT_TAG
13+
fi
1014
1115
.service-deploy-manifest:
1216
stage: deploy

compose.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ services:
3232
# container itself, making Kafka unreachable
3333
- POLYCENTRIC_KAFKA_BROKERS=kafka:19092
3434
# Reached over the isolated `scraper_link` network (see below).
35-
- POLYCENTRIC_SCRAPER_URL=http://scraper:3002
35+
- POLYCENTRIC_SCRAPER_URL=http://scraper:8855
3636
# On both the shared app network and the isolated scraper link, so it can
3737
# talk to postgres/kafka/etc. AND to the scraper.
3838
networks:
@@ -71,7 +71,7 @@ services:
7171
- CMD
7272
- node
7373
- -e
74-
- fetch('http://localhost:3002/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))
74+
- fetch('http://localhost:8855/health').then(r=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))
7575
interval: 10s
7676
timeout: 5s
7777
retries: 5

services/scraper/.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
# Port the scraper HTTP API listens on. Defaults to 3002 when unset.
2-
PORT=3002
1+
# Port the scraper HTTP API listens on. Defaults to 8855 when unset.
2+
PORT=8855
33

44
# Path to the Chromium/Chrome binary that puppeteer/browserless launches for
55
# prerendering. The Docker image installs a system Chromium here; for local

services/scraper/Dockerfile

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,10 +56,11 @@ ENV NODE_ENV=production
5656
# System Chromium provided below; puppeteer/browserless launch it.
5757
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
5858
# PORT is supplied at runtime (compose / `docker run -e PORT=…`); the service
59-
# falls back to 3002 when unset.
59+
# falls back to 8855 when unset.
6060

6161
# Headless Chromium for prerendering (Debian `chromium` → /usr/bin/chromium),
62-
# plus nftables for the startup egress firewall.
62+
# nftables for the startup egress firewall, and curl for in-container debugging
63+
# (e.g. testing egress rules from inside the scraper's netns).
6364
RUN apt-get update \
6465
&& apt-get install -y --no-install-recommends chromium nftables \
6566
&& rm -rf /var/lib/apt/lists/*
@@ -76,5 +77,5 @@ RUN chmod +x /usr/local/bin/docker-entrypoint.sh
7677
# unprivileged `node` user to run the service. REQUIRES `--cap-add=NET_ADMIN`
7778
# (and `--init` is recommended so the headless-Chromium children get reaped).
7879
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]
79-
EXPOSE 3002
80+
EXPOSE 8855
8081
CMD ["node", "dist/index.js"]

services/scraper/egress.nft

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
#!/usr/sbin/nft -f
22
# Egress firewall applied inside the scraper container's network namespace at
3-
# startup (see docker-entrypoint.sh). Allows the public internet; drops NEW
4-
# connections to internal/reserved ranges. Because every connection the
5-
# headless browser makes is locally generated, the output hook covers it too.
3+
# startup (see docker-entrypoint.sh). Allows the public internet on HTTP/HTTPS
4+
# ports only; drops NEW connections to internal/reserved ranges and to non-web
5+
# ports. Because every connection the headless browser makes is locally
6+
# generated, the output hook covers it too.
67
#
78
# Do NOT `flush ruleset`: that would wipe Docker's embedded-DNS NAT rules in
89
# this netns and break name resolution. Replace only our own table (the
@@ -73,6 +74,16 @@ table inet egress {
7374
# Block NEW outbound connections to internal/reserved space.
7475
ip daddr @blocked4 drop
7576
ip6 daddr @blocked6 drop
76-
# policy accept -> NEW connections to public addresses pass.
77+
78+
# Of the remaining (public) destinations, the scraper only ever needs
79+
# HTTP/HTTPS. Drop any other NEW outbound so a malicious URL/redirect — or a
80+
# compromised scraper — can't reach public services on non-web ports
81+
# (port-scanning, SMTP/Redis/SSH, odd-port exfiltration, etc.). Loopback,
82+
# established replies, and DNS to the resolver(s) were accepted above. udp
83+
# 443 is allowed for HTTP/3 (QUIC); all other new UDP — including DNS to
84+
# non-resolvers — is dropped, keeping name resolution scoped to @resolvers.
85+
ct state new tcp dport != { 80, 443 } drop
86+
ct state new udp dport != 443 drop
87+
# policy accept -> NEW connections to public addresses on 80/443 pass.
7788
}
7889
}

services/scraper/src/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ export const scrape = async (targetUrl: string): Promise<LinkMetadata> => {
8787
}
8888
};
8989

90-
const PORT = Number(process.env.PORT ?? 3002);
90+
const PORT = Number(process.env.PORT ?? 8855);
9191

9292
/** Largest image we'll proxy. Preview thumbnails are small; this bounds memory. */
9393
const MAX_IMAGE_BYTES = 10 * 1024 * 1024;

services/server/.env.example

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,8 +31,8 @@ CDN_URL=http://localhost:3000
3131

3232
# Internal scraper service (services/scraper) used to fetch link-preview
3333
# metadata (UrlInfo) and proxy preview images (/image_proxy).
34-
# Defaults to http://localhost:3002.
35-
POLYCENTRIC_SCRAPER_URL=http://localhost:3002
34+
# Defaults to http://localhost:8855.
35+
POLYCENTRIC_SCRAPER_URL=http://localhost:8855
3636

3737
POLYCENTRIC_KAFKA_SECURITY_PROTOCOL=SASL_PLAINTEXT
3838
POLYCENTRIC_KAFKA_SASL_MECHANISM=SCRAM-SHA-256

services/server/src/util/scraper.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
/// Base URL of the scraper service, overridable via `POLYCENTRIC_SCRAPER_URL`.
44
fn base_url() -> String {
55
std::env::var("POLYCENTRIC_SCRAPER_URL")
6-
.unwrap_or_else(|_| "http://localhost:3002".to_string())
6+
.unwrap_or_else(|_| "http://localhost:8855".to_string())
77
}
88

99
/// URL of the `/scrape` endpoint (link-preview metadata).

0 commit comments

Comments
 (0)