From 35f2bc17fc619cc5b4012403069c140b8cb694c8 Mon Sep 17 00:00:00 2001 From: kdima Date: Tue, 17 Nov 2020 17:53:10 +0000 Subject: [PATCH 01/12] 1495 rest allocation endpoint --- cmd/allocator/main.go | 63 ++++++++++++++++++----------- test/e2e/allocator_test.go | 81 ++++++++++++++++++++++++++++++++++++-- vendor/modules.txt | 1 + 3 files changed, 117 insertions(+), 28 deletions(-) diff --git a/cmd/allocator/main.go b/cmd/allocator/main.go index 269a9f2674..ffd331d4c0 100644 --- a/cmd/allocator/main.go +++ b/cmd/allocator/main.go @@ -19,7 +19,6 @@ import ( "crypto/x509" "fmt" "io/ioutil" - "net" "net/http" "os" "path/filepath" @@ -37,13 +36,13 @@ import ( "agones.dev/agones/pkg/gameservers" "agones.dev/agones/pkg/util/runtime" "agones.dev/agones/pkg/util/signals" + gw_runtime "github.com/grpc-ecosystem/grpc-gateway/runtime" "github.com/heptiolabs/healthcheck" "github.com/pkg/errors" "github.com/sirupsen/logrus" "go.opencensus.io/plugin/ocgrpc" "google.golang.org/grpc" "google.golang.org/grpc/codes" - "google.golang.org/grpc/credentials" "google.golang.org/grpc/keepalive" "google.golang.org/grpc/status" "gopkg.in/fsnotify.v1" @@ -64,6 +63,18 @@ const ( sslPort = "8443" ) +// grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC +// connections or otherHandler otherwise. Copied from https://github.com/philips/grpc-gateway-example. +func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler { + return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + // This is a partial recreation of gRPC's internal checks https://github.com/grpc/grpc-go/pull/514/files#diff-95e9a25b738459a2d3030e1e6fa2a718R61 + if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { + grpcServer.ServeHTTP(w, r) + } else { + otherHandler.ServeHTTP(w, r) + } + }) +} func main() { conf := parseEnvFlags() @@ -101,11 +112,6 @@ func main() { h := newServiceHandler(kubeClient, agonesClient, health, conf.MTLSDisabled, conf.TLSDisabled, conf.remoteAllocationTimeout, conf.totalRemoteAllocationTimeout) - listener, err := net.Listen("tcp", fmt.Sprintf(":%s", sslPort)) - if err != nil { - logger.WithError(err).Fatalf("failed to listen on TCP port %s", sslPort) - } - if !h.tlsDisabled { watcherTLS, err := fsnotify.NewWatcher() if err != nil { @@ -179,10 +185,33 @@ func main() { grpcServer := grpc.NewServer(opts...) pb.RegisterAllocationServiceServer(grpcServer, h) - // serve GRPC for allocation + // opts here + + mux := gw_runtime.NewServeMux() + err = pb.RegisterAllocationServiceHandlerServer(context.Background(), mux, h) + if err != nil { + panic(err) + } + + cfg := &tls.Config{} + if !h.tlsDisabled { + cfg.GetCertificate = h.getTLSCert + } + if !h.mTLSDisabled { + cfg.ClientAuth = tls.RequireAnyClientCert + cfg.VerifyPeerCertificate = h.verifyClientCertificate + } + + // Create a Server instance to listen on port 8443 with the TLS config + server := &http.Server{ + Addr: ":8443", + TLSConfig: cfg, + Handler: grpcHandlerFunc(grpcServer, mux), + } + go func() { - err := grpcServer.Serve(listener) - logger.WithError(err).Fatal("allocation service crashed") + err = server.ListenAndServeTLS("", "") + logger.WithError(err).Fatal("could not listen on REST") os.Exit(1) }() @@ -255,24 +284,10 @@ func readTLSCert() (*tls.Certificate, error) { // getServerOptions returns a list of GRPC server options. // Current options are TLS certs and opencensus stats handler. func (h *serviceHandler) getServerOptions() []grpc.ServerOption { - if h.tlsDisabled { - return []grpc.ServerOption{grpc.StatsHandler(&ocgrpc.ServerHandler{})} - } - - cfg := &tls.Config{ - GetCertificate: h.getTLSCert, - } - - if !h.mTLSDisabled { - cfg.ClientAuth = tls.RequireAnyClientCert - cfg.VerifyPeerCertificate = h.verifyClientCertificate - } - // Add options for creds and OpenCensus stats handler to enable stats and tracing. // The keepalive options are useful for efficiency purposes (keeping a single connection alive // instead of constantly recreating connections), when placing the Agones allocator behind load balancers. return []grpc.ServerOption{ - grpc.Creds(credentials.NewTLS(cfg)), grpc.StatsHandler(&ocgrpc.ServerHandler{}), grpc.KeepaliveEnforcementPolicy(keepalive.EnforcementPolicy{ MinTime: 1 * time.Minute, diff --git a/test/e2e/allocator_test.go b/test/e2e/allocator_test.go index 3cac04bdd3..2d75484063 100644 --- a/test/e2e/allocator_test.go +++ b/test/e2e/allocator_test.go @@ -15,16 +15,20 @@ package e2e import ( + "bytes" "context" "crypto/rand" "crypto/rsa" "crypto/tls" "crypto/x509" "crypto/x509/pkix" + "encoding/json" "encoding/pem" "fmt" + "io/ioutil" "math/big" "net" + "net/http" "testing" "time" @@ -99,6 +103,68 @@ func TestAllocator(t *testing.T) { assert.NoError(t, err) } +func TestRestAllocator(t *testing.T) { + ip, port := getAllocatorEndpoint(t) + requestURL := fmt.Sprintf(allocatorReqURLFmt, ip, port) + tlsCA := refreshAllocatorTLSCerts(t, ip) + + flt, err := createFleet(framework.Namespace) + if !assert.Nil(t, err) { + return + } + framework.AssertFleetCondition(t, flt, e2e.FleetReadyCount(flt.Spec.Replicas)) + request := &pb.AllocationRequest{ + Namespace: framework.Namespace, + RequiredGameServerSelector: &pb.LabelSelector{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}, + PreferredGameServerSelectors: []*pb.LabelSelector{{MatchLabels: map[string]string{agonesv1.FleetNameLabel: flt.ObjectMeta.Name}}}, + Scheduling: pb.AllocationRequest_Packed, + MetaPatch: &pb.MetaPatch{Labels: map[string]string{"gslabel": "allocatedbytest"}}, + } + tlsCfg, err := getTlsConfig(allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA) + if !assert.Nil(t, err) { + return + } + client := &http.Client{ + Transport: &http.Transport{ + TLSClientConfig: tlsCfg, + }, + } + jsonRes, err := json.Marshal(request) + if !assert.Nil(t, err) { + return + } + req, err := http.NewRequest("POST", "https://"+requestURL+"/gameserverallocation", bytes.NewBuffer(jsonRes)) + if !assert.Nil(t, err) { + logrus.WithError(err).Info("failed to create rest request") + return + } + + // wait for the allocation system to come online + err = wait.PollImmediate(2*time.Second, 5*time.Minute, func() (bool, error) { + resp, err := client.Do(req) + if err != nil { + logrus.WithError(err).Info("failed Allocate rest request") + return false, nil + } + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + logrus.WithError(err).Info("failed Allocate rest request") + return false, nil + } + defer resp.Body.Close() + var response pb.AllocationResponse + err = json.Unmarshal(body, &response) + if err != nil { + logrus.WithError(err).Info("failed to unmarshal Allocate response") + return false, nil + } + validateAllocatorResponse(t, &response) + return true, nil + }) + + assert.NoError(t, err) +} + // Tests multi-cluster allocation by reusing the same cluster but across namespace. // Multi-cluster is represented as two namespaces A and B in the same cluster. // Namespace A received the allocation request, but because namespace B has the highest priority, A will forward the request to B. @@ -229,6 +295,15 @@ func getAllocatorEndpoint(t *testing.T) (string, int32) { // createRemoteClusterDialOption creates a grpc client dial option with proper certs to make a remote call. func createRemoteClusterDialOption(namespace, clientSecretName string, tlsCA []byte) (grpc.DialOption, error) { + tlsConfig, err := getTlsConfig(namespace, clientSecretName, tlsCA) + if err != nil { + return nil, err + } + + return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), nil +} + +func getTlsConfig(namespace, clientSecretName string, tlsCA []byte) (*tls.Config, error) { kubeCore := framework.KubeClient.CoreV1() clientSecret, err := kubeCore.Secrets(namespace).Get(clientSecretName, metav1.GetOptions{}) if err != nil { @@ -253,12 +328,10 @@ func createRemoteClusterDialOption(namespace, clientSecretName string, tlsCA []b return nil, errors.New("could not append PEM format CA cert") } - tlsConfig := &tls.Config{ + return &tls.Config{ Certificates: []tls.Certificate{cert}, RootCAs: rootCA, - } - - return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), nil + }, nil } func createFleet(namespace string) (*agonesv1.Fleet, error) { diff --git a/vendor/modules.txt b/vendor/modules.txt index 0defab5af1..d2cd1f378d 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -232,6 +232,7 @@ golang.org/x/net/context golang.org/x/net/context/ctxhttp golang.org/x/net/http/httpguts golang.org/x/net/http2 +golang.org/x/net/http2/h2c golang.org/x/net/http2/hpack golang.org/x/net/idna golang.org/x/net/internal/timeseries From fbb94e35c9d00b0407b8c7c18c4b2af6b520fc74 Mon Sep 17 00:00:00 2001 From: kdima Date: Tue, 17 Nov 2020 18:01:15 +0000 Subject: [PATCH 02/12] fixes --- cmd/allocator/main.go | 2 -- test/e2e/allocator_test.go | 2 +- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/cmd/allocator/main.go b/cmd/allocator/main.go index ffd331d4c0..e03013f127 100644 --- a/cmd/allocator/main.go +++ b/cmd/allocator/main.go @@ -185,8 +185,6 @@ func main() { grpcServer := grpc.NewServer(opts...) pb.RegisterAllocationServiceServer(grpcServer, h) - // opts here - mux := gw_runtime.NewServeMux() err = pb.RegisterAllocationServiceHandlerServer(context.Background(), mux, h) if err != nil { diff --git a/test/e2e/allocator_test.go b/test/e2e/allocator_test.go index 2d75484063..4225e35627 100644 --- a/test/e2e/allocator_test.go +++ b/test/e2e/allocator_test.go @@ -148,7 +148,7 @@ func TestRestAllocator(t *testing.T) { } body, err := ioutil.ReadAll(resp.Body) if err != nil { - logrus.WithError(err).Info("failed Allocate rest request") + logrus.WithError(err).Info("failed to read Allocate response body") return false, nil } defer resp.Body.Close() From ecbd1c2f803ca63cef0fd660897ae91616d833b1 Mon Sep 17 00:00:00 2001 From: kdima Date: Tue, 17 Nov 2020 18:10:44 +0000 Subject: [PATCH 03/12] fixes --- cmd/allocator/main.go | 2 +- test/e2e/allocator_test.go | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/cmd/allocator/main.go b/cmd/allocator/main.go index e03013f127..98b90a9994 100644 --- a/cmd/allocator/main.go +++ b/cmd/allocator/main.go @@ -65,7 +65,7 @@ const ( // grpcHandlerFunc returns an http.Handler that delegates to grpcServer on incoming gRPC // connections or otherHandler otherwise. Copied from https://github.com/philips/grpc-gateway-example. -func grpcHandlerFunc(grpcServer *grpc.Server, otherHandler http.Handler) http.Handler { +func grpcHandlerFunc(grpcServer http.Handler, otherHandler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // This is a partial recreation of gRPC's internal checks https://github.com/grpc/grpc-go/pull/514/files#diff-95e9a25b738459a2d3030e1e6fa2a718R61 if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { diff --git a/test/e2e/allocator_test.go b/test/e2e/allocator_test.go index 4225e35627..3479e6a354 100644 --- a/test/e2e/allocator_test.go +++ b/test/e2e/allocator_test.go @@ -120,7 +120,7 @@ func TestRestAllocator(t *testing.T) { Scheduling: pb.AllocationRequest_Packed, MetaPatch: &pb.MetaPatch{Labels: map[string]string{"gslabel": "allocatedbytest"}}, } - tlsCfg, err := getTlsConfig(allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA) + tlsCfg, err := getTLSConfig(allocatorClientSecretNamespace, allocatorClientSecretName, tlsCA) if !assert.Nil(t, err) { return } @@ -151,7 +151,7 @@ func TestRestAllocator(t *testing.T) { logrus.WithError(err).Info("failed to read Allocate response body") return false, nil } - defer resp.Body.Close() + defer resp.Body.Close() // nolint: errcheck var response pb.AllocationResponse err = json.Unmarshal(body, &response) if err != nil { @@ -295,7 +295,7 @@ func getAllocatorEndpoint(t *testing.T) (string, int32) { // createRemoteClusterDialOption creates a grpc client dial option with proper certs to make a remote call. func createRemoteClusterDialOption(namespace, clientSecretName string, tlsCA []byte) (grpc.DialOption, error) { - tlsConfig, err := getTlsConfig(namespace, clientSecretName, tlsCA) + tlsConfig, err := getTLSConfig(namespace, clientSecretName, tlsCA) if err != nil { return nil, err } @@ -303,7 +303,7 @@ func createRemoteClusterDialOption(namespace, clientSecretName string, tlsCA []b return grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)), nil } -func getTlsConfig(namespace, clientSecretName string, tlsCA []byte) (*tls.Config, error) { +func getTLSConfig(namespace, clientSecretName string, tlsCA []byte) (*tls.Config, error) { kubeCore := framework.KubeClient.CoreV1() clientSecret, err := kubeCore.Secrets(namespace).Get(clientSecretName, metav1.GetOptions{}) if err != nil { From 5b451df61187164b13478faa882e5ef24cee6781 Mon Sep 17 00:00:00 2001 From: kdima Date: Wed, 18 Nov 2020 14:19:36 +0000 Subject: [PATCH 04/12] docs --- .../en/docs/Advanced/allocator-service.md | 33 +++++++++++++++---- .../docs/Advanced/multi-cluster-allocation.md | 11 +++++++ 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index e911f004bc..c057221380 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -6,11 +6,11 @@ description: > Agones provides an mTLS based allocator service that is accessible from outside the cluster using a load balancer. The service is deployed and scales independent to Agones controller. --- -To allocate a game server, Agones in addition to {{< ghlink href="pkg/apis/allocation/v1/gameserverallocation.go" >}}GameServerAllocations{{< /ghlink >}}, provides a gRPC service with mTLS authentication, called `agones-allocator`. +To allocate a game server, Agones in addition to {{< ghlink href="pkg/apis/allocation/v1/gameserverallocation.go" >}}GameServerAllocations{{< /ghlink >}}, provides a gRPC and REST service with mTLS authentication, called `agones-allocator`. -The gRPC service is accessible through a Kubernetes service that is externalized using a load balancer. For the gRPC request to succeed, a client certificate must be provided that is in the authorization list of the allocator service. +Both services are accessible through a Kubernetes service that is externalized using a load balancer and they run on the same port. For requests to succeed, a client certificate must be provided that is in the authorization list of the allocator service. -The remainder of this article describes how to manually make a successful allocation request using the gRPC API. +The remainder of this article describes how to manually make a successful allocation request using the API. The guide assumes you have command line tools installed for [jq](https://stedolan.github.io/jq/), [go](https://golang.org/) and [openssl](https://www.openssl.org/). ## Find the external IP @@ -111,11 +111,13 @@ kubectl get secret allocator-client-ca -o json -n agones-system | jq '.data["cli The last command creates a new entry in the secret data map for `allocator-client-ca` for the client CA. This is for the `agones-allocator` service to accept the newly generated client certificate. -## Send allocation request +## Sending allocation request -After setting up `agones-allocator` with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. To start, take a look at the allocation gRPC client examples in {{< ghlink href="examples/allocator-client/main.go" >}}golang{{< /ghlink >}} and {{< ghlink href="examples/allocator-client-csharp/Program.cs" >}}C#{{< /ghlink >}} languages. In the following, the {{< ghlink href="examples/allocator-client/main.go" >}}golang gRPC client example{{< /ghlink >}} is used to allocate a Game Server in the `default` namespace. +After setting up `agones-allocator` with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. Make sure you have a [fleet]({{< ref "/docs/Getting Started/create-fleet.md" >}}) with ready game servers in the game server namespace. -Make sure you have a [fleet]({{< ref "/docs/Getting Started/create-fleet.md" >}}) with ready game servers in the game server namespace. Then proceed with running the following script. +### Using gRPC + +To start, take a look at the allocation gRPC client examples in {{< ghlink href="examples/allocator-client/main.go" >}}golang{{< /ghlink >}} and {{< ghlink href="examples/allocator-client-csharp/Program.cs" >}}C#{{< /ghlink >}} languages. In the following, the {{< ghlink href="examples/allocator-client/main.go" >}}golang gRPC client example{{< /ghlink >}} is used to allocate a Game Server in the `default` namespace. ```bash #!/bin/bash @@ -140,6 +142,25 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ --cacert ${TLS_CA_FILE} ``` +### Using REST +```bash +#!/bin/bash + +NAMESPACE=default # replace with any namespace +EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +KEY_FILE=client.key +CERT_FILE=client.crt +TLS_CA_FILE=ca.crt + +# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. +# In case of MacOS replace "base64 -d" with "base64 -D" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" +kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" + +curl --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-Type: application/json" --data '{"namespace":"'${NAMESPACE}'"}' https://${EXTERNAL_IP}/gameserverallocation -XPOST +``` + ## Secrets Explained `agones-allocator` has a dependency on three Kubernetes secrets: diff --git a/site/content/en/docs/Advanced/multi-cluster-allocation.md b/site/content/en/docs/Advanced/multi-cluster-allocation.md index 04a114f7a5..7aad3a85f2 100644 --- a/site/content/en/docs/Advanced/multi-cluster-allocation.md +++ b/site/content/en/docs/Advanced/multi-cluster-allocation.md @@ -103,6 +103,17 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ --multicluster true ``` +If using REST use + +```bash +go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ + --port 443 \ + --namespace ${NAMESPACE} \ + --key ${KEY_FILE} \ + --cert ${CERT_FILE} \ + --cacert ${TLS_CA_FILE} +``` + ## Troubleshooting If you encounter problems, explore the following potential root causes: From 272d48509f1129a6a3762ec6d3ce1394facae86f Mon Sep 17 00:00:00 2001 From: kdima Date: Wed, 18 Nov 2020 15:05:16 +0000 Subject: [PATCH 05/12] rename --- site/content/en/docs/Advanced/allocator-service.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index c057221380..dfccc9cc3b 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -111,7 +111,7 @@ kubectl get secret allocator-client-ca -o json -n agones-system | jq '.data["cli The last command creates a new entry in the secret data map for `allocator-client-ca` for the client CA. This is for the `agones-allocator` service to accept the newly generated client certificate. -## Sending allocation request +## Send allocation request After setting up `agones-allocator` with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. Make sure you have a [fleet]({{< ref "/docs/Getting Started/create-fleet.md" >}}) with ready game servers in the game server namespace. From b94451624988bdbb185c498a37bbe1e55da944c4 Mon Sep 17 00:00:00 2001 From: kdima Date: Wed, 18 Nov 2020 15:34:40 +0000 Subject: [PATCH 06/12] minor fix --- site/content/en/docs/Advanced/allocator-service.md | 1 + 1 file changed, 1 insertion(+) diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index dfccc9cc3b..3a18b7a796 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -143,6 +143,7 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ ``` ### Using REST + ```bash #!/bin/bash From c808770779e34ec7c87cb8af32cb8e2103ea65cd Mon Sep 17 00:00:00 2001 From: kdima Date: Fri, 20 Nov 2020 13:51:32 +0000 Subject: [PATCH 07/12] cleanup --- cmd/allocator/main.go | 8 +++- .../en/docs/Advanced/allocator-service.md | 39 +++++++++++++++++++ .../docs/Advanced/multi-cluster-allocation.md | 23 ++++++++--- 3 files changed, 62 insertions(+), 8 deletions(-) diff --git a/cmd/allocator/main.go b/cmd/allocator/main.go index 98b90a9994..b9cd4893e9 100644 --- a/cmd/allocator/main.go +++ b/cmd/allocator/main.go @@ -208,8 +208,12 @@ func main() { } go func() { - err = server.ListenAndServeTLS("", "") - logger.WithError(err).Fatal("could not listen on REST") + if !h.tlsDisabled { + err = server.ListenAndServeTLS("", "") + } else { + err = server.ListenAndServe() + } + logger.WithError(err).Fatal("unable to start HTTPS listener") os.Exit(1) }() diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index 3a18b7a796..350814ef96 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -111,6 +111,38 @@ kubectl get secret allocator-client-ca -o json -n agones-system | jq '.data["cli The last command creates a new entry in the secret data map for `allocator-client-ca` for the client CA. This is for the `agones-allocator` service to accept the newly generated client certificate. +{{% feature expiryVersion="1.11.0" %}} +## Send allocation request + +After setting up `agones-allocator` with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. To start, take a look at the allocation gRPC client examples in {{< ghlink href="examples/allocator-client/main.go" >}}golang{{< /ghlink >}} and {{< ghlink href="examples/allocator-client-csharp/Program.cs" >}}C#{{< /ghlink >}} languages. In the following, the {{< ghlink href="examples/allocator-client/main.go" >}}golang gRPC client example{{< /ghlink >}} is used to allocate a Game Server in the `default` namespace. + +Make sure you have a [fleet]({{< ref "/docs/Getting Started/create-fleet.md" >}}) with ready game servers in the game server namespace. Then proceed with running the following script. + +```bash +#!/bin/bash + +NAMESPACE=default # replace with any namespace +EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +KEY_FILE=client.key +CERT_FILE=client.crt +TLS_CA_FILE=ca.crt + +# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. +# In case of MacOS replace "base64 -d" with "base64 -D" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" +kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" + +go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ + --port 443 \ + --namespace ${NAMESPACE} \ + --key ${KEY_FILE} \ + --cert ${CERT_FILE} \ + --cacert ${TLS_CA_FILE} +``` +{{% /feature %}} + +{{% feature publishVersion="1.11.0" %}} ## Send allocation request After setting up `agones-allocator` with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. Make sure you have a [fleet]({{< ref "/docs/Getting Started/create-fleet.md" >}}) with ready game servers in the game server namespace. @@ -162,6 +194,13 @@ kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\. curl --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-Type: application/json" --data '{"namespace":"'${NAMESPACE}'"}' https://${EXTERNAL_IP}/gameserverallocation -XPOST ``` +You should expect to see the following output: + +``` + +``` +{{% /feature %}} + ## Secrets Explained `agones-allocator` has a dependency on three Kubernetes secrets: diff --git a/site/content/en/docs/Advanced/multi-cluster-allocation.md b/site/content/en/docs/Advanced/multi-cluster-allocation.md index 7aad3a85f2..051c321a9c 100644 --- a/site/content/en/docs/Advanced/multi-cluster-allocation.md +++ b/site/content/en/docs/Advanced/multi-cluster-allocation.md @@ -103,16 +103,27 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ --multicluster true ``` +{{% feature publishVersion="1.11.0" %}} If using REST use ```bash -go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ - --port 443 \ - --namespace ${NAMESPACE} \ - --key ${KEY_FILE} \ - --cert ${CERT_FILE} \ - --cacert ${TLS_CA_FILE} +#!/bin/bash + +NAMESPACE=default # replace with any namespace +EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +KEY_FILE=client.key +CERT_FILE=client.crt +TLS_CA_FILE=ca.crt + +# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. +# In case of MacOS replace "base64 -d" with "base64 -D" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" +kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" + +curl --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-Type: application/json" --data '{"namespace":"'${NAMESPACE}'", "multi_cluster_settings":{"enabled":"true"}}' https://${EXTERNAL_IP}/gameserverallocation -XPOST ``` +{{% /feature %}} ## Troubleshooting From eba1cc19af0601b3e13eefa385b9f75c0df42058 Mon Sep 17 00:00:00 2001 From: kdima Date: Fri, 20 Nov 2020 16:04:00 +0000 Subject: [PATCH 08/12] fixes --- cmd/allocator/main.go | 7 +++++-- site/content/en/docs/Advanced/allocator-service.md | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/cmd/allocator/main.go b/cmd/allocator/main.go index b9cd4893e9..0d7dfd3ffe 100644 --- a/cmd/allocator/main.go +++ b/cmd/allocator/main.go @@ -213,8 +213,11 @@ func main() { } else { err = server.ListenAndServe() } - logger.WithError(err).Fatal("unable to start HTTPS listener") - os.Exit(1) + + if err != nil { + logger.WithError(err).Fatal("unable to start HTTP/HTTPS listener") + os.Exit(1) + } }() // Finally listen on 8080 (http) and block the main goroutine diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index 350814ef96..7ee036b8a0 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -197,7 +197,7 @@ curl --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-T You should expect to see the following output: ``` - +{"gameServerName":"game-server-name","ports":[{"name":"default","port":7463}],"address":"1.2.3.4","nodeName":"node-name"} ``` {{% /feature %}} From e11a001908628b1f6c4cd4e0968725f6ab9d113f Mon Sep 17 00:00:00 2001 From: kdima Date: Fri, 20 Nov 2020 16:08:34 +0000 Subject: [PATCH 09/12] docs --- site/content/en/docs/Advanced/allocator-service.md | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index 7ee036b8a0..2e6a1ce27d 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -5,12 +5,20 @@ publishDate: 2019-10-25T05:45:05Z description: > Agones provides an mTLS based allocator service that is accessible from outside the cluster using a load balancer. The service is deployed and scales independent to Agones controller. --- +{{% feature expiryVersion="1.11.0" %}} +To allocate a game server, Agones in addition to {{< ghlink href="pkg/apis/allocation/v1/gameserverallocation.go" >}}GameServerAllocations{{< /ghlink >}}, provides a gRPC service with mTLS authentication, called `agones-allocator`. + +The gRPC service is accessible through a Kubernetes service that is externalized using a load balancer. For the gRPC request to succeed, a client certificate must be provided that is in the authorization list of the allocator service. + +The remainder of this article describes how to manually make a successful allocation request using the gRPC API. +{{% /feature %}} +{{% feature publishVersion="1.11.0" %}} To allocate a game server, Agones in addition to {{< ghlink href="pkg/apis/allocation/v1/gameserverallocation.go" >}}GameServerAllocations{{< /ghlink >}}, provides a gRPC and REST service with mTLS authentication, called `agones-allocator`. Both services are accessible through a Kubernetes service that is externalized using a load balancer and they run on the same port. For requests to succeed, a client certificate must be provided that is in the authorization list of the allocator service. - The remainder of this article describes how to manually make a successful allocation request using the API. +{{% /feature %}} The guide assumes you have command line tools installed for [jq](https://stedolan.github.io/jq/), [go](https://golang.org/) and [openssl](https://www.openssl.org/). ## Find the external IP From 1bd593c7a2d4b2af33ca7b5682b4b26586bb9b94 Mon Sep 17 00:00:00 2001 From: kdima Date: Thu, 26 Nov 2020 14:08:20 +0000 Subject: [PATCH 10/12] docs --- cmd/allocator/main.go | 1 + .../en/docs/Advanced/allocator-service.md | 21 ++++++++----------- .../docs/Advanced/multi-cluster-allocation.md | 16 +++++++------- 3 files changed, 19 insertions(+), 19 deletions(-) diff --git a/cmd/allocator/main.go b/cmd/allocator/main.go index 0d7dfd3ffe..524668cbff 100644 --- a/cmd/allocator/main.go +++ b/cmd/allocator/main.go @@ -68,6 +68,7 @@ const ( func grpcHandlerFunc(grpcServer http.Handler, otherHandler http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { // This is a partial recreation of gRPC's internal checks https://github.com/grpc/grpc-go/pull/514/files#diff-95e9a25b738459a2d3030e1e6fa2a718R61 + // We switch on HTTP/1.1 or HTTP/2 by checking the ProtoMajor if r.ProtoMajor == 2 && strings.Contains(r.Header.Get("Content-Type"), "application/grpc") { grpcServer.ServeHTTP(w, r) } else { diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index 2e6a1ce27d..8e85bbc8d7 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -155,6 +155,15 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ After setting up `agones-allocator` with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. Make sure you have a [fleet]({{< ref "/docs/Getting Started/create-fleet.md" >}}) with ready game servers in the game server namespace. +Set the following environment variables: +``` +NAMESPACE=default # replace with any namespace +EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +KEY_FILE=client.key +CERT_FILE=client.crt +TLS_CA_FILE=ca.crt +``` + ### Using gRPC To start, take a look at the allocation gRPC client examples in {{< ghlink href="examples/allocator-client/main.go" >}}golang{{< /ghlink >}} and {{< ghlink href="examples/allocator-client-csharp/Program.cs" >}}C#{{< /ghlink >}} languages. In the following, the {{< ghlink href="examples/allocator-client/main.go" >}}golang gRPC client example{{< /ghlink >}} is used to allocate a Game Server in the `default` namespace. @@ -162,12 +171,6 @@ To start, take a look at the allocation gRPC client examples in {{< ghlink href= ```bash #!/bin/bash -NAMESPACE=default # replace with any namespace -EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -KEY_FILE=client.key -CERT_FILE=client.crt -TLS_CA_FILE=ca.crt - # allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. # In case of MacOS replace "base64 -d" with "base64 -D" kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" @@ -187,12 +190,6 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ ```bash #!/bin/bash -NAMESPACE=default # replace with any namespace -EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -KEY_FILE=client.key -CERT_FILE=client.crt -TLS_CA_FILE=ca.crt - # allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. # In case of MacOS replace "base64 -d" with "base64 -D" kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" diff --git a/site/content/en/docs/Advanced/multi-cluster-allocation.md b/site/content/en/docs/Advanced/multi-cluster-allocation.md index 051c321a9c..0efcafd1f1 100644 --- a/site/content/en/docs/Advanced/multi-cluster-allocation.md +++ b/site/content/en/docs/Advanced/multi-cluster-allocation.md @@ -91,10 +91,18 @@ EOF To enable multi-cluster allocation, set `multiClusterSetting.enabled` to `true` in {{< ghlink href="proto/allocation/allocation.proto" >}}allocation.proto{{< /ghlink >}} and send allocation requests. For more information visit [agones-allocator]({{< relref "allocator-service.md">}}). In the following, using {{< ghlink href="examples/allocator-client/main.go" >}}allocator-client sample{{< /ghlink >}}, a multi-cluster allocation request is sent to the agones-allocator service. -Follow [agones-allocator]({{< relref "allocator-service.md#send-allocation-request">}}) to set the environment variables. +Set the following environment variables: +``` +NAMESPACE=default # replace with any namespace +EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') +KEY_FILE=client.key +CERT_FILE=client.crt +TLS_CA_FILE=ca.crt +``` ```bash #!/bin/bash + go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ --namespace ${NAMESPACE} \ --key ${KEY_FILE} \ @@ -109,12 +117,6 @@ If using REST use ```bash #!/bin/bash -NAMESPACE=default # replace with any namespace -EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') -KEY_FILE=client.key -CERT_FILE=client.crt -TLS_CA_FILE=ca.crt - # allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. # In case of MacOS replace "base64 -d" with "base64 -D" kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" From cd385f8ee19234e3fdcdec7be0c391c0222ad8af Mon Sep 17 00:00:00 2001 From: kdima Date: Mon, 30 Nov 2020 17:03:55 +0000 Subject: [PATCH 11/12] docs --- cmd/allocator/main.go | 4 ++-- .../en/docs/Advanced/allocator-service.md | 21 +++++++------------ 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/cmd/allocator/main.go b/cmd/allocator/main.go index 524668cbff..72c7f0d2c5 100644 --- a/cmd/allocator/main.go +++ b/cmd/allocator/main.go @@ -288,9 +288,9 @@ func readTLSCert() (*tls.Certificate, error) { } // getServerOptions returns a list of GRPC server options. -// Current options are TLS certs and opencensus stats handler. +// Current options are opencensus stats handler. func (h *serviceHandler) getServerOptions() []grpc.ServerOption { - // Add options for creds and OpenCensus stats handler to enable stats and tracing. + // Add options for OpenCensus stats handler to enable stats and tracing. // The keepalive options are useful for efficiency purposes (keeping a single connection alive // instead of constantly recreating connections), when placing the Agones allocator behind load balancers. return []grpc.ServerOption{ diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index 8e85bbc8d7..f83bc004e7 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -155,13 +155,20 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ After setting up `agones-allocator` with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. Make sure you have a [fleet]({{< ref "/docs/Getting Started/create-fleet.md" >}}) with ready game servers in the game server namespace. -Set the following environment variables: +Reuse following snippet for both grpc and rest examples: + ``` NAMESPACE=default # replace with any namespace EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') KEY_FILE=client.key CERT_FILE=client.crt TLS_CA_FILE=ca.crt + +# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. +# In case of MacOS replace "base64 -d" with "base64 -D" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" +kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" ``` ### Using gRPC @@ -171,12 +178,6 @@ To start, take a look at the allocation gRPC client examples in {{< ghlink href= ```bash #!/bin/bash -# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. -# In case of MacOS replace "base64 -d" with "base64 -D" -kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" -kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" -kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" - go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ --port 443 \ --namespace ${NAMESPACE} \ @@ -190,12 +191,6 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ ```bash #!/bin/bash -# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. -# In case of MacOS replace "base64 -d" with "base64 -D" -kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" -kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" -kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" - curl --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-Type: application/json" --data '{"namespace":"'${NAMESPACE}'"}' https://${EXTERNAL_IP}/gameserverallocation -XPOST ``` From c3bd3b425eff979cc2aab89caaae868972bd4966 Mon Sep 17 00:00:00 2001 From: kdima Date: Tue, 1 Dec 2020 10:39:46 +0000 Subject: [PATCH 12/12] fixes --- site/content/en/docs/Advanced/allocator-service.md | 2 +- .../en/docs/Advanced/multi-cluster-allocation.md | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/site/content/en/docs/Advanced/allocator-service.md b/site/content/en/docs/Advanced/allocator-service.md index f83bc004e7..12e917ef7a 100644 --- a/site/content/en/docs/Advanced/allocator-service.md +++ b/site/content/en/docs/Advanced/allocator-service.md @@ -155,7 +155,7 @@ go run examples/allocator-client/main.go --ip ${EXTERNAL_IP} \ After setting up `agones-allocator` with server certificate and allowlisting the client certificate, the service can be used to allocate game servers. Make sure you have a [fleet]({{< ref "/docs/Getting Started/create-fleet.md" >}}) with ready game servers in the game server namespace. -Reuse following snippet for both grpc and rest examples: +Set the environment variables and store the client secrets before allocating using gRPC or REST APIs: ``` NAMESPACE=default # replace with any namespace diff --git a/site/content/en/docs/Advanced/multi-cluster-allocation.md b/site/content/en/docs/Advanced/multi-cluster-allocation.md index 0efcafd1f1..46439931ec 100644 --- a/site/content/en/docs/Advanced/multi-cluster-allocation.md +++ b/site/content/en/docs/Advanced/multi-cluster-allocation.md @@ -91,13 +91,19 @@ EOF To enable multi-cluster allocation, set `multiClusterSetting.enabled` to `true` in {{< ghlink href="proto/allocation/allocation.proto" >}}allocation.proto{{< /ghlink >}} and send allocation requests. For more information visit [agones-allocator]({{< relref "allocator-service.md">}}). In the following, using {{< ghlink href="examples/allocator-client/main.go" >}}allocator-client sample{{< /ghlink >}}, a multi-cluster allocation request is sent to the agones-allocator service. -Set the following environment variables: +Set the environment variables and store the client secrets before allocating using gRPC or REST APIs ``` NAMESPACE=default # replace with any namespace EXTERNAL_IP=$(kubectl get services agones-allocator -n agones-system -o jsonpath='{.status.loadBalancer.ingress[0].ip}') KEY_FILE=client.key CERT_FILE=client.crt TLS_CA_FILE=ca.crt + +# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. +# In case of MacOS replace "base64 -d" with "base64 -D" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" +kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" +kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" ``` ```bash @@ -117,12 +123,6 @@ If using REST use ```bash #!/bin/bash -# allocator-client.default secret is created only when using helm installation. Otherwise generate the client certificate and replace the following. -# In case of MacOS replace "base64 -d" with "base64 -D" -kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.crt}" | base64 -d > "${CERT_FILE}" -kubectl get secret allocator-client.default -n "${NAMESPACE}" -ojsonpath="{.data.tls\.key}" | base64 -d > "${KEY_FILE}" -kubectl get secret allocator-tls-ca -n agones-system -ojsonpath="{.data.tls-ca\.crt}" | base64 -d > "${TLS_CA_FILE}" - curl --key ${KEY_FILE} --cert ${CERT_FILE} --cacert ${TLS_CA_FILE} -H "Content-Type: application/json" --data '{"namespace":"'${NAMESPACE}'", "multi_cluster_settings":{"enabled":"true"}}' https://${EXTERNAL_IP}/gameserverallocation -XPOST ``` {{% /feature %}}