From a86a0cb539758f6bacb4738555c37e4cb85808bc Mon Sep 17 00:00:00 2001 From: Daniel Noulet Date: Sun, 20 Sep 2026 14:07:51 +0200 Subject: [PATCH] feat: configure dedicated server proxy Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .github/workflows/deploy-rancher.yml | 16 +----- .gitignore | 1 + README.md | 14 +++--- deploy/helm/go-guess/templates/NOTES.txt | 3 +- deploy/helm/go-guess/values-production.yaml | 17 ++----- deploy/local.yaml | 55 --------------------- deploy/nginx/README.md | 55 +++++++++++++++++++++ deploy/nginx/goguess.urpi.be.conf | 21 ++++++++ 8 files changed, 91 insertions(+), 91 deletions(-) delete mode 100644 deploy/local.yaml create mode 100644 deploy/nginx/README.md create mode 100644 deploy/nginx/goguess.urpi.be.conf diff --git a/.github/workflows/deploy-rancher.yml b/.github/workflows/deploy-rancher.yml index 6df6a0b..06eb405 100644 --- a/.github/workflows/deploy-rancher.yml +++ b/.github/workflows/deploy-rancher.yml @@ -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" \ diff --git a/.gitignore b/.gitignore index 2f5592d..3eae1ef 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ frontend/dist/ frontend/coverage/ bdd/node_modules/ bdd/reports/ +deploy/local.yaml diff --git a/README.md b/README.md index d0b9fbe..45ef4cc 100644 --- a/README.md +++ b/README.md @@ -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 diff --git a/deploy/helm/go-guess/templates/NOTES.txt b/deploy/helm/go-guess/templates/NOTES.txt index 678da57..7389a46 100644 --- a/deploy/helm/go-guess/templates/NOTES.txt +++ b/deploy/helm/go-guess/templates/NOTES.txt @@ -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 }} diff --git a/deploy/helm/go-guess/values-production.yaml b/deploy/helm/go-guess/values-production.yaml index 3ecb0d9..bf19e1a 100644 --- a/deploy/helm/go-guess/values-production.yaml +++ b/deploy/helm/go-guess/values-production.yaml @@ -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: diff --git a/deploy/local.yaml b/deploy/local.yaml deleted file mode 100644 index 35633c7..0000000 --- a/deploy/local.yaml +++ /dev/null @@ -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" diff --git a/deploy/nginx/README.md b/deploy/nginx/README.md new file mode 100644 index 0000000..518d8f4 --- /dev/null +++ b/deploy/nginx/README.md @@ -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. diff --git a/deploy/nginx/goguess.urpi.be.conf b/deploy/nginx/goguess.urpi.be.conf new file mode 100644 index 0000000..5101736 --- /dev/null +++ b/deploy/nginx/goguess.urpi.be.conf @@ -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; + } +}