-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathdocker-compose.yml
More file actions
153 lines (149 loc) · 5.58 KB
/
Copy pathdocker-compose.yml
File metadata and controls
153 lines (149 loc) · 5.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
services:
# One-shot registrar: reads the App key (mounted RO from host /etc/github-app/),
# mints a short-lived runner registration token, writes it to a shared volume,
# and exits. The runner waits for this to complete before starting.
#
# Why this exists: pull_request_target workflows execute attacker-supplied PR
# code on the runner. Anything in the runner container's process env is
# reachable from there. By keeping the App key out of the runner's env (and
# only inside this short-lived sidecar that exits before any workflow runs),
# a compromised PR can never read it. See mint-runner-token.sh for details.
roku-runner-registrar:
image: alpine:latest@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
container_name: roku-runner-registrar
environment:
- TZ=${TIMEZONE:-America/New_York}
volumes:
- /etc/github-app:/run/secrets/github-app:ro
- registrar-shared:/shared
- ./mint-runner-token.sh:/usr/local/bin/mint-runner-token.sh:ro
command:
- sh
- -c
- |
rm -f /shared/runner-token
apk add --no-cache --quiet curl openssl jq >/dev/null
exec /usr/local/bin/mint-runner-token.sh
# The `rm` above is load-bearing. registrar-shared is a PERSISTENT named
# volume, so a token from a prior cycle survives a restart. The runner gates
# only on `test -s /shared/runner-token` (presence, not freshness), so a
# leftover token lets the runner start and register with a dead token → 404,
# which crash-loops the whole project until StartLimitBurst parks the unit.
# Deleting the token BEFORE the slow `apk add` means the runner's healthcheck
# correctly waits for a freshly minted token instead of racing a stale one.
# Stays running after writing the token (sleep infinity inside the script).
# See the comment in mint-runner-token.sh for why — TL;DR systemd uses
# --abort-on-container-exit, which would tear the project down if we exited.
# The runner gates its startup on this container's healthcheck below.
healthcheck:
test: ["CMD", "test", "-s", "/shared/runner-token"]
interval: 5s
timeout: 2s
retries: 12
start_period: 30s
restart: "no"
logging:
driver: "json-file"
options:
max-size: "5m"
max-file: "2"
roku-runner:
# Pinned to a digest, not :latest. GitHub deprecates older runner binaries
# periodically (~every 1-3 months); a stale local image then hits
# "Runner version X is deprecated and cannot receive messages" and enters a
# restart loop. Renovate bumps this digest via PR — see renovate.json.
image: myoung34/github-runner@sha256:7416ffaad6a080686cbca035b93b7189e5c6c2ecd287c36023a36919ed619d04
container_name: roku-runner
depends_on:
roku-runner-registrar:
condition: service_healthy
iptables-sidecar:
condition: service_started
# See runner-entrypoint.sh — reads RUNNER_TOKEN from the shared volume
# written by the registrar, then execs upstream's /entrypoint.sh.
entrypoint: ["/usr/local/bin/runner-entrypoint.sh"]
# Compose's `entrypoint:` override CLEARS the image's default CMD, so we
# must explicitly restore it. Without this, upstream's /entrypoint.sh
# registers the runner, then runs `"$@"` (empty), and the container
# exits before reaching `Runner.Listener`. See:
# https://docs.docker.com/reference/compose-file/services/#entrypoint
command: ["./bin/Runner.Listener", "run", "--startuptype", "service"]
environment:
- RUNNER_NAME=${RUNNER_NAME:-roku-runner-01}
- EPHEMERAL=true
- DISABLE_AUTO_UPDATE=true
- RUNNER_SCOPE=repo
- REPO_URL=https://github.com/jellyrock/jellyrock
- LABELS=${RUNNER_LABELS:-self-hosted,roku,roku-device}
- TZ=${TIMEZONE:-America/New_York}
- RUN_AS_ROOT=true
# Optional Healthchecks.io push URL — runner-entrypoint.sh pings on
# boot and every 60s. Unset = heartbeat disabled (no-op).
- HEALTHCHECKS_URL=${HEALTHCHECKS_URL:-}
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- SETUID
- SETGID
- CHOWN
- DAC_OVERRIDE
- FOWNER
tmpfs:
- /tmp:rw,noexec,nosuid,size=2g
- /home/runner:rw,noexec,nosuid,size=500m
volumes:
- registrar-shared:/shared:ro
- runner-work:/_work
- ./runner-entrypoint.sh:/usr/local/bin/runner-entrypoint.sh:ro
network_mode: service:iptables-sidecar
restart: "no"
deploy:
resources:
limits:
cpus: '2'
memory: 3G
healthcheck:
test: ["CMD-SHELL", "ps aux | grep -q '[R]unner.Listener' || exit 1"]
interval: 30s
timeout: 5s
retries: 3
start_period: 60s
logging:
driver: "json-file"
options:
max-size: "10m"
max-file: "3"
iptables-sidecar:
image: alpine:latest@sha256:294b683cb724975bec92580e1e685676bd4b50bda910ddb8c51d4cabeaec77e6
container_name: roku-iptables
cap_add:
- NET_ADMIN
environment:
- ROKU_DEVICE_IP=${ROKU_DEVICE_IP}
volumes:
- ./iptables-rules.sh:/usr/local/bin/iptables-rules.sh:ro
command: >
sh -c "
sh /usr/local/bin/iptables-rules.sh &&
echo 'IP filtering active. Monitoring...' &&
while true; do
sleep 3600
done
"
restart: "no"
logging:
driver: "json-file"
options:
max-size: "5m"
max-file: "2"
volumes:
runner-work:
driver: local
registrar-shared:
driver: local
networks:
cicd-isolated:
name: cicd-isolated
driver: bridge