feat: configure dedicated server proxy

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
Daniel Noulet 2026-09-20 14:07:51 +02:00
parent e787359c54
commit a86a0cb539
8 changed files with 91 additions and 91 deletions

View file

@ -43,9 +43,7 @@ jobs:
REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }} REGISTRY_USERNAME: ${{ secrets.REGISTRY_USERNAME }}
REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }} REGISTRY_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
JWT_SECRET: ${{ secrets.GO_GUESS_JWT_SECRET }} JWT_SECRET: ${{ secrets.GO_GUESS_JWT_SECRET }}
GO_GUESS_HOST: ${{ vars.GO_GUESS_HOST }}
GO_GUESS_FRONTEND_URL: ${{ vars.GO_GUESS_FRONTEND_URL }} GO_GUESS_FRONTEND_URL: ${{ vars.GO_GUESS_FRONTEND_URL }}
GO_GUESS_TLS_SECRET: ${{ vars.GO_GUESS_TLS_SECRET }}
RANCHER_NAMESPACE: ${{ vars.RANCHER_NAMESPACE }} RANCHER_NAMESPACE: ${{ vars.RANCHER_NAMESPACE }}
steps: steps:
@ -82,10 +80,6 @@ jobs:
echo "GO_GUESS_JWT_SECRET must contain at least 32 characters" >&2 echo "GO_GUESS_JWT_SECRET must contain at least 32 characters" >&2
exit 1 exit 1
fi fi
if [ "$TARGET_ENVIRONMENT" = "production" ] && [ -z "$GO_GUESS_HOST" ]; then
echo "GO_GUESS_HOST is required for production" >&2
exit 1
fi
namespace="${RANCHER_NAMESPACE:-go-guess-$TARGET_ENVIRONMENT}" namespace="${RANCHER_NAMESPACE:-go-guess-$TARGET_ENVIRONMENT}"
if [ -n "$PUBLISHED_SHA" ]; then if [ -n "$PUBLISHED_SHA" ]; then
image_tag="$PUBLISHED_SHA" image_tag="$PUBLISHED_SHA"
@ -94,7 +88,7 @@ jobs:
fi fi
frontend_url="$GO_GUESS_FRONTEND_URL" frontend_url="$GO_GUESS_FRONTEND_URL"
if [ "$TARGET_ENVIRONMENT" = "production" ] && [ -z "$frontend_url" ]; then if [ "$TARGET_ENVIRONMENT" = "production" ] && [ -z "$frontend_url" ]; then
frontend_url="https://$GO_GUESS_HOST" frontend_url="https://goguess.urpi.be"
fi fi
{ {
echo "DEPLOY_NAMESPACE=$namespace" echo "DEPLOY_NAMESPACE=$namespace"
@ -130,14 +124,6 @@ jobs:
if [ -n "$RESOLVED_FRONTEND_URL" ]; then if [ -n "$RESOLVED_FRONTEND_URL" ]; then
set -- "$@" --set-string "app.frontendURL=$RESOLVED_FRONTEND_URL" set -- "$@" --set-string "app.frontendURL=$RESOLVED_FRONTEND_URL"
fi fi
if [ "$TARGET_ENVIRONMENT" = "production" ]; then
set -- "$@" --set-string "ingress.hosts[0].host=$GO_GUESS_HOST"
if [ -n "$GO_GUESS_TLS_SECRET" ]; then
set -- "$@" \
--set-string "ingress.tls[0].secretName=$GO_GUESS_TLS_SECRET" \
--set-string "ingress.tls[0].hosts[0]=$GO_GUESS_HOST"
fi
fi
helm upgrade --install go-guess deploy/helm/go-guess \ helm upgrade --install go-guess deploy/helm/go-guess \
--namespace "$DEPLOY_NAMESPACE" \ --namespace "$DEPLOY_NAMESPACE" \
--values "$values_file" \ --values "$values_file" \

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ frontend/dist/
frontend/coverage/ frontend/coverage/
bdd/node_modules/ bdd/node_modules/
bdd/reports/ bdd/reports/
deploy/local.yaml

View file

@ -128,11 +128,11 @@ make helm-deploy-production
Development uses demo fixtures, ephemeral database and message-broker storage, Development uses demo fixtures, ephemeral database and message-broker storage,
and NodePort `30080`. Production uses production fixtures, persistent volumes, and NodePort `30080`. Production uses production fixtures, persistent volumes,
an Nginx ingress, generated secrets that are preserved across upgrades, and generated secrets that are preserved across upgrades, and larger resource
larger resource limits. Before production deployment, replace limits. It exposes the application at NodePort `31374` for the dedicated-server
`go-guess.example.com` in `values-production.yaml` or override Nginx proxy while PostgreSQL and RabbitMQ remain cluster-internal. The public
`app.frontendURL` and `ingress.hosts[0].host`. Configure a TLS entry under URL is `https://goguess.urpi.be`; one.com DNS, Nginx, Certbot, firewall, and
`ingress.tls` when the Rancher cluster does not provide TLS separately. verification instructions are in `deploy/nginx/README.md`.
To use external managed services, disable the bundled StatefulSets and provide To use external managed services, disable the bundled StatefulSets and provide
full connection URLs: full connection URLs:
@ -157,8 +157,8 @@ workflow succeeds. Create GitHub environments named `development` and
- Secrets `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, and - Secrets `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, and
`GO_GUESS_JWT_SECRET`. `GO_GUESS_JWT_SECRET`.
- Variable `RANCHER_NAMESPACE` for the target namespace. - Variable `RANCHER_NAMESPACE` for the target namespace.
- Production variable `GO_GUESS_HOST`, plus optional - Optional variable `GO_GUESS_FRONTEND_URL` when overriding an environment's
`GO_GUESS_FRONTEND_URL` and `GO_GUESS_TLS_SECRET`. configured public URL.
Because the configured image registry uses HTTP, every Rancher cluster node must Because the configured image registry uses HTTP, every Rancher cluster node must
trust `batty1039.startdedicated.net:5000` as an insecure registry. Use HTTPS for trust `batty1039.startdedicated.net:5000` as an insecure registry. Use HTTPS for

View file

@ -3,7 +3,8 @@ Go Guess has been installed in namespace {{ .Release.Namespace }}.
{{- if .Values.ingress.enabled }} {{- if .Values.ingress.enabled }}
Open {{ .Values.app.frontendURL }} Open {{ .Values.app.frontendURL }}
{{- else if eq .Values.service.type "NodePort" }} {{- else if eq .Values.service.type "NodePort" }}
Open a cluster node on port {{ .Values.service.nodePort }}. The application is exposed on cluster node port {{ .Values.service.nodePort }}.
Open {{ .Values.app.frontendURL }} through the external reverse proxy.
{{- else }} {{- else }}
Run: Run:
kubectl -n {{ .Release.Namespace }} port-forward service/{{ include "go-guess.fullname" . }} 8080:{{ .Values.service.port }} kubectl -n {{ .Release.Namespace }} port-forward service/{{ include "go-guess.fullname" . }} 8080:{{ .Values.service.port }}

View file

@ -4,23 +4,14 @@ image:
app: app:
environment: production environment: production
frontendURL: https://go-guess.example.com frontendURL: https://goguess.urpi.be
service: service:
type: ClusterIP type: NodePort
nodePort: 31374
ingress: ingress:
enabled: true enabled: false
className: nginx
annotations:
nginx.ingress.kubernetes.io/proxy-body-size: 10m
nginx.ingress.kubernetes.io/proxy-read-timeout: "3600"
hosts:
- host: go-guess.example.com
paths:
- path: /
pathType: Prefix
tls: []
resources: resources:
requests: requests:

View file

@ -1,55 +0,0 @@
# Generated by Rancher
# name: kubeconfig-ldfvp
# createdTimestamp: 2026-09-20T09:13:04Z
# ttl: 2592000
apiVersion: v1
kind: Config
clusters:
- name: "rancher"
cluster:
server: "https://rancher.urpi.be"
certificate-authority-data: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ2VENDQ\
VdPZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQkdNUnd3R2dZRFZRUUtFeE5rZVc1aGJXbGoKY\
kdsemRHVnVaWEl0YjNKbk1TWXdKQVlEVlFRRERCMWtlVzVoYldsamJHbHpkR1Z1WlhJdFkyRkFNV\
GM0T1RnNQpNVGczTURBZUZ3MHlOakE1TWpBd056RXhNVEJhRncwek5qQTVNVGN3TnpFeE1UQmFNR\
Vl4SERBYUJnTlZCQW9UCkUyUjVibUZ0YVdOc2FYTjBaVzVsY2kxdmNtY3hKakFrQmdOVkJBTU1IV\
1I1Ym1GdGFXTnNhWE4wWlc1bGNpMWoKWVVBeE56ZzVPRGt4T0Rjd01Ga3dFd1lIS29aSXpqMENBU\
VlJS29aSXpqMERBUWNEUWdBRXNIVEFSdUJJcjNZQQo0NlVzRmM5N1lkRHd1eGtnNHNSU21ML3ZCS\
Th3dWh5Z2M4Wm1jVUI4VWhWbUZvdEd4UnFqR3dWNG1GYXlxdGdQCmNnQVZmdkJ4bUtOQ01FQXdEZ\
1lEVlIwUEFRSC9CQVFEQWdLa01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGT\
kNyVndRcjFXeWFpdnJMS1BLZ0hmbUVjRUFyTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFERAorU\
2lMaXdOODFNU2FEOWEzaStUNkJGOHVjdXNKQm5xM2pJdHV4WGpFMlFJZ1Z5L3FNaEtRSFRIa3pzV\
jIwWThBCll5SWdTWWZ1czZkaFdwNEI5TFl2eEVzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
- name: "local"
cluster:
server: "https://rancher.urpi.be/k8s/clusters/local"
certificate-authority-data: "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJ2VENDQ\
VdPZ0F3SUJBZ0lCQURBS0JnZ3Foa2pPUFFRREFqQkdNUnd3R2dZRFZRUUtFeE5rZVc1aGJXbGoKY\
kdsemRHVnVaWEl0YjNKbk1TWXdKQVlEVlFRRERCMWtlVzVoYldsamJHbHpkR1Z1WlhJdFkyRkFNV\
GM0T1RnNQpNVGczTURBZUZ3MHlOakE1TWpBd056RXhNVEJhRncwek5qQTVNVGN3TnpFeE1UQmFNR\
Vl4SERBYUJnTlZCQW9UCkUyUjVibUZ0YVdOc2FYTjBaVzVsY2kxdmNtY3hKakFrQmdOVkJBTU1IV\
1I1Ym1GdGFXTnNhWE4wWlc1bGNpMWoKWVVBeE56ZzVPRGt4T0Rjd01Ga3dFd1lIS29aSXpqMENBU\
VlJS29aSXpqMERBUWNEUWdBRXNIVEFSdUJJcjNZQQo0NlVzRmM5N1lkRHd1eGtnNHNSU21ML3ZCS\
Th3dWh5Z2M4Wm1jVUI4VWhWbUZvdEd4UnFqR3dWNG1GYXlxdGdQCmNnQVZmdkJ4bUtOQ01FQXdEZ\
1lEVlIwUEFRSC9CQVFEQWdLa01BOEdBMVVkRXdFQi93UUZNQU1CQWY4d0hRWUQKVlIwT0JCWUVGT\
kNyVndRcjFXeWFpdnJMS1BLZ0hmbUVjRUFyTUFvR0NDcUdTTTQ5QkFNQ0EwZ0FNRVVDSVFERAorU\
2lMaXdOODFNU2FEOWEzaStUNkJGOHVjdXNKQm5xM2pJdHV4WGpFMlFJZ1Z5L3FNaEtRSFRIa3pzV\
jIwWThBCll5SWdTWWZ1czZkaFdwNEI5TFl2eEVzPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0t"
users:
- name: "rancher"
user:
token: "ext/token-9fzp2:d729wqhv4gbjhd9qjghs74f7h6wqcmml9c79stw8bw5nj2w9p752nw"
contexts:
- name: "rancher"
context:
user: "rancher"
cluster: "rancher"
- name: "local"
context:
user: "rancher"
cluster: "local"
current-context: "local"

55
deploy/nginx/README.md Normal file
View file

@ -0,0 +1,55 @@
# Dedicated-server reverse proxy
Production exposes only the Go Guess application through Kubernetes NodePort
`31374`. PostgreSQL and RabbitMQ remain available only through their
cluster-internal services.
## DNS
In one.com, create an `A` record for `goguess.urpi.be` pointing to the dedicated
server's public IPv4 address. Add an `AAAA` record only when the server and its
firewall are configured for public IPv6. Wait for:
```bash
dig +short goguess.urpi.be
```
to return the dedicated server address before requesting a certificate.
## Nginx and Certbot
The included virtual host assumes Nginx runs on the Kubernetes node and can
reach the NodePort at `127.0.0.1:31374`.
```bash
sudo install -m 0644 deploy/nginx/goguess.urpi.be.conf \
/etc/nginx/sites-available/goguess.urpi.be.conf
sudo ln -s /etc/nginx/sites-available/goguess.urpi.be.conf \
/etc/nginx/sites-enabled/goguess.urpi.be.conf
sudo nginx -t
sudo systemctl reload nginx
sudo certbot --nginx -d goguess.urpi.be
sudo certbot renew --dry-run
```
Certbot adds the HTTPS listener, certificate paths, and HTTP-to-HTTPS redirect
to this virtual host. Allow inbound TCP ports `80` and `443`. Do not expose
NodePort `31374`, PostgreSQL `5432`, RabbitMQ `5672`, or RabbitMQ management
ports to the public internet. If Nginx is not on a Kubernetes node, replace
`127.0.0.1` in `proxy_pass` with a private cluster-node address and allow
`31374` only from the Nginx server.
## Deploy and verify
Deploy the production profile:
```bash
make helm-deploy-production
kubectl -n go-guess-production get pods
kubectl -n go-guess-production get service go-guess
curl --fail http://127.0.0.1:31374/api/health
curl --fail https://goguess.urpi.be/api/health
```
The `go-guess` service should show `80:31374/TCP`. The PostgreSQL and RabbitMQ
services should remain `ClusterIP` services without NodePorts.

View file

@ -0,0 +1,21 @@
server {
listen 80;
listen [::]:80;
server_name goguess.urpi.be;
client_max_body_size 10m;
location / {
proxy_pass http://127.0.0.1:31374;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header Connection "";
proxy_buffering off;
proxy_request_buffering off;
proxy_read_timeout 3600s;
proxy_send_timeout 3600s;
}
}