<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Andrew Minkin</title>
    <description>The latest articles on DEV Community by Andrew Minkin (@gen1us2k).</description>
    <link>https://dev.to/gen1us2k</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F846844%2Fab78a138-d8ce-4af6-b55a-d5708e75ee31.jpeg</url>
      <title>DEV Community: Andrew Minkin</title>
      <link>https://dev.to/gen1us2k</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gen1us2k"/>
    <language>en</language>
    <item>
      <title>Testing Kubernetes with Kuttl</title>
      <dc:creator>Andrew Minkin</dc:creator>
      <pubDate>Tue, 20 Dec 2022 08:04:48 +0000</pubDate>
      <link>https://dev.to/gen1us2k/testing-kubernetes-with-kuttl-5gp2</link>
      <guid>https://dev.to/gen1us2k/testing-kubernetes-with-kuttl-5gp2</guid>
      <description>&lt;p&gt;Automated testing is the only way to be sure that your code works. Enabling automated testing can be hard and we say a lot of tools to write automated tests in the industry since the beginning. Some veterans in the industry may remember Selenium,  Cucumber frameworks that help automate testing in the browser. However, testing in Kubernetes can be hard.&lt;/p&gt;

&lt;p&gt;In Percona we deal with Kubernetes and have different operators to automate the management of databases. It requires testing.  A lot of testing. We have different frameworks to help us with it&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Codecept.js to write UI tests for PMM. Also, we use a playwright for some cases.&lt;/li&gt;
&lt;li&gt;We have tools to help us with API testing as well as automating some routines by running bash commands during the test step.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;However, those frameworks are not applicable to test Kubernetes workloads as well as Kubernetes operators. I've been working in the PMM integrations team for six months and saw different approaches to automate testing for PMM/DBaaS. We have a Go test library with wrappers around kubectl and codecept.js for end-to-end tests for the User Interface.&lt;/p&gt;

&lt;h2&gt;
  
  
  What challenges do we have?
&lt;/h2&gt;

&lt;p&gt;Well, to be sure that a database cluster creation works we need to automate the following steps&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Installation of operators to Kubernetes cluster&lt;/li&gt;
&lt;li&gt;Test integration with version service to respect compatibility matrix.&lt;/li&gt;
&lt;li&gt;Create a database cluster and wait once it'll be available&lt;/li&gt;
&lt;li&gt;Do some assertions against Kubernetes as well as UI.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The main pain point here is that we need to wait up to 10-15 minutes for each step and we can't have different test cases to cover as many cases as we can. Yet we can achieve some performance benefits by paralleling workloads, still, it requires learning Javascript and testing framework to work with it. We had some architectural changes recently and moved from our custom gRPC API to create and manage database clusters to an operator that runs on top of other operators and converts K8s' Custom Resource from generic format to operator specific. We had a couple of options for this new project and after research, we chose kuttl as a framework for integration/e2e testing.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is KUTTL anyway and why should I care?
&lt;/h2&gt;

&lt;p&gt;KUTTL is the KUbernetes Test TooL. It's written in Go and provides a declarative way to test Kubernetes operators using Kubernetes primitives. It's easy to start kuttling. Let's take a deeper look. I'll use &lt;a href="https://github.com/percona/dbaas-operator" rel="noopener noreferrer"&gt;dbaas-operator&lt;/a&gt; as an example. DBaaS-operator is an operator that has a simple and generic Custom Resource Definition available to create Percona Server MongoDB or Percona XtraDB Cluster instances in Kubernetes. It uses underlying operators as a dependency. We have the following structure&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;e2e-tests
├── kind.yml
├── kuttl-eks.yml
├── kuttl.yml
└── tests
    └── pxc
        ├── 00-assert.yaml
        ├── 00-deploy-operators.yaml
        ├── 01-assert.yaml
        ├── 01-deploy-pxc.yaml
        ├── 02-assert.yaml
        ├── 02-upgrade-pxc.yaml
        ├── 03-assert.yaml
        ├── 03-restart-pxc.yaml
        ├── 04-delete-cluster.yaml
        ├── 05-assert.yaml
        ├── 05-create-cluster.yaml
        ├── 06-assert.yaml
        ├── 06-scale-up-pxc.yaml
        ├── 07-assert.yaml
        ├── 07-scale-down-pxc.yaml
        └── 08-delete-cluster.yaml

2 directories, 19 files
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Let's discuss these YAML files more&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;kind.yml contains settings to run &lt;a href="https://kind.sigs.k8s.io/" rel="noopener noreferrer"&gt;Kind&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;kuttl.yml has all the required settings for the Kuttl framework and kuttl-eks.yml has some EKS-specific configurations&lt;/li&gt;
&lt;li&gt;tests folder has test steps and assertions&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Kind and KUTTL settings
&lt;/h2&gt;

&lt;p&gt;Let's discuss Kind and kuttl settings and I'll start with KUTTL first&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kuttl.dev/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TestSuite&lt;/span&gt;
&lt;span class="na"&gt;kindConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;e2e-tests/kind.yml&lt;/span&gt;  &lt;span class="c1"&gt;# Path to Kind config that will be used to create Kind clusters&lt;/span&gt;
&lt;span class="na"&gt;crdDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;config/crd/bases&lt;/span&gt;        &lt;span class="c1"&gt;# Path to a directory that contains CRD files. Kuttl will apply them before running tests&lt;/span&gt;
&lt;span class="na"&gt;artifactsDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/tmp/&lt;/span&gt;             &lt;span class="c1"&gt;# Path to a directory to store artifacts such as logs and other information&lt;/span&gt;
&lt;span class="na"&gt;testDirs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;e2e-tests/tests&lt;/span&gt;               &lt;span class="c1"&gt;# Path to directories that have test steps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Kind config is quite easy&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Cluster&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kind.x-k8s.io/v1alpha4&lt;/span&gt;
&lt;span class="na"&gt;nodes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;control-plane&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;worker&lt;/span&gt;
&lt;span class="na"&gt;containerdConfigPatches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="pi"&gt;|-&lt;/span&gt;
  &lt;span class="s"&gt;[plugins."io.containerd.grpc.v1.cri".registry.mirrors."localhost:5000"]&lt;/span&gt;
    &lt;span class="s"&gt;endpoint = ["http://kind-registry:5000"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The aforementioned config will use a local registry and will create 3 k8s worker nodes controlled by the control plane&lt;/p&gt;

&lt;h2&gt;
  
  
  Writing tests
&lt;/h2&gt;

&lt;p&gt;At the first glance, kuttling can be easy because it uses Kubernetes primitives as a test step and assertion but I had a couple of problems testing my operator. Let's take a look at a couple of examples. Since dbaas-operator depends on the PXC operator we need to prepare our environment for testing. Let's write the first test that installs the PXC operator and ensures that it was installed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat e2e-tests/tests/pxc/00-deploy-pxc-operator.yml

apiVersion: kuttl.dev/v1beta1
kind: TestStep
timeout: 10 # Timeout for the test step
commands:
  - command: kubectl apply -f https://raw.githubusercontent.com/percona/percona-xtradb-cluster-operator/v${PXC_OPERATOR_VERSION}/deploy/bundle.yaml -n "${NAMESPACE}"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;KUTTL test steps easily extensible with &lt;a href="https://kuttl.dev/docs/testing/reference.html#commands" rel="noopener noreferrer"&gt;commands&lt;/a&gt;. One can run even scripts as a prerequisite for a test case. PXC operator installs CRDs and creates a deployment and here's an example of assertion.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat e2e-tests/tests/pxc/00-assert.yml

apiVersion: kuttl.dev/v1beta1
kind: TestAssert
timeout: 120  # Timeout waiting for the state
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: perconaxtradbclusters.pxc.percona.com
spec:
  group: pxc.percona.com
  names:
    kind: PerconaXtraDBCluster
    listKind: PerconaXtraDBClusterList
    plural: perconaxtradbclusters
    shortNames:
    - pxc
    - pxcs
    singular: perconaxtradbcluster
  scope: Namespaced
---
apiVersion: apiextensions.k8s.io/v1
kind: CustomResourceDefinition
metadata:
  name: databaseclusters.dbaas.percona.com
spec:
  group: dbaas.percona.com
  names:
    kind: DatabaseCluster
    listKind: DatabaseClusterList
    plural: databaseclusters
    shortNames:
    - db
    singular: databasecluster
  scope: Namespaced
---
apiVersion: apps/v1
kind: Deployment
metadata:
  name: percona-xtradb-cluster-operator
status:
  availableReplicas: 1
  observedGeneration: 1
  readyReplicas: 1
  replicas: 1
  updatedReplicas: 1
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Our first test is ready and one needs to run &lt;code&gt;kubectl kuttl test --config ./e2e-tests/kuttl.yml&lt;/code&gt; to run kuttl.&lt;/p&gt;

&lt;h2&gt;
  
  
  More advanced tests
&lt;/h2&gt;

&lt;p&gt;We need to run our operator first to be able to work with resources and test it. KUTTL recomends configure it via &lt;code&gt;TestSuite&lt;/code&gt; by the following example&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kuttl.dev/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TestSuite&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;span class="na"&gt;commands&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./bin/manager&lt;/span&gt;
&lt;span class="nn"&gt;...&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;However, since dbaas-operator depends on underlying operators it needs to work correctly even if they are not present in a Kubernetes cluster. It has the following logic in the controller&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// SetupWithManager sets up the controller with the Manager.
func (r *DatabaseReconciler) SetupWithManager(mgr ctrl.Manager) error {
    fmt.Println(os.Getenv("WATCH_NAMESPACE"))
    unstructuredResource := &amp;amp;unstructured.Unstructured{}
    unstructuredResource.SetGroupVersionKind(schema.GroupVersionKind{
        Group:   "apiextensions.k8s.io",
        Kind:    "CustomResourceDefinition",
        Version: "v1",
    })
    controller := ctrl.NewControllerManagedBy(mgr).
        For(&amp;amp;dbaasv1.DatabaseCluster{})
    err := r.Get(context.Background(), types.NamespacedName{Name: pxcCRDName}, unstructuredResource)
    if err == nil {
        if err := r.addPXCToScheme(r.Scheme); err == nil {
            controller.Owns(&amp;amp;pxcv1.PerconaXtraDBCluster{})
        }
    }
    err = r.Get(context.Background(), types.NamespacedName{Name: psmdbCRDName}, unstructuredResource)
    if err == nil {
        if err := r.addPSMDBToScheme(r.Scheme); err == nil {
            controller.Owns(&amp;amp;psmdbv1.PerconaServerMongoDB{})
        }
    }
    return controller.Complete(r)
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;controller.Owns&lt;/code&gt; sets up a controller to watch specified resources and once they were changed it'll run a reconciliation loop to sync changes. Also, it checks that operator is present in the cluster by checking that deployment and CRDs are available. It means that to make the operator work correctly in tests we need to choose from the following options&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Restart operator once the upstream operator was installed by sending &lt;code&gt;HUP&lt;/code&gt; signal&lt;/li&gt;
&lt;li&gt;Run the operator only after the underlying operator is present in a cluster&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Hence, I moved the command as the next step before creating a cluster. You can see it below&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;cat e2e-tests/tests/pxc/01-deploy-pxc.yml

apiVersion: kuttl.dev/v1beta1
kind: TestStep
timeout: 10
commands:
  - script: WATCH_NAMESPACE=$NAMESPACE ../../../bin/manager
    background: true
---
apiVersion: dbaas.percona.com/v1
kind: DatabaseCluster
metadata:
  name: test-cluster
spec:
  databaseType: pxc
  databaseImage: percona/percona-xtradb-cluster:8.0.23-14.1
  databaseConfig: |
    [mysqld]
    wsrep_provider_options="debug=1;gcache.size=1G"
  secretsName: pxc-sample-secrets
  clusterSize: 1
  loadBalancer:
    type: haproxy
    exposeType: ClusterIP
    size: 1
    image: percona/percona-xtradb-cluster-operator:1.11.0-haproxy
  dbInstance:
    cpu: "1"
    memory: 1G
    diskSize: 15G
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: &lt;code&gt;command&lt;/code&gt; supports only simple commands and does not fully support env variables. It supports only $NAMESPACE, $PATH and $HOME. However, &lt;code&gt;script&lt;/code&gt; solves the problem of setting &lt;code&gt;WATCH_NAMESPACE&lt;/code&gt; environment variable.&lt;/p&gt;

&lt;p&gt;In nutshell, the test step above does two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Runs the operator&lt;/li&gt;
&lt;li&gt;Creates a database cluster&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The assertion checks that kubernetes cluster has the &lt;code&gt;DatabaseCluster&lt;/code&gt; object with &lt;code&gt;ready&lt;/code&gt; status as well as PXC cluster with the same status.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kuttl.dev/v1beta1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;TestAssert&lt;/span&gt;
&lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;600&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dbaas.percona.com/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;DatabaseCluster&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-cluster&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;databaseType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pxc&lt;/span&gt;
  &lt;span class="na"&gt;databaseImage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;percona/percona-xtradb-cluster:8.0.23-14.1&lt;/span&gt;
  &lt;span class="na"&gt;databaseConfig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
    &lt;span class="s"&gt;[mysqld]&lt;/span&gt;
    &lt;span class="s"&gt;wsrep_provider_options="debug=1;gcache.size=1G"&lt;/span&gt;
  &lt;span class="na"&gt;secretsName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pxc-sample-secrets&lt;/span&gt;
  &lt;span class="na"&gt;clusterSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;loadBalancer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;haproxy&lt;/span&gt;
    &lt;span class="na"&gt;exposeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;percona/percona-xtradb-cluster-operator:1.11.0-haproxy&lt;/span&gt;
  &lt;span class="na"&gt;dbInstance&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
    &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1G&lt;/span&gt;
    &lt;span class="na"&gt;diskSize&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15G&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pxc.percona.com/v1&lt;/span&gt;
&lt;span class="na"&gt;kind&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PerconaXtraDBCluster&lt;/span&gt;
&lt;span class="na"&gt;metadata&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;test-cluster&lt;/span&gt;
&lt;span class="na"&gt;spec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;allowUnsafeConfigurations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="na"&gt;crVersion&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1.11.0&lt;/span&gt;
  &lt;span class="na"&gt;haproxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;percona/percona-xtradb-cluster-operator:1.11.0-haproxy&lt;/span&gt;
    &lt;span class="na"&gt;serviceType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
  &lt;span class="na"&gt;pxc&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;configuration&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
      &lt;span class="s"&gt;[mysqld]&lt;/span&gt;
      &lt;span class="s"&gt;wsrep_provider_options="debug=1;gcache.size=1G"&lt;/span&gt;
    &lt;span class="na"&gt;expose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;percona/percona-xtradb-cluster:8.0.23-14.1&lt;/span&gt;
    &lt;span class="na"&gt;livenessProbes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
    &lt;span class="na"&gt;readinessProbes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
    &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;cpu&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;1"&lt;/span&gt;
        &lt;span class="na"&gt;memory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;1G&lt;/span&gt;
    &lt;span class="na"&gt;serviceType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ClusterIP&lt;/span&gt;
    &lt;span class="na"&gt;sidecarResources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;{}&lt;/span&gt;
    &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
    &lt;span class="na"&gt;volumeSpec&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;persistentVolumeClaim&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;requests&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;storage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;15G&lt;/span&gt;
  &lt;span class="na"&gt;secretsName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;pxc-sample-secrets&lt;/span&gt;
  &lt;span class="na"&gt;updateStrategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;SmartUpdate&lt;/span&gt;
  &lt;span class="na"&gt;upgradeOptions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;apply&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;8.0-recommended&lt;/span&gt;
    &lt;span class="na"&gt;schedule&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;0 4 * * *&lt;/span&gt;
&lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;ready&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;size&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;2&lt;/span&gt;
  &lt;span class="na"&gt;state&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ready&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Caveats and notes
&lt;/h2&gt;

&lt;p&gt;I had problems running tests in Kind. They were flaky because PXC operator can't expose metrics and had problems with the liveness probe. I haven't figured out how to fix it but as a workaround, I use minikube to run tests&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    minikube start --nodes=4 --cpus=2 --memory=4g --apiserver-names host.docker.internal --kubernetes-version=v1.23.6
    minikube kubectl -- config view --flatten --minify &amp;gt; ~/.kube/test-minikube
    KUBECONFIG=~/.kube/test-minikube kubectl kuttl test --config ./e2e-tests/kuttl.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Further steps
&lt;/h2&gt;

&lt;p&gt;There's always room for improvement and I have these steps in mind&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Use docker images and OLM bundles as a way to run the operator for tests. This will be the best way to simulate a production-like environment.&lt;/li&gt;
&lt;li&gt;Add more advanced tests for database clusters such as running queries, trying loading data as well as capacity testing. It's easily achievable with kuttl&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Leadership Lessons from Video Games</title>
      <dc:creator>Andrew Minkin</dc:creator>
      <pubDate>Tue, 02 Aug 2022 12:21:08 +0000</pubDate>
      <link>https://dev.to/gen1us2k/leadership-lessons-from-video-games-37de</link>
      <guid>https://dev.to/gen1us2k/leadership-lessons-from-video-games-37de</guid>
      <description>&lt;p&gt;I love playing games. My favorites are the World of Warcraft and the God Of War series. At the beginning of my career, it was just a way to relax and keep a work-life balance. After a while, I noticed wisdom inside them.&lt;/p&gt;

&lt;p&gt;I became a tech team leader in 2014. As it usually happens, I had no idea what to do in the new position. Everything was new to me. The worst thing about my new position was that I had no plan to educate myself. Thus, I started to read nonfiction books about management, and I watched a lot of management talks from other professionals in this field on YouTube.&lt;/p&gt;

&lt;p&gt;When I read a lot of books about management, philosophy, and psychology, I realized that you can learn how to lead from every situation in your life. For example, you can learn it in a gym from your coach, from movies, and even from video games.&lt;/p&gt;

&lt;h2&gt;
  
  
  Focus on your goal
&lt;/h2&gt;

&lt;p&gt;Goals give you a destination that you need to reach. They help you to track your progress, discover problems, and give you a clear vision. Goals can help you in every area of your life, your job, and your personal life alike.&lt;/p&gt;

&lt;p&gt;Once you have set goals for your project every team member should keep the focus on them. Focusing can help you to activate all abilities in your brain, such as learning, awarenesses, problem-solving, and decision making. Your team will solve problems faster when each team member focuses on one thing for a certain period. Sometimes people can forget about project goals, and it’s a leader’s responsibility to remind them of the key tasks that they should be focused on.&lt;/p&gt;

&lt;p&gt;Once I was a leader in an enterprise project with a slow-release cycle. One main issue was that team didn’t build it from scratch by themselves. The project was new to us, and we made many necessary changes before deployment. Unfortunately, the codebase had a lack of unit tests, and we weren’t sure that our changes wouldn’t break anything. We decided to take a business trip and work on-site in order to decrease the feedback time of our actions and to react faster if anything went wrong.&lt;/p&gt;

&lt;p&gt;We had a plan for deployment and discovered almost all the risks. We planned our deployment for the upcoming weekend, and this was the ideal time to deploy it because we had no users online.&lt;/p&gt;

&lt;p&gt;One of my team members &lt;strong&gt;was worried about deployment and shared his concerns with us&lt;/strong&gt;, let’s call him Mike.&lt;/p&gt;

&lt;p&gt;Mike told us that we could break everything, and we would be unable to roll back the changes. Also, we didn’t have the necessary information in the log files to fix it. This situation was not likely to happen, but I thought that we could fix it if it occurred.&lt;/p&gt;

&lt;p&gt;I used Kratos’ quote from the God Of War 4 game&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Do not concern yourself with what might be. Focus on what is, and always remain vigilant. Kratos (God of War PS4)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;I had played this game before our trip and this quote was quite funny to me. I had no idea that I would use it at work. &lt;strong&gt;A colleague of mine said ‘okay’ and started to prepare the deployment&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Deployment went smoothly without any problems. After a few days of monitoring and fixing small bugs, we determined that deployment had finished successfully.&lt;br&gt;
One evening, later on, Mike came to me and said, “Thank you. Your words were useful for me”. After a small chat, I understood that a simple phrase could make another person confident. Also, &lt;strong&gt;he felt supported by his team&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A leader should keep focused on a team’s goals and dispel a team’s fears, doubts, and uncertainty. A leader needs to be aware of two other areas when leading a team: decision-making and responsibility.&lt;/p&gt;

&lt;h2&gt;
  
  
  Decision-making and taking responsibility
&lt;/h2&gt;

&lt;p&gt;There are a lot of great leaders in the world. Most of them changed our lives and our attitude on some topics. One of them is &lt;a href="https://en.wikipedia.org/wiki/Nelson_Mandela"&gt;Nelson Mandela&lt;/a&gt;. He is a remarkable example of a great leader. He was a South African revolutionary who ended the apartheid regime in his own country. In addition, he was elected as President of South Africa in a fully representative democratic election. He took responsibility for the anti-apartheid campaign and it was one of his goals in his life.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;— A man chooses; a slave obeys. Bioshock&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Mandela could not choose to end apartheid, but he decided to end it. He was a leader who cared and disobeyed the regime as others did.&lt;/p&gt;

&lt;p&gt;As a leader of a team, you will always make decisions, and when you make decisions you need to have as many options as you can to choose the right decision. For example, you have a project with a big backlog and tight deadlines, and you realize that you will never finish all the tasks in the backlog by the deadline. You have many options on how to deal with this situation such as talking with your customer to cut features, focusing on the most significant features, or doing something else. However, it’s possible if you carry out the customer’s orders your team will be overloaded, and this can lead to the emotional burnout of your team members.&lt;/p&gt;

&lt;p&gt;A great example of decision-making and taking responsibility is Jocko Willink’s story he gave in his &lt;a href="https://www.youtube.com/watch?v=ljqra3BcqWM"&gt;TED Talk&lt;/a&gt; about extreme ownership. He told a great story about taking responsibility for the consequences of the entire situation. In his story, the actions were during the war in Iraq. There was a fog of war, and his battalion opened fire. In the end, a friendly Iraqi soldier was shot, and one of his soldiers was wounded.&lt;/p&gt;

&lt;p&gt;The commanding officer said they needed to shut down all operations, and Jocko needed to prepare a debrief. “Who is responsible for what had happened?” was the critical question in the debrief. Jocko realized that as a senior man on the battlefield, he was responsible for everything that happened. Also, he shares lessons learned after the debriefing. When he took responsibility by taking ownership of the problem, the commanding officer started to trust him even more, and so did the team, and they realized that he would never shirk responsibility.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;— Even the good leaders make poor decisions. It is the best leaders who take responsibility for them. Kratos (God of War PS4)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Jocko is an example of a good leader. He gave me a clear understanding of taking ownership.&lt;/p&gt;

&lt;p&gt;When I started to own all project problems and shared this point of view within a team, everything became more accessible. It means that we are all responsible for the outcome of a project. When a team owns a project, they think differently. For instance, when a team is responsible for everything in a project, they care more about quality, about incidents concerning production, and they strive to have a project without problems, while before that, they can look the other way and disregard some problems. The team acts more proactively, and I even had suggestions from the team to talk to customers to gain time to decrease technical debt.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;— We all make choices, but in the end, our choices make us. Andrew Ryan (BioShock)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The project's success depends on the manager&lt;/strong&gt;, and all choices the manager makes give a result that can lead to success and failure. Let’s talk about how we can deal with failures.&lt;/p&gt;

&lt;h2&gt;
  
  
  Retrospect
&lt;/h2&gt;

&lt;p&gt;Taking responsibility for the project’s success as a leader means that you’ll fail sometimes. People deal with failures differently. Some of them take them as lessons, while others take them seriously.&lt;/p&gt;

&lt;p&gt;There are a lot of stories about the failures of successful people you can find on the Internet. Michael Jordan missed 9000 shots in his career. 12 major publishers rejected the first book of Harry Potter. One of Stephen King’s most successful books, Carrie, was rejected by 30 publishers. As you can see, most of them didn’t give up and continued achieving their goal.&lt;/p&gt;

&lt;p&gt;How to deal with a possible failure? One way to deal with it is to face your fear and accept that you’ll fail anyway. There is a chance that it won’t happen, but most of the possible failures become real. When you accept that you will fail, you can change your interpretation of it. You can act differently, and you can see that failure is not as bad as you imagined; and when it’s not bad as you imagined, you can be patient when you deal with consequences.&lt;/p&gt;

&lt;p&gt;You need to accept your failure and &lt;strong&gt;learn&lt;/strong&gt; from it. Failure is a necessity for learning and you need to correct your behavior, find all errors you made, and find a solution on how to not repeat them. That’s how you’ll become better.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Don’t be sorry, be better. Kratos (God of War PS4)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Dealing with black boxes
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;Atreus: We will fight? Why?&lt;br&gt;
Kratos: Because you are afraid of it. (God of War PS4)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Let’s imagine the situation when you need to take a project from another team. The project runs in production and has a lot of users. It was implemented poorly and had no tests and no documentation at all. The last team disappeared. You have no one to ask questions, and you need to implement features for the system. Also, you need to gather a team for the project.&lt;/p&gt;

&lt;p&gt;Once you’ve onboarded new team members to the project, newbies will be afraid to change anything because they will suffer from a lack of documentation and tests in the project. Even if they get a working copy of a project on their machines, they will be afraid to deploy their changes to production because they are not sure they wouldn’t break anything.&lt;/p&gt;

&lt;p&gt;As a leader, you should help your team to solve problems. You need to facilitate discussions, test, and deliver value. You need to have working sessions, discover the risks of changes, and reduce fear in the team by taking responsibility. For instance, you made a change, but your teammate is afraid to deploy it. He feels very uncomfortable in this situation. As a manager, you can push him to deploy it by taking responsibility for his action. You can say that you’ll inform customers, and we can roll it back if anything goes wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The leader needs to constantly improve his skills. Also, he needs to focus on his and the project’s goals, make decisions and take responsibility for them, learn from his failures, and open black boxes. As a leader, you can find leadership lessons almost everywhere in your life. You can find them in the gym, in movies, and even in video games. You can find a lot of wisdom in video games’ stories that apply to real life.&lt;/p&gt;

</description>
      <category>videogames</category>
      <category>leadership</category>
    </item>
    <item>
      <title>Secure microservices with Kong and Ory</title>
      <dc:creator>Andrew Minkin</dc:creator>
      <pubDate>Wed, 13 Apr 2022 13:25:16 +0000</pubDate>
      <link>https://dev.to/gen1us2k/secure-microservices-with-kong-and-ory-3j8l</link>
      <guid>https://dev.to/gen1us2k/secure-microservices-with-kong-and-ory-3j8l</guid>
      <description>&lt;p&gt;Microservice architecture is nowadays almost a standard for backend development. An API gateway is an excellent way to connect a group of microservices to a single API accessible to users. API gateways are available from cloud providers such as AWS/Azure/Google Cloud Platform and Cloudflare. Kong is a scalable API gateway built on open source and as such can be an excellent alternative if you don't want to have your system locked in to a particular vendor.&lt;/p&gt;

&lt;p&gt;This tutorial shows an example using Kong API gateway,&lt;br&gt;
&lt;a href="https://www.ory.sh/docs/kratos" rel="noopener noreferrer"&gt;Ory Kratos&lt;/a&gt;, and &lt;a href="https://www.ory.sh/docs/oathkeeper" rel="noopener noreferrer"&gt;Ory Oathkeeper&lt;/a&gt;. &lt;br&gt;
The illustration below shows you the final architecture we are going to build in this guid&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggqbctgtvaf27u9mpsu9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggqbctgtvaf27u9mpsu9.jpeg" alt="Kong with Ory Products"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The full source code for this tutorial is available on&lt;br&gt;
&lt;a href="https://github.com/gen1us2k/kong_showcase" rel="noopener noreferrer"&gt;github&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  What we will use
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://konghq.com/" rel="noopener noreferrer"&gt;Kong&lt;/a&gt; gateway can be an excellent solution for an
ingress load balancer and API gateway if you do not want vendor lock-in of any
cloud API Gateways in your application. Kong uses
&lt;a href="https://openresty.org/en/" rel="noopener noreferrer"&gt;OpenResty&lt;/a&gt; and Lua. OpenResty extends Nginx with
Lua scripting to use Nginx's event model for non-blocking I/O with HTTP
clients and remote backends like PostgreSQL, Memcached, and Redis. OpenResty
is not an Nginx fork, and Kong is not an Openresty fork. Kong uses OpenResty
to enable
&lt;a href="https://microservices.io/patterns/apigateway.html" rel="noopener noreferrer"&gt;API gateway features&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ory.sh/docs/oathkeeper" rel="noopener noreferrer"&gt;Oathkeeper&lt;/a&gt; acts like an identity and access
proxy for our microservices. It allows to proxy only authenticated requests to
our microservices, and so we don't need to implement a middleware to check
authentication. It can also transform requests, for example convert session
auth into JWT for a back-end service.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://ory.sh/docs/kratos" rel="noopener noreferrer"&gt;Kratos&lt;/a&gt; is the authentication provider; it
handles all first-party authentication flows: username/password, forgot
password, MFA/2FA, &lt;a href="https://www.ory.sh/docs/kratos/self-service" rel="noopener noreferrer"&gt;and more&lt;/a&gt;. It
also provides OIDC/social login capabilities for example "Login with GitHub".&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Building simple microservices
&lt;/h2&gt;

&lt;p&gt;Let's say we have two microservices: &lt;code&gt;hello&lt;/code&gt; and &lt;code&gt;world&lt;/code&gt;. They are pretty simple and serve only to test our API gateway, but you can switch them out for more complex components.&lt;/p&gt;

&lt;p&gt;The "World" microservice exposes a &lt;code&gt;/world&lt;/code&gt; API endpoint and returns a simple JSON message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"message"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;helloJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"World microservice"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/world"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;helloJSON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8090"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The "Hello" microservice exposes a &lt;code&gt;/hello&lt;/code&gt; API endpoint and returns a simple JSON message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight go"&gt;&lt;code&gt;&lt;span class="k"&gt;package&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"encoding/json"&lt;/span&gt;
    &lt;span class="s"&gt;"log"&lt;/span&gt;
    &lt;span class="s"&gt;"net/http"&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;type&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt; &lt;span class="k"&gt;struct&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;Message&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="s"&gt;`json:"message"`&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;helloJSON&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt; &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ResponseWriter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;:=&lt;/span&gt; &lt;span class="n"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;Message&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s"&gt;"Hello microservice"&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Header&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Content-type"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s"&gt;"application/json"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;WriteHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;StatusOK&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;NewEncoder&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;w&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;func&lt;/span&gt; &lt;span class="n"&gt;main&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;HandleFunc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"/hello"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;helloJSON&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;log&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Fatal&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;http&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;ListenAndServe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;":8090"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;

&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We now want to secure the access to these microservices and let only authenticated users access these endpoints.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlkor2ll3qwj6vljnncn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fxlkor2ll3qwj6vljnncn.png" alt="Gopher at work"&gt;&lt;/a&gt;&lt;br&gt;
&lt;em&gt;Okay. Let's start hacking, shall we?&lt;/em&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  Ory Kratos setup
&lt;/h2&gt;

&lt;p&gt;Follow the &lt;a href="https://www.ory.sh/docs/kratos/quickstart" rel="noopener noreferrer"&gt;Quickstart&lt;/a&gt; guide to set up Ory Kratos. In this tutorial you only need a docker-compose file with the following configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;postgres-kratos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;5432:5432"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_USER=kratos&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_PASSWORD=secret&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POSTGRES_DB=kratos&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;intranet&lt;/span&gt;

  &lt;span class="na"&gt;kratos-migrate&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oryd/kratos:v0.8.0-alpha.3&lt;/span&gt;
    &lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres-kratos:postgres-kratos&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DSN=postgres://kratos:secret@postgres-kratos:5432/kratos?sslmode=disable&amp;amp;max_conns=20&amp;amp;max_idle_conns=4&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;intranet&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./kratos&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/config/kratos&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;-c /etc/config/kratos/kratos.yml migrate sql -e --yes&lt;/span&gt;

  &lt;span class="na"&gt;kratos&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oryd/kratos:v0.8.0-alpha.3&lt;/span&gt;
    &lt;span class="na"&gt;links&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;postgres-kratos:postgres-kratos&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DSN=postgres://kratos:secret@postgres-kratos:5432/kratos?sslmode=disable&amp;amp;max_conns=20&amp;amp;max_idle_conns=4&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4433:4433'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4434:4434'&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bind&lt;/span&gt;
        &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./kratos&lt;/span&gt;
        &lt;span class="na"&gt;target&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/etc/config/kratos&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;intranet&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;serve -c /etc/config/kratos/kratos.yml --dev --watch-courier&lt;/span&gt;

  &lt;span class="na"&gt;kratos-selfservice-ui-node&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oryd/kratos-selfservice-ui-node:v0.8.0-alpha.3&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KRATOS_PUBLIC_URL=http://kratos:4433/&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;KRATOS_BROWSER_URL=http://127.0.0.1:4433/&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;intranet&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;4455:3000"&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure&lt;/span&gt;


  &lt;span class="na"&gt;mailslurper&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;oryd/mailslurper:latest-smtps&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4436:4436'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;4437:4437'&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;intranet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Some notes on the network architecture:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;HTTP &lt;code&gt;:4433&lt;/code&gt; and &lt;code&gt;:4434&lt;/code&gt; are the public and admin API's of Ory Kratos.&lt;/li&gt;
&lt;li&gt;HTTP &lt;code&gt;:4436&lt;/code&gt; for Mailslurper - a mock Email server. You can get an activation
link by accessing &lt;a href="http://127.0.0.1:4436" rel="noopener noreferrer"&gt;http://127.0.0.1:4436&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;HTTP &lt;code&gt;:4455&lt;/code&gt; for the UI interface that allows one to start
sign-up/login/recovery flows.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;After running &lt;code&gt;docker-compose up&lt;/code&gt; you can open &lt;code&gt;http://127.0.0.1:4455/welcome&lt;/code&gt; to test your configuration.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configuring Ory Oathkeeper
&lt;/h2&gt;

&lt;p&gt;Now we can start configuring our gateways for this example. Kong is the entry point for the network traffic. Ory Oathkeeper would be accessible from the internal network only in this case. Let's review our architecture diagram from&lt;br&gt;
before:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggqbctgtvaf27u9mpsu9.jpeg" class="article-body-image-wrapper"&gt;&lt;img src="https://media.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fggqbctgtvaf27u9mpsu9.jpeg" alt="Kong with Ory Products"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Oathkeeper checks sessions and proxies traffic to our microservice while Kong provides ingress load balancing. We can even set up &lt;a href="https://en.wikipedia.org/wiki/Round-robin_DNS" rel="noopener noreferrer"&gt;Round-Robin DNS&lt;/a&gt; to have a more robust configuration for our service. Here is how we configure the access rules for Ory Oathkeeper:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="pi"&gt;-&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api:hello-protected"&lt;/span&gt;
  &lt;span class="na"&gt;upstream&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;preserve_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://hello:8090"&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://oathkeeper:4455/hello"&lt;/span&gt;
    &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
  &lt;span class="na"&gt;authenticators&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cookie_session&lt;/span&gt;
  &lt;span class="na"&gt;mutators&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;noop&lt;/span&gt;
  &lt;span class="na"&gt;authorizer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
  &lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redirect&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:4455/login&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt;
  &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;api:world-protected"&lt;/span&gt;
  &lt;span class="na"&gt;upstream&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;preserve_host&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://world:8090"&lt;/span&gt;
  &lt;span class="na"&gt;match&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;http://oathkeeper:4455/world"&lt;/span&gt;
    &lt;span class="na"&gt;methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
  &lt;span class="na"&gt;authenticators&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt;
      &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cookie_session&lt;/span&gt;
  &lt;span class="na"&gt;mutators&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;noop&lt;/span&gt;
  &lt;span class="na"&gt;authorizer&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;allow&lt;/span&gt;
  &lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;redirect&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:4455/login&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The Ory Oathkeeper configuration:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;level&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;debug&lt;/span&gt;
  &lt;span class="na"&gt;format&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;json&lt;/span&gt;

&lt;span class="na"&gt;serve&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;proxy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;cors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;allowed_origins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;*"&lt;/span&gt;
      &lt;span class="na"&gt;allowed_methods&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;POST&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;GET&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PUT&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;PATCH&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;DELETE&lt;/span&gt;
      &lt;span class="na"&gt;allowed_headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Authorization&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Content-Type&lt;/span&gt;
      &lt;span class="na"&gt;exposed_headers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;Content-Type&lt;/span&gt;
      &lt;span class="na"&gt;allow_credentials&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;errors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fallback&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;json&lt;/span&gt;

  &lt;span class="na"&gt;handlers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;redirect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;to&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:4455/login&lt;/span&gt;
        &lt;span class="na"&gt;when&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="pi"&gt;-&lt;/span&gt;
            &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;unauthorized&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;forbidden&lt;/span&gt;
            &lt;span class="na"&gt;request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="na"&gt;header&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                &lt;span class="na"&gt;accept&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
                  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;text/html&lt;/span&gt;
    &lt;span class="na"&gt;json&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;verbose&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;access_rules&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;matching_strategy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;glob&lt;/span&gt;
  &lt;span class="na"&gt;repositories&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;file:///etc/config/oathkeeper/access-rules.yml&lt;/span&gt;

&lt;span class="na"&gt;authenticators&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;anonymous&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;subject&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;guest&lt;/span&gt;

  &lt;span class="na"&gt;cookie_session&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;config&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;check_session_url&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://kratos:4433/sessions/whoami&lt;/span&gt;
      &lt;span class="na"&gt;preserve_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
      &lt;span class="na"&gt;extra_from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@this"&lt;/span&gt;
      &lt;span class="na"&gt;subject_from&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;identity.id"&lt;/span&gt;
      &lt;span class="na"&gt;only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;ory_kratos_session&lt;/span&gt;

  &lt;span class="na"&gt;noop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;authorizers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;allow&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;

&lt;span class="na"&gt;mutators&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;noop&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;enabled&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ory Oathkeeper now looks up a valid session in the request cookies, and proxies only authenticated requests. It redirects to login UI if there's no &lt;code&gt;ory_kratos_session&lt;/code&gt; cookie available.&lt;/p&gt;

&lt;h2&gt;
  
  
  Adding Kong
&lt;/h2&gt;

&lt;p&gt;Now all that is needed is to configure Kong:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;kong-migrations&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kong:latest"&lt;/span&gt;
    &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kong migrations bootstrap&lt;/span&gt;
    &lt;span class="na"&gt;depends_on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;db&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*kong-env&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;intranet&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure&lt;/span&gt;

  &lt;span class="na"&gt;kong&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;platform&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;linux/arm64&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kong:latest"&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;&amp;lt;&amp;lt;&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;*kong-env&lt;/span&gt;
      &lt;span class="na"&gt;KONG_ADMIN_ACCESS_LOG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/stdout&lt;/span&gt;
      &lt;span class="na"&gt;KONG_ADMIN_ERROR_LOG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/stderr&lt;/span&gt;
      &lt;span class="na"&gt;KONG_PROXY_LISTEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${KONG_PROXY_LISTEN:-0.0.0.0:8000}"&lt;/span&gt;
      &lt;span class="na"&gt;KONG_ADMIN_LISTEN&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${KONG_ADMIN_LISTEN:-0.0.0.0:8001}"&lt;/span&gt;
      &lt;span class="na"&gt;KONG_PROXY_ACCESS_LOG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/stdout&lt;/span&gt;
      &lt;span class="na"&gt;KONG_PROXY_ERROR_LOG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/dev/stderr&lt;/span&gt;
      &lt;span class="na"&gt;KONG_PREFIX&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${KONG_PREFIX:-/var/run/kong}&lt;/span&gt;
      &lt;span class="na"&gt;KONG_DECLARATIVE_CONFIG&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/opt/kong/kong.yaml"&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;intranet&lt;/span&gt;
    &lt;span class="na"&gt;ports&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="c1"&gt;# The following two environment variables default to an insecure value (0.0.0.0)&lt;/span&gt;
      &lt;span class="c1"&gt;# according to the CIS Security test.&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${KONG_INBOUND_PROXY_LISTEN:-0.0.0.0}:8000:8000/tcp"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;${KONG_INBOUND_SSL_PROXY_LISTEN:-0.0.0.0}:8443:8443/tcp"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:8001:8001/tcp"&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;127.0.0.1:8444:8444/tcp"&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kong"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;health"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;10s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;10&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure:5&lt;/span&gt;
    &lt;span class="na"&gt;read_only&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
    &lt;span class="na"&gt;volumes&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kong_prefix_vol:${KONG_PREFIX:-/var/run/kong}&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;kong_tmp_vol:/tmp&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./config:/opt/kong&lt;/span&gt;
    &lt;span class="na"&gt;security_opt&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;no-new-privileges&lt;/span&gt;

  &lt;span class="na"&gt;db&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;image&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;postgres:9.6&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_DB&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kong&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_USER&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kong&lt;/span&gt;
      &lt;span class="na"&gt;POSTGRES_PASSWORD&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;kong&lt;/span&gt;
    &lt;span class="na"&gt;healthcheck&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;CMD"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pg_isready"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;-U"&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;kong"&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
      &lt;span class="na"&gt;interval&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;30s&lt;/span&gt;
      &lt;span class="na"&gt;retries&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3&lt;/span&gt;
    &lt;span class="na"&gt;restart&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;on-failure&lt;/span&gt;
    &lt;span class="na"&gt;networks&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;intranet&lt;/span&gt;

  &lt;span class="na"&gt;hello&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The docker-compose creates three containers&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;db container with PostgreSQL database to store the configuration of
services/routes for our API gateway.&lt;/li&gt;
&lt;li&gt;kong-migrate to run migrations against the database.&lt;/li&gt;
&lt;li&gt;kong container that exposes &lt;code&gt;8000&lt;/code&gt; port for proxying traffic and &lt;code&gt;8001&lt;/code&gt; port
with admin API.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As last step, we need to create a service for Kong and configure routes.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/bash&lt;/span&gt;

&lt;span class="c"&gt;# Creates an secure-api service&lt;/span&gt;
&lt;span class="c"&gt;# and proxies network traffic to oathkeeper&lt;/span&gt;
curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8001/services/ &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'name=secure-api'&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'url=http://oathkeeper:4455'&lt;/span&gt;

&lt;span class="c"&gt;# Creates routes for secure-api service&lt;/span&gt;
curl &lt;span class="nt"&gt;-i&lt;/span&gt; &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--url&lt;/span&gt; http://localhost:8001/services/secure-api/routes &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--data&lt;/span&gt; &lt;span class="s1"&gt;'paths[]=/'&lt;/span&gt;&lt;span class="se"&gt;\&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;You can open &lt;a href="http://127.0.0.1:8000/hello" rel="noopener noreferrer"&gt;http://127.0.0.1:8000/hello&lt;/a&gt; or zttp://127.0.0.1:8000/world in your browser and there are two possible scenarios:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;You receive &lt;code&gt;{"message": "Hello microservice"}&lt;/code&gt; (or &lt;code&gt;"World microservice"&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;The browser redirects you to &lt;a href="http://127.0.0.1:4455/login" rel="noopener noreferrer"&gt;http://127.0.0.1:4455/login&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Further steps
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.ory.sh/docs/kratos/guides/zero-trust-iap-proxy-identity-access-proxy" rel="noopener noreferrer"&gt;Configure&lt;/a&gt;
&lt;code&gt;id_token&lt;/code&gt; mutator to have the identity accessible as JWT token for your
microservices.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.ory.sh/docs/kratos/guides/password-policy" rel="noopener noreferrer"&gt;Configure&lt;/a&gt; the
password policy to better suit your use case.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.ory.sh/docs/kratos/guides/two-factor-authentication-2fa-mfa" rel="noopener noreferrer"&gt;Add two-factor authentication&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Consider using &lt;a href="https://docs.nginx.com/nginx/admin-guide/security-controls/configuring-subrequest-authentication/" rel="noopener noreferrer"&gt;authentication based on subrequest result&lt;/a&gt; instead of having an additional reverse proxy inside your network&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/ascho/kong-auth-request" rel="noopener noreferrer"&gt;Kong auth request&lt;/a&gt; can be an excellent plugin to use oathkeeper as a decision API for Kong&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>microservices</category>
      <category>zerotrust</category>
      <category>zerotrustnetworks</category>
      <category>ory</category>
    </item>
  </channel>
</rss>
