Skip to content

Commit c92bec0

Browse files
timothy-spenceramirbey
authored andcommitted
This is to get an image that we can use in kubernetes-land in a production-like way.
* Add prod pivcac image * Make sure image is largely read-only to the app user * Add RDS cert bundle * Add nginx image * make prod pivcac image and nginx images be built automatically by gitlab
1 parent 20c6263 commit c92bec0

File tree

8 files changed

+513
-13
lines changed

8 files changed

+513
-13
lines changed

.gitlab-ci.yml

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,86 @@ build-pivcac-image:
210210
--compressed-caching=false
211211
--build-arg "http_proxy=${http_proxy}" --build-arg "https_proxy=${https_proxy}" --build-arg "no_proxy=${no_proxy}"
212212
213+
# Build a container image async, and don't block CI tests
214+
# Cache intermediate images for 1 week (168 hours)
215+
build-prod-pivcac-image:
216+
stage: review
217+
needs: []
218+
interruptible: true
219+
variables:
220+
BRANCH_TAGGING_STRING: ""
221+
rules:
222+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
223+
variables:
224+
BRANCH_TAGGING_STRING: "--destination ${ECR_REGISTRY}/identity-pivcac/review:main"
225+
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
226+
- if: $CI_PIPELINE_SOURCE != "merge_request_event"
227+
when: never
228+
tags:
229+
- build-pool
230+
image:
231+
name: gcr.io/kaniko-project/executor:debug
232+
entrypoint: ['']
233+
script:
234+
- mkdir -p /kaniko/.docker
235+
- |-
236+
KANIKOCFG="\"credsStore\":\"ecr-login\""
237+
if [ "x${http_proxy}" != "x" -o "x${https_proxy}" != "x" ]; then
238+
KANIKOCFG="${KANIKOCFG}, \"proxies\": { \"default\": { \"httpProxy\": \"${http_proxy}\", \"httpsProxy\": \"${https_proxy}\", \"noProxy\": \"${no_proxy}\"}}"
239+
fi
240+
KANIKOCFG="{ ${KANIKOCFG} }"
241+
echo "${KANIKOCFG}" > /kaniko/.docker/config.json
242+
- >-
243+
/kaniko/executor
244+
--context "${CI_PROJECT_DIR}"
245+
--dockerfile "${CI_PROJECT_DIR}/prod.Dockerfile"
246+
--destination "${ECR_REGISTRY}/identity-pivcac/pivcac:${CI_COMMIT_SHA}"
247+
${BRANCH_TAGGING_STRING}
248+
--cache-repo="${ECR_REGISTRY}/identity-pivcac/pivcac/cache"
249+
--cache-ttl=168h
250+
--cache=true
251+
--compressed-caching=false
252+
--build-arg "http_proxy=${http_proxy}" --build-arg "https_proxy=${https_proxy}" --build-arg "no_proxy=${no_proxy}"
253+
254+
build-prod-nginx-image:
255+
stage: review
256+
needs: []
257+
interruptible: true
258+
variables:
259+
BRANCH_TAGGING_STRING: ""
260+
rules:
261+
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
262+
variables:
263+
BRANCH_TAGGING_STRING: "--destination ${ECR_REGISTRY}/identity-pivcac/review:main"
264+
- if: $CI_COMMIT_BRANCH != $CI_DEFAULT_BRANCH
265+
- if: $CI_PIPELINE_SOURCE != "merge_request_event"
266+
when: never
267+
tags:
268+
- build-pool
269+
image:
270+
name: gcr.io/kaniko-project/executor:debug
271+
entrypoint: ['']
272+
script:
273+
- mkdir -p /kaniko/.docker
274+
- |-
275+
KANIKOCFG="\"credsStore\":\"ecr-login\""
276+
if [ "x${http_proxy}" != "x" -o "x${https_proxy}" != "x" ]; then
277+
KANIKOCFG="${KANIKOCFG}, \"proxies\": { \"default\": { \"httpProxy\": \"${http_proxy}\", \"httpsProxy\": \"${https_proxy}\", \"noProxy\": \"${no_proxy}\"}}"
278+
fi
279+
KANIKOCFG="{ ${KANIKOCFG} }"
280+
echo "${KANIKOCFG}" > /kaniko/.docker/config.json
281+
- >-
282+
/kaniko/executor
283+
--context "${CI_PROJECT_DIR}"
284+
--dockerfile "${CI_PROJECT_DIR}/nginx.Dockerfile"
285+
--destination "${ECR_REGISTRY}/identity-pivcac/nginx:${CI_COMMIT_SHA}"
286+
${BRANCH_TAGGING_STRING}
287+
--cache-repo="${ECR_REGISTRY}/identity-pivcac/pivcac/cache"
288+
--cache-ttl=168h
289+
--cache=true
290+
--compressed-caching=false
291+
--build-arg "http_proxy=${http_proxy}" --build-arg "https_proxy=${https_proxy}" --build-arg "no_proxy=${no_proxy}"
292+
213293
review-app:
214294
stage: review
215295
allow_failure: true

Dockerfile

Lines changed: 71 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Use the official Ruby image because the Rails images have been deprecated
2-
FROM logindotgov/build as build
2+
FROM ruby:3.3.1-slim as build
3+
4+
RUN apt-get update && \
5+
apt-get install -y \
6+
git-core \
7+
build-essential \
8+
git-lfs \
9+
curl \
10+
zlib1g-dev \
11+
libssl-dev \
12+
libreadline-dev \
13+
libyaml-dev \
14+
libsqlite3-dev \
15+
sqlite3 \
16+
libxml2-dev \
17+
libxslt1-dev \
18+
libcurl4-openssl-dev \
19+
software-properties-common \
20+
libffi-dev \
21+
libpq-dev \
22+
xz-utils \
23+
unzip && \
24+
rm -rf /var/lib/apt/lists/*
325

426
# Everything happens here from now on
527
WORKDIR /pivcac
@@ -9,20 +31,61 @@ COPY Gemfile* ./
931
RUN gem install bundler --conservative && \
1032
bundle install --without deploy production
1133

12-
# Copy everything else over
13-
COPY . .
34+
# Generate and place SSL certificates for puma
35+
RUN mkdir -p /pivcac/keys
36+
RUN openssl req -x509 -sha256 -nodes -newkey rsa:2048 -days 1825 \
37+
-keyout /pivcac/keys/localhost.key \
38+
-out /pivcac/keys/localhost.crt \
39+
-subj "/C=US/ST=Fake/L=Fakerton/O=Dis/CN=localhost" && \
40+
chmod 644 /pivcac/keys/localhost.key /pivcac/keys/localhost.crt
41+
42+
# Download RDS Combined CA Bundle
43+
RUN mkdir -p /usr/local/share/aws \
44+
&& curl https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem > /usr/local/share/aws/rds-combined-ca-bundle.pem \
45+
&& chmod 644 /usr/local/share/aws/rds-combined-ca-bundle.pem
46+
1447

1548
# Switch to base image
16-
FROM logindotgov/base
49+
FROM ruby:3.3.1-slim
1750
WORKDIR /pivcac
1851

52+
RUN apt-get update && \
53+
apt-get install -y \
54+
curl \
55+
zlib1g-dev \
56+
libssl-dev \
57+
libreadline-dev \
58+
libyaml-dev \
59+
libxml2-dev \
60+
libxslt1-dev \
61+
libcurl4-openssl-dev \
62+
libffi-dev \
63+
libpq-dev && \
64+
rm -rf /var/lib/apt/lists/*
65+
1966
# Copy Gems, NPMs, and other relevant items from build layer
20-
COPY --chown=appuser:appuser --from=build /pivcac .
67+
COPY --from=build /pivcac .
2168

2269
# Copy in whole source (minus items matched in .dockerignore)
23-
COPY --chown=appuser:appuser . .
70+
COPY . .
71+
72+
# Create a new user and set up the working directory
73+
RUN addgroup --gid 1000 app && \
74+
adduser --uid 1000 --gid 1000 --disabled-password --gecos "" app && \
75+
mkdir -p /pivcac && \
76+
mkdir -p /pivcac/tmp/pids && \
77+
mkdir -p /pivcac/log
78+
79+
# make everything the proper perms after everything is initialized
80+
RUN chown -R app:app /pivcac/tmp && \
81+
chown -R app:app /pivcac/log && \
82+
find /pivcac -type d | xargs chmod 755
83+
84+
# get rid of suid/sgid binaries
85+
RUN find / -perm /4000 -type f | xargs chmod u-s
86+
RUN find / -perm /2000 -type f | xargs chmod g-s
2487

25-
USER appuser
88+
USER app
2689

2790
EXPOSE 8443
28-
CMD ["bundle", "exec", "rackup", "config.ru", "--host", "ssl://localhost:8443?key=config/local-certs/server.key&cert=config/local-certs/server.crt"]
91+
CMD ["bundle", "exec", "rackup", "config.ru", "--host", "ssl://0.0.0.0:3000?key=/pivcac/keys/localhost.key&cert=/pivcac/keys/localhost.crt"]

Gemfile.lock

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -260,7 +260,7 @@ GEM
260260
regexp_parser (2.7.0)
261261
request_store (1.5.1)
262262
rack (>= 1.4)
263-
rexml (3.3.3)
263+
rexml (3.3.6)
264264
strscan
265265
rgl (0.5.6)
266266
lazy_priority_queue (~> 0.1.0)

k8.Dockerfile

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ ENV TZ=Etc/UTC
2121
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
2222

2323
# Install dependencies
24-
RUN apt-get update && apt-get install -y \
24+
RUN apt-get update && apt-get install -y \
2525
build-essential \
2626
cron \
27-
curl \
27+
curl \
2828
gettext-base \
2929
git-core \
30-
tar \
30+
tar \
3131
unzip \
3232
jq \
3333
libcurl4-openssl-dev \
@@ -99,7 +99,7 @@ COPY --chmod=644 ./k8files/status.conf /opt/nginx/conf/sites.d/
9999
COPY ./k8files/pivcac.conf /opt/nginx/conf/sites.d/pivcac.conftemp
100100

101101
# Download RDS Combined CA Bundles
102-
RUN wget -P /usr/local/share/aws/ https://s3.amazonaws.com/rds-downloads/rds-combined-ca-bundle.pem
102+
RUN wget -P /usr/local/share/aws/ https://truststore.pki.rds.amazonaws.com/global/global-bundle.pem
103103

104104
# Create cron jobs
105105
RUN echo '* */4 * * * websrv flock -n /tmp/update_cert_revocations.lock -c /usr/local/bin/update_cert_revocations' > /etc/cron.d/update_cert_revocations; \

k8files/nginx-prod.conf

Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
#user nginx;
2+
worker_processes 2;
3+
worker_rlimit_nofile 2048;
4+
pid /var/run/nginx.pid;
5+
daemon off;
6+
load_module /usr/lib/nginx/modules/ngx_http_headers_more_filter_module.so;
7+
8+
9+
events {
10+
worker_connections 1024;
11+
}
12+
13+
http {
14+
include mime.types;
15+
default_type application/octet-stream;
16+
17+
sendfile on;
18+
tcp_nopush off;
19+
keepalive_timeout 60 50;
20+
gzip on;
21+
gzip_types text/plain text/css application/xml application/javascript application/json image/jpg image/jpeg image/png image/gif image/svg+xml font/woff2 woff2;
22+
23+
# Timeouts definition
24+
client_body_timeout 10;
25+
client_header_timeout 10;
26+
send_timeout 10;
27+
# Set buffer size limits
28+
client_body_buffer_size 1k;
29+
client_header_buffer_size 1k;
30+
client_max_body_size 20k;
31+
large_client_header_buffers 2 20k;
32+
# Limit connections
33+
limit_conn addr 20;
34+
limit_conn_status 429;
35+
limit_conn_zone $binary_remote_addr zone=addr:5m;
36+
# Disable sending server info and versions
37+
server_tokens off;
38+
more_clear_headers Server;
39+
more_clear_headers X-Powered-By;
40+
# Prevent clickJacking attack
41+
add_header X-Frame-Options SAMEORIGIN;
42+
# Disable content-type sniffing
43+
add_header X-Content-Type-Options nosniff;
44+
# Enable XSS filter
45+
add_header X-XSS-Protection "1; mode=block";
46+
47+
# Enables nginx to check multiple set_real_ip_from lines
48+
real_ip_recursive on;
49+
50+
real_ip_header X-Forwarded-For;
51+
52+
# Exclude all private IPv4 space from client source calculation when
53+
# processing the X-Forewarded-For header
54+
set_real_ip_from 10.0.0.0/8;
55+
set_real_ip_from 100.64.0.0/10;
56+
set_real_ip_from 172.16.0.0/12;
57+
set_real_ip_from 192.168.0.0/16;
58+
# TODO - IPv6 CIDR for VPCs will require autoconfiguration
59+
60+
# Add CloudFront source address ranges to trusted CIDR range for real ip computation
61+
include /etc/nginx/cloudfront-ips.conf;
62+
63+
# logging
64+
access_log /dev/stdout;
65+
error_log /dev/stdout info;
66+
67+
# Specify a key=value format useful for machine parsing
68+
log_format kv escape=json
69+
'{'
70+
'"time": "$time_local", '
71+
'"hostname": "$host", '
72+
'"dest_port": "$server_port", '
73+
'"dest_ip": "$server_addr", '
74+
'"src": "$remote_addr", '
75+
'"src_ip": "$realip_remote_addr", '
76+
'"user": "$remote_user", '
77+
'"protocol": "$server_protocol", '
78+
'"http_method": "$request_method", '
79+
'"status": "$status", '
80+
'"bytes_out": "$body_bytes_sent", '
81+
'"bytes_in": "$request_length", '
82+
'"http_referer": "$http_referer", '
83+
'"http_user_agent": "$http_user_agent", '
84+
'"nginx_version": "$nginx_version", '
85+
'"http_cloudfront_viewer_address": "$http_cloudfront_viewer_address", '
86+
'"http_cloudfront_viewer_http_version": "$http_cloudfront_viewer_http_version", '
87+
'"http_cloudfront_viewer_tls": "$http_cloudfront_viewer_tls", '
88+
'"http_cloudfront_viewer_country": "$http_cloudfront_viewer_country", '
89+
'"http_cloudfront_viewer_country_region": "$http_cloudfront_viewer_country_region", '
90+
'"http_x_forwarded_for": "$http_x_forwarded_for", '
91+
'"http_x_amzn_trace_id": "$http_x_amzn_trace_id", '
92+
'"response_time": "$upstream_response_time", '
93+
'"request_time": "$request_time", '
94+
'"request": "$request", '
95+
'"tls_protocol": "$ssl_protocol", '
96+
'"tls_cipher": "$ssl_cipher", '
97+
'"uri_path": "$uri", '
98+
'"uri_query": "$query_string",'
99+
'"log_filename": "nginx_access.log"'
100+
'}';
101+
102+
# Get $status_reason variable, a human readable version of $status
103+
include status-map.conf;
104+
105+
# Set HSTS header only if not already set by app. Some clients get unhappy if
106+
# you set multiple Strict-Transport-Security headers.
107+
# https://serverfault.com/a/598106
108+
map $upstream_http_strict_transport_security $sts_value {
109+
'' "max-age=31536000; preload";
110+
}
111+
112+
# Always add a HSTS header - This is still inside the http block, so will not
113+
# conflict with headers set in nginx.conf
114+
add_header Strict-Transport-Security $sts_value always;
115+
116+
server {
117+
listen 8443 ssl;
118+
server_name _;
119+
access_log /dev/stdout kv;
120+
121+
ssl_certificate /keys/tls.crt;
122+
ssl_certificate_key /keys/tls.key;
123+
ssl_client_certificate /etc/nginx/ficam_bundle.pem;
124+
ssl_verify_client optional_no_ca; # on;
125+
ssl_verify_depth 10;
126+
127+
ssl_ciphers 'EECDH+AESGCM:EDH+AESGCM:AES256+EECDH:AES256+EDH:!ECDHE-RSA-AES256-SHA384:!ECDHE-RSA-AES256-SHA:!DHE-RSA-AES256-SHA256:!DHE-RSA-AES256-SHA';
128+
ssl_dhparam /etc/ssl/certs/dhparam.pem;
129+
ssl_prefer_server_ciphers on;
130+
ssl_protocols TLSv1.2;
131+
ssl_session_cache shared:SSL:10m;
132+
ssl_session_timeout 5m;
133+
ssl_stapling on;
134+
ssl_stapling_verify on;
135+
proxy_buffer_size 32k;
136+
proxy_buffers 8 32k;
137+
proxy_busy_buffers_size 64k;
138+
139+
location ~* \.(html|txt|ico|png|json)$ {
140+
root "/srv";
141+
try_files $uri @backend;
142+
}
143+
144+
location / {
145+
proxy_pass https://0.0.0.0:3000;
146+
147+
proxy_set_header X-Real-Host $host;
148+
proxy_set_header X-Real-Ip $remote_addr;
149+
proxy_set_header X-Real-Proto https;
150+
proxy_set_header X-Client-Verify $ssl_client_verify;
151+
proxy_set_header X-Client-S-Dn $ssl_client_s_dn;
152+
proxy_set_header X-Client-I-Dn $ssl_client_i_dn;
153+
proxy_set_header X-Client-Serial $ssl_client_serial;
154+
proxy_set_header X-Client-Fingerprint $ssl_client_fingerprint;
155+
proxy_set_header X-Client-Cert $ssl_client_escaped_cert;
156+
}
157+
158+
location @backend {
159+
proxy_pass https://0.0.0.0:3000;
160+
161+
proxy_set_header X-Real-Host $host;
162+
proxy_set_header X-Real-Ip $remote_addr;
163+
proxy_set_header X-Real-Proto https;
164+
proxy_set_header X-Client-Verify $ssl_client_verify;
165+
proxy_set_header X-Client-S-Dn $ssl_client_s_dn;
166+
proxy_set_header X-Client-I-Dn $ssl_client_i_dn;
167+
proxy_set_header X-Client-Serial $ssl_client_serial;
168+
proxy_set_header X-Client-Fingerprint $ssl_client_fingerprint;
169+
proxy_set_header X-Client-Cert $ssl_client_escaped_cert;
170+
}
171+
}
172+
}

0 commit comments

Comments
 (0)