chore(frontend): use bitnami/openresty as base image for frontend Dockerfile (#1335)

* chore(frontend): use bitnami/openresty as base image for frontend Dockerfile

openresty/openresty was not being patched as frequently as we would like, resulting in numerous
vulnerabilities without resolution. bitnami/openresty is being patched more frequently.

Some additional changes were necessary when porting our frontend between these distributions:
- html files are in /app
- nginx.conf is in /opt/bitnami/openresty/nginx/conf/nginx.conf
- envsubst is not available by default in bitnami/openresty and needs to be copied in
- Nginx.conf - we wrap the server block in http block and overwrite root nginx.conf
    - using the existing bitnami/openresty nginx.conf as a server block alone causes issues with bitnami/openresty, as bitnami/openresty provides a root nginx.conf which conflicts with directives in Speckle's server block
- we copy the directives from openresty/openresty (which are known to work with Speckle's server block), and apply them alongside Speckle's server block. This creates a new root nginx.conf which we can overwrite the default on the image.
- nginx should use a port available to non sudo/root user, we have selected 8080 instead of previous 80
- need to explicitly output nginx logs to stderr / stdout

Created a readonly root file system on Kubernetes. This requires the following changes:
- emptyDir volumes are mounted in kubernetes to allow bitnami/openresty to write to specific locations
- explicitly include and copy mime.types file to nginx configuration directory

Due to the change to non-privileged port number (8080), the following subsequent changes were required:
- Update 1-click deployment script to match frontend at port 8080
- Updates docker-compose-speckle.yaml file

Co-authored-by: Gergő Jedlicska <gergo@jedlicska.com>
This commit is contained in:
Iain Sproat 2023-01-25 19:06:48 +00:00 коммит произвёл GitHub
Родитель 7f617f132e
Коммит 68fd86b754
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
10 изменённых файлов: 277 добавлений и 127 удалений

Просмотреть файл

@ -7,7 +7,7 @@ services:
image: speckle/speckle-frontend:local
restart: always
ports:
- '0.0.0.0:80:80'
- '0.0.0.0:80:8080'
environment:
FILE_SIZE_LIMIT_MB: 100

Просмотреть файл

@ -30,21 +30,29 @@ COPY packages/shared ./packages/shared/
# This way the foreach only builds the frontend and its deps
RUN yarn workspaces foreach run build
RUN DEBIAN_FRONTEND=noninteractive \
apt-get -q update && \
apt-get install --no-install-recommends -y \
gettext=0.21-4 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/*
# production stage
FROM openresty/openresty:1.21.4.1-4-jammy-amd64 as production-stage
FROM bitnami/openresty:1.21.4-1-debian-11-r80 as production-stage
ARG NODE_ENV
ARG SPECKLE_SERVER_VERSION
ENV NODE_ENV=${NODE_ENV}
ENV FILE_SIZE_LIMIT_MB=100
COPY --from=build-stage /speckle-server/packages/frontend/dist /usr/share/nginx/html
RUN rm /etc/nginx/conf.d/default.conf
COPY --from=build-stage /usr/bin/envsubst /usr/bin/envsubst
COPY packages/frontend/nginx/ /etc/nginx/
COPY --from=build-stage /speckle-server/packages/frontend/dist /app
COPY packages/frontend/nginx/ /opt/bitnami/openresty/nginx/
# prepare the environment
ENTRYPOINT ["/etc/nginx/docker-entrypoint.sh"]
ENTRYPOINT ["/opt/bitnami/openresty/nginx/docker-entrypoint.sh"]
EXPOSE 80
CMD ["nginx", "-g", "daemon off;"]
EXPOSE 8080
CMD ["/opt/bitnami/scripts/openresty/entrypoint.sh", "/opt/bitnami/scripts/openresty/run.sh"]

Просмотреть файл

@ -5,7 +5,8 @@ defined_envs=$(printf '${%s} ' $(env | cut -d= -f1))
echo Starting nginx environment template rendering with "${defined_envs}"
envsubst "${defined_envs}" < /etc/nginx/templates/nginx.conf.template > /etc/nginx/conf.d/nginx.conf
cp /opt/bitnami/openresty/nginx/mime.types /opt/bitnami/openresty/nginx/conf/mime.types
envsubst "${defined_envs}" < /opt/bitnami/openresty/nginx/templates/nginx.conf.template > /opt/bitnami/openresty/nginx/conf/nginx.conf
echo Nginx conf rendered, starting server...
exec "$@"

Просмотреть файл

@ -0,0 +1,98 @@
types {
text/html html htm shtml;
text/css css;
text/xml xml;
image/gif gif;
image/jpeg jpeg jpg;
application/javascript js;
application/atom+xml atom;
application/rss+xml rss;
text/mathml mml;
text/plain txt;
text/vnd.sun.j2me.app-descriptor jad;
text/vnd.wap.wml wml;
text/x-component htc;
image/avif avif;
image/png png;
image/svg+xml svg svgz;
image/tiff tif tiff;
image/vnd.wap.wbmp wbmp;
image/webp webp;
image/x-icon ico;
image/x-jng jng;
image/x-ms-bmp bmp;
font/woff woff;
font/woff2 woff2;
application/java-archive jar war ear;
application/json json;
application/mac-binhex40 hqx;
application/msword doc;
application/pdf pdf;
application/postscript ps eps ai;
application/rtf rtf;
application/vnd.apple.mpegurl m3u8;
application/vnd.google-earth.kml+xml kml;
application/vnd.google-earth.kmz kmz;
application/vnd.ms-excel xls;
application/vnd.ms-fontobject eot;
application/vnd.ms-powerpoint ppt;
application/vnd.oasis.opendocument.graphics odg;
application/vnd.oasis.opendocument.presentation odp;
application/vnd.oasis.opendocument.spreadsheet ods;
application/vnd.oasis.opendocument.text odt;
application/vnd.openxmlformats-officedocument.presentationml.presentation
pptx;
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
xlsx;
application/vnd.openxmlformats-officedocument.wordprocessingml.document
docx;
application/vnd.wap.wmlc wmlc;
application/wasm wasm;
application/x-7z-compressed 7z;
application/x-cocoa cco;
application/x-java-archive-diff jardiff;
application/x-java-jnlp-file jnlp;
application/x-makeself run;
application/x-perl pl pm;
application/x-pilot prc pdb;
application/x-rar-compressed rar;
application/x-redhat-package-manager rpm;
application/x-sea sea;
application/x-shockwave-flash swf;
application/x-stuffit sit;
application/x-tcl tcl tk;
application/x-x509-ca-cert der pem crt;
application/x-xpinstall xpi;
application/xhtml+xml xhtml;
application/xspf+xml xspf;
application/zip zip;
application/octet-stream bin exe dll;
application/octet-stream deb;
application/octet-stream dmg;
application/octet-stream iso img;
application/octet-stream msi msp msm;
audio/midi mid midi kar;
audio/mpeg mp3;
audio/ogg ogg;
audio/x-m4a m4a;
audio/x-realaudio ra;
video/3gpp 3gpp 3gp;
video/mp2t ts;
video/mp4 mp4;
video/mpeg mpeg mpg;
video/quicktime mov;
video/webm webm;
video/x-flv flv;
video/x-m4v m4v;
video/x-mng mng;
video/x-ms-asf asx asf;
video/x-ms-wmv wmv;
video/x-msvideo avi;
}

Просмотреть файл

@ -1,130 +1,147 @@
server_tokens off;
gzip on;
gzip_disable "msie6";
pcre_jit on;
error_log stderr info;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
events {
worker_connections 1024;
}
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
#use any of the following two
real_ip_header CF-Connecting-IP;
#real_ip_header X-Forwarded-For;
server {
listen 80;
client_max_body_size 100m;
http {
include mime.types;
default_type application/octet-stream;
# move default write paths to a custom directory
# kubernetes can mount this directory and prevent writes to the root directory
# https://github.com/openresty/docker-openresty/issues/119
client_body_temp_path /var/run/openresty/nginx-client-body;
proxy_temp_path /var/run/openresty/nginx-proxy;
fastcgi_temp_path /var/run/openresty/nginx-fastcgi;
uwsgi_temp_path /var/run/openresty/nginx-uwsgi;
scgi_temp_path /var/run/openresty/nginx-scgi;
client_body_temp_path /bitnami/openresty/nginx-client-body;
proxy_temp_path /bitnami/openresty/nginx-proxy;
fastcgi_temp_path /bitnami/openresty/nginx-fastcgi;
uwsgi_temp_path /bitnami/openresty/nginx-uwsgi;
scgi_temp_path /bitnami/openresty/nginx-scgi;
location / {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
sendfile on;
keepalive_timeout 65;
access_log /dev/stdout;
location ~* ^/(favicon.ico|logo.svg|loadingImage.png|og_image.png) {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
expires 1d;
}
# Speckle configuration
server_tokens off;
gzip on;
gzip_disable "msie6";
location ~* ^/(js/.*|fonts/.*|(css/.*)|(img/.*)|(assets/.*)) {
root /usr/share/nginx/html;
index index.html;
try_files $uri $uri/ /index.html;
expires 1y;
}
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6;
gzip_buffers 16 8k;
gzip_http_version 1.1;
gzip_min_length 256;
gzip_types
application/atom+xml
application/geo+json
application/javascript
application/x-javascript
application/json
application/ld+json
application/manifest+json
application/rdf+xml
application/rss+xml
application/xhtml+xml
application/xml
font/eot
font/otf
font/ttf
image/svg+xml
text/css
text/javascript
text/plain
text/xml;
location ~ ^/streams/.* {
default_type text/html;
content_by_lua_block {
local f = assert(io.open('/usr/share/nginx/html/index.html', "rb"))
local content = f:read("*all")
f:close()
local http_host = ngx.var.http_host
set_real_ip_from 103.21.244.0/22;
set_real_ip_from 103.22.200.0/22;
set_real_ip_from 103.31.4.0/22;
set_real_ip_from 104.16.0.0/13;
set_real_ip_from 104.24.0.0/14;
set_real_ip_from 108.162.192.0/18;
set_real_ip_from 131.0.72.0/22;
set_real_ip_from 141.101.64.0/18;
set_real_ip_from 162.158.0.0/15;
set_real_ip_from 172.64.0.0/13;
set_real_ip_from 173.245.48.0/20;
set_real_ip_from 188.114.96.0/20;
set_real_ip_from 190.93.240.0/20;
set_real_ip_from 197.234.240.0/22;
set_real_ip_from 198.41.128.0/17;
set_real_ip_from 2400:cb00::/32;
set_real_ip_from 2606:4700::/32;
set_real_ip_from 2803:f800::/32;
set_real_ip_from 2405:b500::/32;
set_real_ip_from 2405:8100::/32;
set_real_ip_from 2c0f:f248::/32;
set_real_ip_from 2a06:98c0::/29;
content = content:gsub('<meta property=og:title (.-)>', '<meta property=og:title content="Speckle Stream">')
#use any of the following two
real_ip_header CF-Connecting-IP;
#real_ip_header X-Forwarded-For;
local stream_id = ngx.var.uri:sub(10)
local img_tag = '<meta property=og:image content="https://' .. http_host .. '/preview/' .. stream_id .. '?postprocess=og&ts=' .. ngx.now() .. '">'
server {
listen 8080;
client_max_body_size 100m;
content = content:gsub('<meta property=og:image (.-)>', img_tag)
location / {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
add_header Cache-Control "no-store, no-cache, must-revalidate";
}
ngx.say(content)
location ~* ^/(favicon.ico|logo.svg|loadingImage.png|og_image.png) {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
expires 1d;
}
location ~* ^/(js/.*|fonts/.*|(css/.*)|(img/.*)|(assets/.*)) {
root /app;
index index.html;
try_files $uri $uri/ /index.html;
expires 1y;
}
location ~ ^/streams/.* {
default_type text/html;
content_by_lua_block {
local f = assert(io.open('/app/index.html', "rb"))
local content = f:read("*all")
f:close()
local http_host = ngx.var.http_host
content = content:gsub('<meta property=og:title (.-)>', '<meta property=og:title content="Speckle Stream">')
local stream_id = ngx.var.uri:sub(10)
local img_tag = '<meta property=og:image content="https://' .. http_host .. '/preview/' .. stream_id .. '?postprocess=og&ts=' .. ngx.now() .. '">'
content = content:gsub('<meta property=og:image (.-)>', img_tag)
ngx.say(content)
}
}
location ~* ^/(graphql|explorer|(auth/.*)|(objects/.*)|(preview/.*)|(api/.*)|(static/.*)) {
resolver 127.0.0.11 valid=30s;
set $upstream_speckle_server speckle-server;
client_max_body_size ${FILE_SIZE_LIMIT_MB}m;
proxy_pass http://$upstream_speckle_server:3000;
proxy_buffering off;
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /app;
}
}
location ~* ^/(graphql|explorer|(auth/.*)|(objects/.*)|(preview/.*)|(api/.*)|(static/.*)) {
resolver 127.0.0.11 valid=30s;
set $upstream_speckle_server speckle-server;
client_max_body_size ${FILE_SIZE_LIMIT_MB}m;
proxy_pass http://$upstream_speckle_server:3000;
proxy_buffering off;
proxy_request_buffering off;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

Просмотреть файл

@ -40,7 +40,7 @@ services:
image: speckle/speckle-frontend:2
restart: always
ports:
- '127.0.0.1:8000:80'
- '127.0.0.1:8080:8080'
environment:
FILE_SIZE_LIMIT_MB: 100

Просмотреть файл

@ -10,7 +10,7 @@ server {
client_max_body_size 100m;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://localhost:8000;
proxy_pass http://localhost:8080;
proxy_buffering off;
proxy_request_buffering off;

Просмотреть файл

@ -25,7 +25,7 @@ spec:
ports:
- name: www
containerPort: 80
containerPort: 8080
protocol: TCP
resources:
@ -53,6 +53,24 @@ spec:
- name: FILE_SIZE_LIMIT_MB
value: {{ .Values.file_size_limit_mb | quote }}
securityContext:
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
privileged: false
readOnlyRootFilesystem: true
runAsNonRoot: true
runAsUser: 20000
volumeMounts:
- name: openresty-temp
mountPath: /bitnami/openresty
- name: openresty-conf
mountPath: /opt/bitnami/openresty/nginx/conf/
- name: openresty-logs
mountPath: /opt/bitnami/openresty/nginx/logs/
priorityClassName: high-priority
{{- if .Values.frontend.affinity }}
affinity: {{- include "speckle.renderTpl" (dict "value" .Values.frontend.affinity "context" $) | nindent 8 }}
@ -69,3 +87,11 @@ spec:
{{- if .Values.frontend.serviceAccount.create }}
serviceAccountName: {{ include "frontend.name" $ }}
{{- end }}
volumes:
- name: openresty-temp
emptyDir: {}
- name: openresty-conf
emptyDir: {}
- name: openresty-logs
emptyDir: {}

Просмотреть файл

@ -12,5 +12,5 @@ spec:
ports:
- protocol: TCP
name: www
port: 80
port: 8080
targetPort: www

Просмотреть файл

@ -30,7 +30,7 @@ spec:
service:
name: speckle-frontend
port:
number: 80
number: 8080
- pathType: Exact
path: "/(graphql|explorer|(auth/.*)|(objects/.*)|(preview/.*)|(api/.*)|(static/.*))"
backend: