From d5ceaa8210cb31bf7702d4e3f8f9c8aca615bcf1 Mon Sep 17 00:00:00 2001 From: Mark Mandel Date: Sun, 5 Mar 2023 11:21:25 -0800 Subject: [PATCH] Move OAuth Setup to a manual process (#98) * Move OAuth Setup to a manual process Moving the manual setup to a manual process, and setup initial callback URL using sslip.io. In the process also included: * Improved error logging for failure to login. * Updated README.md instructions. * Set fixed size on launcher window, also center on screen (mainly because I use a tiling window manager) * Use a static IP for the frontend service so we can template out config variables. Work on #86 * Fix single quoted syntax error. --------- Co-authored-by: Ben Huston --- README.md | 69 ++++++++++++++++--- game/GameLauncher/README.md | 20 ++++-- game/GameLauncher/app.ini | 2 +- game/GameLauncher/main.go | 17 +++-- ...gmap.yaml.tpl => frontend-config.yaml.tpl} | 14 ++++ infrastructure/org-policies.tf | 16 ++--- infrastructure/providers.tf | 2 +- infrastructure/services-gke.tf | 31 ++++----- infrastructure/terraform.tfvars.sample | 4 +- infrastructure/variables.tf | 4 +- services/frontend/.gitignore | 2 +- services/frontend/deployment.yaml | 12 ---- services/profile/deployment.yaml | 2 +- services/skaffold.yaml | 2 +- 14 files changed, 130 insertions(+), 67 deletions(-) rename infrastructure/files/services/{frontend-configmap.yaml.tpl => frontend-config.yaml.tpl} (76%) diff --git a/README.md b/README.md index 15d6c15..34ad0e1 100644 --- a/README.md +++ b/README.md @@ -3,12 +3,28 @@ This multiplayer demo is a cloud first implementation of a global scale, realtime multiplayer game utilising dedicated game servers, utlising both Google Cloud's products and open source gaming solutions. +## OAuth Authentication + +We need to manually set up the OAuth authentication, as unfortunately this cannot be automated. + +The details, such as name and email address of both of these steps don't matter, so feel free to use something +arbitrary for any part not specified. + +Open the [Google OAuth consent screen](https://console.cloud.google.com/apis/credentials/consent) for your project, +and create an "External" App, and allowlist any users you wish to be able to login to your deployment of this game. + +Open the [Google Credentials](https://console.cloud.google.com/apis/credentials) screen for your project, and click +"+ CREATE CREDENTIALS", and create an "OAuth Client ID" of type "Web Application". + +Leave this page open, as we'll need the Client ID and Client secret of the ID you just created shortly. + ## Infrastructure and Services ### Prerequisites To run the Game Demo install, you will need the following applications installed on your workstation: +* A Google Cloud Project * [Terraform](https://developer.hashicorp.com/terraform/tutorials/aws-get-started/install-cli) * [Google Cloud CLI](https://cloud.google.com/sdk/docs/install) @@ -57,11 +73,18 @@ NOTE: The GCS bucket does not have to exist in the same Google project as the Gl cd $GAME_DEMO_HOME/infrastructure terraform init cp terraform.tfvars.sample terraform.tfvars - -# Edit terraform.tfvars as needed, especially . -# Setting `apply_org_policies = true` will also apply any neccessary GCP Org Policies as part of the provioning process. ``` +You will need to now edit `terraform.tfvars` + +* Update with the ID of your Google Cloud Project, +* Updated and with the Client ID and Client secret created in the above step. +* Setting `apply_org_policies = true` will also apply any necessary GCP Org Policies as part of the provisioning + process. + +You can edit other variables in this file, but we recommend leaving the default values for your first run before +experimenting. + ### Provision the infrastructure. ```shell @@ -70,19 +93,26 @@ terraform apply ### OAuth Authentication -Terraform is only able to make an [Internal Oauth consent screen](https://support.google.com/cloud/answer/10311615), -which means that only users from your Google organisation will be able to authenticate against the project when -using logging in via the Game Launcher. +We now need to update our OAuth authentication configuration with the address of our authenticating frontend API. + +Let's grab the IP for that API, by running: + +```shell +gcloud compute addresses list --filter=name=frontend-service --format="value(address)" +``` + +This should give you back an IP, such as `35.202.107.204`. + +1. Click "+ ADD URI" under "Authorised JavaScript origins" and add "http://[IP_ADDRESS].sslip.io". +2. Click "+ ADD URI" under "Authorised redirect URIs" and add "http://[IP_ADDRESS].sslip.io/callback" +3. Click "Save". -You can manually move the consent screen to External (Testing), such that you can allow list accounts outside your -organisation to be able to authenticate against the project, but that has to be a manual step through the -[OAuth Consent screen](https://console.cloud.google.com/apis/credentials/consent). +Since OAuth needs a domain to authenticate against, we'll use [sslip.io](https://sslip.io) for development purposes. ### Deploy Agones To Agones GKE Clusters The Agones deployment is in two steps: The Initial Install and the Allocation Endpoint Patch. - ### Initial Install Replace the` _RELEASE_NAME` substitution with a unique build name. Cloudbuild will deploy Agones using Cloud Deploy. @@ -137,6 +167,25 @@ Open the [`game`](./game) folder in Unreal Engine. Once finished opening, you ca the editor (Hit the ▶️ button), or we can package the project via: Platforms > {your host platform} > Package Project, and execute the resultant package. +## Run the Game Launcher + +{TODO: still requires more details} + +```shell +cd $GAME_DEMO_HOME/game/GameLauncher + +# Grab the IP Address again of our frontend service, so we can use it +gcloud compute addresses list --filter=name=frontend-service --format="value(address)" +``` + +Edit the app.ini, and replace the `frontend_api` value with http://[IP_ADDRESS].sslip.io + +The run: + +```shell +go run main.go +``` + ### Troubleshooting ##### This project was made with a different version of the Unreal Engine. diff --git a/game/GameLauncher/README.md b/game/GameLauncher/README.md index ed20f27..59a7602 100644 --- a/game/GameLauncher/README.md +++ b/game/GameLauncher/README.md @@ -1,15 +1,25 @@ ## Generic game launcher with "Sign-in with google" authentication. -For prerequisites check here: -https://developer.fyne.io/started/ +To run the launcher locally: -For packaging check here: -https://developer.fyne.io/started/packaging +```shell +go run main.go +``` To build locally after installing necessary dependencies just run: -`go build .` +```shell +go build . +``` Move the built binary together with `app.ini` and `assets/` to the game client folder. Launch there. `app.ini` contains the configuration endpoint for the Frontend API as well as executable names for the game client. + +If you want to fully package the launcher: + +For prerequisites check here: +https://developer.fyne.io/started/ + +For packaging check here: +https://developer.fyne.io/started/packaging diff --git a/game/GameLauncher/app.ini b/game/GameLauncher/app.ini index 0424626..abb1b92 100644 --- a/game/GameLauncher/app.ini +++ b/game/GameLauncher/app.ini @@ -19,7 +19,7 @@ callback_listen_port = 8082 binary = DroidShooterClient.exe [linux] -binary = DroidShooterClient +binary = DroidShooterClient.sh [darwin] binary = DroidShooterClient diff --git a/game/GameLauncher/main.go b/game/GameLauncher/main.go index 338346d..1623dff 100644 --- a/game/GameLauncher/main.go +++ b/game/GameLauncher/main.go @@ -17,7 +17,7 @@ package main import ( "encoding/json" "fmt" - "io/ioutil" + "io" "log" "net/http" "os" @@ -63,7 +63,9 @@ func main() { // UI myApp = app.New() myWindow = myApp.NewWindow("Google for Games Launcher") + myWindow.SetFixedSize(true) myWindow.Resize(fyne.NewSize(320, 260)) + myWindow.CenterOnScreen() image := canvas.NewImageFromFile("assets/header.png") image.FillMode = canvas.ImageFillContain @@ -180,19 +182,16 @@ func getPlayerName() string { if err != nil { log.Fatal(err) } - - if response.StatusCode != 200 { - log.Fatal("Unable to fetch user information. Expired token?") - } - defer response.Body.Close() - // Use response.Body to get user information. - - data, err := ioutil.ReadAll(response.Body) + data, err := io.ReadAll(response.Body) if err != nil { log.Fatal(err) } + if response.StatusCode != 200 { + log.Fatalf("Unable to fetch user information. Expired token?: %s", data) + } + var result map[string]interface{} if err := json.Unmarshal(data, &result); err != nil { log.Fatalf("Unable to decode json: %s", err) diff --git a/infrastructure/files/services/frontend-configmap.yaml.tpl b/infrastructure/files/services/frontend-config.yaml.tpl similarity index 76% rename from infrastructure/files/services/frontend-configmap.yaml.tpl rename to infrastructure/files/services/frontend-config.yaml.tpl index 058be3a..9753f7f 100644 --- a/infrastructure/files/services/frontend-configmap.yaml.tpl +++ b/infrastructure/files/services/frontend-config.yaml.tpl @@ -20,7 +20,21 @@ data: CLIENT_ID: ${client_id} CLIENT_SECRET: ${client_secret} LISTEN_PORT: "8080" + CALLBACK_HOSTNAME: http://${service_address}.sslip.io/callback CLIENT_LAUNCHER_PORT: "8082" PROFILE_SERVICE: http://profile PING_SERVICE: http://ping-discovery JWT_KEY: ${jwt_key} +--- +apiVersion: v1 +kind: Service +metadata: + name: frontend +spec: + type: LoadBalancer + selector: + app: frontend + ports: + - port: 80 + targetPort: 8080 + loadBalancerIP: ${service_address} diff --git a/infrastructure/org-policies.tf b/infrastructure/org-policies.tf index 185ed0c..c3ed946 100644 --- a/infrastructure/org-policies.tf +++ b/infrastructure/org-policies.tf @@ -1,8 +1,8 @@ # Optionally apply these Org Policies, as specified in terraform.tfvars file module "gcp_org_policy_v2_requireShieldedVm" { - source = "terraform-google-modules/org-policy/google//modules/org_policy_v2" - version = "~> 5.2.0" + source = "terraform-google-modules/org-policy/google//modules/org_policy_v2" + version = "~> 5.2.0" count = var.apply_org_policies == true ? 1 : 0 policy_root = "project" @@ -18,8 +18,8 @@ module "gcp_org_policy_v2_requireShieldedVm" { } module "gcp_org_policy_v2_disableServiceAccountKeyCreation" { - source = "terraform-google-modules/org-policy/google//modules/org_policy_v2" - version = "~> 5.2.0" + source = "terraform-google-modules/org-policy/google//modules/org_policy_v2" + version = "~> 5.2.0" count = var.apply_org_policies == true ? 1 : 0 policy_root = "project" @@ -35,8 +35,8 @@ module "gcp_org_policy_v2_disableServiceAccountKeyCreation" { } module "gcp_org_policy_v2_vmCanIpForward" { - source = "terraform-google-modules/org-policy/google//modules/org_policy_v2" - version = "~> 5.2.0" + source = "terraform-google-modules/org-policy/google//modules/org_policy_v2" + version = "~> 5.2.0" count = var.apply_org_policies == true ? 1 : 0 policy_root = "project" @@ -52,8 +52,8 @@ module "gcp_org_policy_v2_vmCanIpForward" { } module "gcp_org_policy_v2_vmExternalIpAccess" { - source = "terraform-google-modules/org-policy/google//modules/org_policy_v2" - version = "~> 5.2.0" + source = "terraform-google-modules/org-policy/google//modules/org_policy_v2" + version = "~> 5.2.0" count = var.apply_org_policies == true ? 1 : 0 policy_root = "project" diff --git a/infrastructure/providers.tf b/infrastructure/providers.tf index 1ba87cf..838553d 100644 --- a/infrastructure/providers.tf +++ b/infrastructure/providers.tf @@ -13,7 +13,7 @@ # limitations under the License. provider "google" { - project = var.project + project = var.project user_project_override = true } diff --git a/infrastructure/services-gke.tf b/infrastructure/services-gke.tf index bfd043a..9cd0e94 100644 --- a/infrastructure/services-gke.tf +++ b/infrastructure/services-gke.tf @@ -118,29 +118,28 @@ resource "local_file" "services-ping-service-account" { } # -# OAuth Credentials for the Frontend Service +# Frontend Service # -resource "google_iap_brand" "project_brand" { - support_email = "agones-discuss@googlegroups.com" - application_title = "Global Game Demo" - project = var.project +resource "google_compute_address" "frontend-service" { + project = var.project + provider = google-beta # so we can do labels - depends_on = [google_project_service.project] -} + region = var.services_gke_config.location + name = "frontend-service" -resource "google_iap_client" "project_client" { - display_name = "Global Game Client" - brand = google_iap_brand.project_brand.name + labels = { + "environment" = var.resource_env_label + } } -# Make the environment configmap for the front service resource "local_file" "services-frontend-config-map" { content = templatefile( - "${path.module}/files/services/frontend-configmap.yaml.tpl", { - client_id = google_iap_client.project_client.client_id - client_secret = google_iap_client.project_client.secret - jwt_key = var.frontend-service.jwt_key + "${path.module}/files/services/frontend-config.yaml.tpl", { + service_address = google_compute_address.frontend-service.address + client_id = var.frontend-service.client_id + client_secret = var.frontend-service.client_secret + jwt_key = var.frontend-service.jwt_key }) - filename = "${path.module}/${var.services_directory}/frontend/configmap.yaml" + filename = "${path.module}/${var.services_directory}/frontend/config.yaml" } diff --git a/infrastructure/terraform.tfvars.sample b/infrastructure/terraform.tfvars.sample index 9fb5dfd..7b488b5 100644 --- a/infrastructure/terraform.tfvars.sample +++ b/infrastructure/terraform.tfvars.sample @@ -113,7 +113,9 @@ services_gke_config = { # Frontend Service Config Values frontend-service = { - jwt_key = "r@nd0m$" + client_id = "CLIENT_ID" + client_secret = "CLIENT_SECRET" + jwt_key = "r@nd0m$" } # Artifact Registry variables diff --git a/infrastructure/variables.tf b/infrastructure/variables.tf index 344409a..41c14e9 100644 --- a/infrastructure/variables.tf +++ b/infrastructure/variables.tf @@ -119,7 +119,9 @@ variable "k8s_service_account_id" { variable "frontend-service" { type = object({ - jwt_key = string + client_id = string + client_secret = string + jwt_key = string }) description = "Configuration for the frontend service that provides oAuth authentications" } diff --git a/services/frontend/.gitignore b/services/frontend/.gitignore index 62171cd..1cc1269 100644 --- a/services/frontend/.gitignore +++ b/services/frontend/.gitignore @@ -12,4 +12,4 @@ # See the License for the specific language governing permissions and # limitations under the License. -configmap.yaml \ No newline at end of file +config.yaml \ No newline at end of file diff --git a/services/frontend/deployment.yaml b/services/frontend/deployment.yaml index d63c9ff..ce25879 100644 --- a/services/frontend/deployment.yaml +++ b/services/frontend/deployment.yaml @@ -34,15 +34,3 @@ spec: envFrom: - configMapRef: name: frontend-service ---- -apiVersion: v1 -kind: Service -metadata: - name: frontend -spec: - type: LoadBalancer - selector: - app: frontend - ports: - - port: 80 - targetPort: 8080 diff --git a/services/profile/deployment.yaml b/services/profile/deployment.yaml index fc9d628..67c7fc6 100644 --- a/services/profile/deployment.yaml +++ b/services/profile/deployment.yaml @@ -17,7 +17,7 @@ kind: Service metadata: name: profile spec: - type: LoadBalancer + type: ClusterIP selector: app: profile ports: diff --git a/services/skaffold.yaml b/services/skaffold.yaml index 0e47017..2961244 100644 --- a/services/skaffold.yaml +++ b/services/skaffold.yaml @@ -21,5 +21,5 @@ deploy: - ping-discovery/deployment.yaml - profile/spanner_config.yaml - profile/deployment.yaml + - frontend/config.yaml - frontend/deployment.yaml - - frontend/configmap.yaml