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_PASSWORD: ${{ secrets.REGISTRY_PASSWORD }}
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_TLS_SECRET: ${{ vars.GO_GUESS_TLS_SECRET }}
RANCHER_NAMESPACE: ${{ vars.RANCHER_NAMESPACE }}
steps:
@ -82,10 +80,6 @@ jobs:
echo "GO_GUESS_JWT_SECRET must contain at least 32 characters" >&2
exit 1
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}"
if [ -n "$PUBLISHED_SHA" ]; then
image_tag="$PUBLISHED_SHA"
@ -94,7 +88,7 @@ jobs:
fi
frontend_url="$GO_GUESS_FRONTEND_URL"
if [ "$TARGET_ENVIRONMENT" = "production" ] && [ -z "$frontend_url" ]; then
frontend_url="https://$GO_GUESS_HOST"
frontend_url="https://goguess.urpi.be"
fi
{
echo "DEPLOY_NAMESPACE=$namespace"
@ -130,14 +124,6 @@ jobs:
if [ -n "$RESOLVED_FRONTEND_URL" ]; then
set -- "$@" --set-string "app.frontendURL=$RESOLVED_FRONTEND_URL"
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 \
--namespace "$DEPLOY_NAMESPACE" \
--values "$values_file" \

1
.gitignore vendored
View file

@ -6,3 +6,4 @@ frontend/dist/
frontend/coverage/
bdd/node_modules/
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,
and NodePort `30080`. Production uses production fixtures, persistent volumes,
an Nginx ingress, generated secrets that are preserved across upgrades, and
larger resource limits. Before production deployment, replace
`go-guess.example.com` in `values-production.yaml` or override
`app.frontendURL` and `ingress.hosts[0].host`. Configure a TLS entry under
`ingress.tls` when the Rancher cluster does not provide TLS separately.
generated secrets that are preserved across upgrades, and larger resource
limits. It exposes the application at NodePort `31374` for the dedicated-server
Nginx proxy while PostgreSQL and RabbitMQ remain cluster-internal. The public
URL is `https://goguess.urpi.be`; one.com DNS, Nginx, Certbot, firewall, and
verification instructions are in `deploy/nginx/README.md`.
To use external managed services, disable the bundled StatefulSets and provide
full connection URLs:
@ -157,8 +157,8 @@ workflow succeeds. Create GitHub environments named `development` and
- Secrets `REGISTRY_USERNAME`, `REGISTRY_PASSWORD`, and
`GO_GUESS_JWT_SECRET`.
- Variable `RANCHER_NAMESPACE` for the target namespace.
- Production variable `GO_GUESS_HOST`, plus optional
`GO_GUESS_FRONTEND_URL` and `GO_GUESS_TLS_SECRET`.
- Optional variable `GO_GUESS_FRONTEND_URL` when overriding an environment's
configured public URL.
Because the configured image registry uses HTTP, every Rancher cluster node must
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 }}
Open {{ .Values.app.frontendURL }}
{{- 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 }}
Run:
kubectl -n {{ .Release.Namespace }} port-forward service/{{ include "go-guess.fullname" . }} 8080:{{ .Values.service.port }}

View file

@ -4,23 +4,14 @@ image:
app:
environment: production
frontendURL: https://go-guess.example.com
frontendURL: https://goguess.urpi.be
service:
type: ClusterIP
type: NodePort
nodePort: 31374
ingress:
enabled: true
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: []
enabled: false
resources:
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;
}
}