{"id":4916,"date":"2023-07-29T15:15:42","date_gmt":"2023-07-29T13:15:42","guid":{"rendered":"http:\/\/miro.borodziuk.eu\/?p=4916"},"modified":"2024-02-13T18:11:54","modified_gmt":"2024-02-13T17:11:54","slug":"network-security-on-openshift","status":"publish","type":"post","link":"http:\/\/miro.borodziuk.eu\/index.php\/2023\/07\/29\/network-security-on-openshift\/","title":{"rendered":"Network Security on Openshift"},"content":{"rendered":"<p><!--more--><\/p>\n<p><span style=\"color: #3366ff;\">OpenShift Networking<\/span><\/p>\n<ul>\n<li>Services provide load balancing to replicated Pods in an application, and are essential in providing access to applications\n<ul>\n<li>Services connect to Endpoints, which are Pod individual IP addresses<\/li>\n<\/ul>\n<\/li>\n<li>Ingress is a Kubernetes resource that exposes services to external users\n<ul>\n<li>Ingress adds URLs, load balancing, as well as access rules<\/li>\n<li>Ingress is not used as such in OpenShift<\/li>\n<\/ul>\n<\/li>\n<li>OpenShift routes are an alternative to Ingress<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">OpenShift SDN<\/span><\/p>\n<ul>\n<li>OpenShift uses Software Defined Networking (SDN) to implement connectivity<\/li>\n<li>OpenShift SDN separates the network in a control plane and a data plane<\/li>\n<li>The SDN solves five requirements\n<ul>\n<li>Manage network traffic and resources as software such that they can be programmed by the application owners<\/li>\n<li>Communicate between containers running within the same project<\/li>\n<li>Communicate between Pods within and beyond project boundaries<\/li>\n<li>Manage network communication from a Pod to a service<\/li>\n<li>Manage network communication from external network to service<\/li>\n<\/ul>\n<\/li>\n<li>The network is managed by the OpenShift Cluster Network Operator<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">The DNS Operator<\/span><\/p>\n<ul>\n<li>The DNS operator implements the CoreDNS DNS server<\/li>\n<li>The internal CoreDNS server is used by Pods for DNS resolution<\/li>\n<li>Use <code>oc describe dns.operator\/default<\/code> to see its config<\/li>\n<li>The DNS Operator has different roles\n<ul>\n<li>Create a default cluster DNS name cluster.local<\/li>\n<li>Assign DNS names to namespaces<\/li>\n<li>Assign DNS names to services<\/li>\n<li>Assign DNS names to Pods<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Managing DNS Records for Services<\/span><\/p>\n<ul>\n<li>DNS names are composed as<code> servicename.projectname.cluster-dns-name<\/code>\n<ul>\n<li><code>db.myproject.cluster.local<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Apart from the A resource records, core DNS also implements an SRV record, in which port name and protocol are prepended to the service A record name\n<ul>\n<li><code>_443._tcp.webserver.myproject.cluster.local<\/code><\/li>\n<\/ul>\n<\/li>\n<li>If a service has no IP address, DNS records are created for the IP addresses of the Pods, and roundrobin is applied<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">The Cluster Network Operator<\/span><\/p>\n<ul>\n<li>The OpenShift Cluster Network Operator defines how the network is shaped and provides information about the following:\n<ul>\n<li>Network address<\/li>\n<li>Network mode<\/li>\n<li>Network provider<\/li>\n<li>IP address pools<\/li>\n<\/ul>\n<\/li>\n<li>Use <code>oc get network\/cluster -o yaml<\/code> for details<\/li>\n<li>Notice that currently OpenShift only supports the OpenShift SDN network provider, this may have changed by the time you read this<\/li>\n<li>Future versions will use OVN-Kubernetes to manage the cluster network<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Network Policy<\/span><\/p>\n<ul>\n<li>Network policy allows defining Ingress and Egress filtering<\/li>\n<li>If no network policy exists, all traffic is allowed<\/li>\n<li>If a network policy exists, it will block all traffic with the exception of allowed Ingress and Egress traffic<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Service Types<\/span><\/p>\n<ul>\n<li><strong>ClusterIP<\/strong>: the service is exposed as an IP address internal to the cluster. This is used as the default type, where services cannot be directly addressed<\/li>\n<li><strong>NodePort:<\/strong> a service type that exposes a port on the node IP address.<\/li>\n<li><strong>LoadBalancer<\/strong>: exposes the service through a cloud provider load balancer. The cloud provider LB talks to the OpenShift network controller to automatically create a node port to route incoming requests<\/li>\n<li><strong>ExternalName<\/strong>: creates a CNAME in DNS to match an external host name. Use this to create different access points for applications external to the cluster<\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">The Ingress Resource<\/span><\/p>\n<ul>\n<li>Ingress traffic is generic terminology incoming traffic (and is more than just<br \/>\nKubernetes Ingress)<\/li>\n<li>The Ingress resources is managed by the Ingress operator and accepts external requests that will be proxied<\/li>\n<li>The route resource is an OpenShift resource that provides more features than Ingress\n<ul>\n<li>TLS re-encryption<\/li>\n<li>TLS passthrough<\/li>\n<li>split traffic for blue-green deployments<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">OpenShift Route<\/span><\/p>\n<ul>\n<li>OpenShift route resources are implemented by the shared router serviace that runs as a Pod in the cluster<\/li>\n<li>Router Pods bind to public-facing IP addresses on the nodes<\/li>\n<li>DNS wildcard is required for this to work<\/li>\n<li>Routes can be implemented as secure and as insecure routes<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Creating routes requirements<\/span><\/p>\n<ul>\n<li>Route resources need the following values:\n<ul>\n<li>Name of the service that the route accesses<\/li>\n<li>A host name for the route that is related to the cluster DNS wildcard domain<\/li>\n<li>An optional path for path-based routes<\/li>\n<li>A target port, which is where the application listens<\/li>\n<li>An encryption strategy<\/li>\n<li>Optional labels that can be used as selectors<\/li>\n<\/ul>\n<\/li>\n<li>Notice that the route does not use the service directly, it just needs it to find out to which Pods it should connect<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Route Options and Types<\/span><\/p>\n<ul>\n<li>Secure routes can use different types of TLS termination<\/li>\n<li>Edge: certificates are terminated at the route, so TLS certificates must be configured in the route<\/li>\n<li>Pass-through: termination is happening at the Pods, which means that the Pods are responsible for serving the certificates. Use this to support mutual authentication<\/li>\n<li>Re-encryption: TLS traffic is terminated at the route, and new encrypted traffic is established with the Pods<\/li>\n<li>Unsecure routes require no key or certificates<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Creating Insecure Routes<\/span><\/p>\n<ul>\n<li>Easy: just use <code>oc expose service my.service --hostname my.name.example.com<\/code>\n<ul>\n<li>The service <code>my.service<\/code> is exposed<\/li>\n<li>The hostname <code>my.name.example.com<\/code> is set for the route<\/li>\n<\/ul>\n<\/li>\n<li>If no name is specified, the name <code>routename.projectname.defaultdomain<\/code> is used<\/li>\n<li>Notice that only the OpenShift route, and not the CoreDNS DNS server knows about route names\n<ul>\n<li>DNS has a wildcard domain name that sends traffic to the IP address that runs the router software, which will further take care of the specific name resolving<\/li>\n<li>Therefore, the route name must always be a subdomain of the cluster wildcard domain<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>Let&#8217;s create an insecure route:<\/p>\n<pre class=\"lang:default decode:true \">$ oc new-app --docker-image=bitnami\/nginx --name bitginx\r\n--&gt; Found Docker image b3834d0 (45 hours old) from Docker Hub for \"bitnami\/nginx\"\r\n\r\n    * An image stream tag will be created as \"bitginx:latest\" that will track this image\r\n    * This image will be deployed in deployment config \"bitginx\"\r\n    * Ports 8080\/tcp, 8443\/tcp will be load balanced by service \"bitginx\"\r\n      * Other containers can access this service through the hostname \"bitginx\"\r\n\r\n--&gt; Creating resources ...\r\n    imagestream.image.openshift.io \"bitginx\" created\r\n    deploymentconfig.apps.openshift.io \"bitginx\" created\r\n    service \"bitginx\" created\r\n--&gt; Success\r\n    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:\r\n     'oc expose svc\/bitginx'\r\n    Run 'oc status' to view your app.\r\n\r\n$ oc get all\r\nNAME                                READY     STATUS             RESTARTS   AGE\r\npod\/bitginx-1-jzk9r                 1\/1       Running            0          7s\r\npod\/docker-registry-1-ctgff         1\/1       Running            0          1d\r\npod\/lab4pod                         1\/1       Running            0          8h\r\npod\/nginx-cm                        1\/1       Running            0          9h\r\npod\/persistent-volume-setup-8f6lt   0\/1       Completed          0          1d\r\npod\/pv-pod                          1\/1       Running            0          1d\r\npod\/router-1-k8zgt                  1\/1       Running            0          1d\r\npod\/test1                           0\/1       CrashLoopBackOff   125        10h\r\n\r\nNAME                                      DESIRED   CURRENT   READY     AGE\r\nreplicationcontroller\/bitginx-1           1         1         1         10s\r\nreplicationcontroller\/docker-registry-1   1         1         1         1d\r\nreplicationcontroller\/router-1            1         1         1         1d\r\n\r\nNAME                      TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                   AGE\r\nservice\/bitginx           ClusterIP   172.30.122.7     &lt;none&gt;        8080\/TCP,8443\/TCP         12s\r\nservice\/docker-registry   ClusterIP   172.30.1.1       &lt;none&gt;        5000\/TCP                  1d\r\nservice\/kubernetes        ClusterIP   172.30.0.1       &lt;none&gt;        443\/TCP                   1d\r\nservice\/router            ClusterIP   172.30.110.170   &lt;none&gt;        80\/TCP,443\/TCP,1936\/TCP   1d\r\n\r\nNAME                                DESIRED   SUCCESSFUL   AGE\r\njob.batch\/persistent-volume-setup   1         1            1d\r\n\r\nNAME                                                 REVISION   DESIRED   CURRENT   TRIGGERED BY\r\ndeploymentconfig.apps.openshift.io\/bitginx           1          1         1         config,image(bitginx:latest)\r\ndeploymentconfig.apps.openshift.io\/docker-registry   1          1         1         config\r\ndeploymentconfig.apps.openshift.io\/router            1          1         1         config\r\n\r\nNAME                                     DOCKER REPO                       TAGS      UPDATED\r\nimagestream.image.openshift.io\/bitginx   172.30.1.1:5000\/default\/bitginx   latest    10 seconds ago\r\n\r\n$ oc expose service bitginx\r\nroute.route.openshift.io\/bitginx exposed\r\n\r\n$ oc get routes\r\nNAME      HOST\/PORT                          PATH      SERVICES   PORT       TERMINATION   WILDCARD\r\nbitginx   bitginx-default.127.0.0.1.nip.io             bitginx    8080-tcp                 None\r\n\r\n$ oc describe routes bitginx\r\nName:                   bitginx\r\nNamespace:              default\r\nCreated:                3 minutes ago\r\nLabels:                 app=bitginx\r\nAnnotations:            openshift.io\/host.generated=true\r\nRequested Host:         bitginx-default.127.0.0.1.nip.io\r\n                          exposed on router router 3 minutes ago\r\nPath:                   &lt;none&gt;\r\nTLS Termination:        &lt;none&gt;\r\nInsecure Policy:        &lt;none&gt;\r\nEndpoint Port:          8080-tcp\r\n\r\nService:        bitginx\r\nWeight:         100 (100%)\r\nEndpoints:      172.17.0.14:8080, 172.17.0.14:8443\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Why we need for Certificates ?<\/span><\/p>\n<ul>\n<li>PKI certificates are everywhere in OpenShift<\/li>\n<li>To secure resources &#8211; like routes &#8211; it&#8217;s essential to understand how certificates are working<\/li>\n<li>To use public keys, they need to be signed by a Certificate Authority<\/li>\n<li>Self-signed certificates are an easy way to get started with your own certificates<\/li>\n<li>Next, these certificates can be used in OpenShift resources like routes<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Creating self-signed certificates<\/span><\/p>\n<ul>\n<li>Creating the CA\n<ul>\n<li><code>mkdir ~\/openssl<\/code><\/li>\n<li><code>cd ~\/openssl<\/code><\/li>\n<li><code>openssl genrsa -des3 -out myCA.key 2048<\/code><\/li>\n<li>public key: <code>openssl req -x509 -new -nodes -key myCA.key -sha256 -days 3650 -out<\/code><code>myCA.pem<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Creating the certificate\n<ul>\n<li><code>openssl genrsa -out tls.key 2048<\/code><\/li>\n<li><code>openssl req -new -key tls.key -out tls.csr<\/code> # make sure the CN matches the DNS name of route which is project.apps-crc.testing<\/li>\n<\/ul>\n<\/li>\n<li>Self-signing the certificate\n<ul>\n<li><code>openssl x509 -req -in tls.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out tls.crt -days 1650 -sha256<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>To generate a certificate let&#8217;s use openssl utility. Creating the CA:<\/p>\n<pre class=\"lang:default decode:true\">$ mkdir ~\/openssl\r\n\r\n$ cd ~\/openssl\r\n\r\n$ openssl genrsa -des3 -out myCA.key 2048\r\nGenerating RSA private key, 2048 bit long modulus\r\n............................................................+++\r\n....................+++\r\ne is 65537 (0x10001)\r\nEnter pass phrase for myCA.key:\r\nVerifying - Enter pass phrase for myCA.key:\r\n[root@okd ~]#\r\n[root@okd ~]# openssl req -x509 -new -nodes -key myCA.key -sha256 -days 3650 -out myCA.pem\r\nEnter pass phrase for myCA.key:\r\nYou are about to be asked to enter information that will be incorporated\r\ninto your certificate request.\r\nWhat you are about to enter is what is called a Distinguished Name or a DN.\r\nThere are quite a few fields but you can leave some blank\r\nFor some fields there will be a default value,\r\nIf you enter '.', the field will be left blank.\r\n-----\r\nCountry Name (2 letter code) [XX]:PL\r\nState or Province Name (full name) []:\r\nLocality Name (eg, city) [Default City]:\r\nOrganization Name (eg, company) [Default Company Ltd]:\r\nOrganizational Unit Name (eg, section) []:\r\nCommon Name (eg, your name or your server's hostname) []:<strong>okd.netico.pl<\/strong>\r\nEmail Address []:\r\n\r\n$ ll\r\nrazem 8\r\n-rw-r--r-- 1 root root 1751 07-24 21:39 myCA.key\r\n-rw-r--r-- 1 root root 1285 07-24 21:45 myCA.pem\r\n<\/pre>\n<p>The most important is to speecify Common Name.<\/p>\n<p>Creating the certificate:<\/p>\n<pre class=\"lang:default decode:true\">$ openssl genrsa -out tls.key 2048\r\nGenerating RSA private key, 2048 bit long modulus\r\n..........+++\r\n....................................+++\r\ne is 65537 (0x10001)\r\n\r\n$ openssl req -new -key tls.key -out tls.csr\r\nYou are about to be asked to enter information that will be incorporated\r\ninto your certificate request.\r\nWhat you are about to enter is what is called a Distinguished Name or a DN.\r\nThere are quite a few fields but you can leave some blank\r\nFor some fields there will be a default value,\r\nIf you enter '.', the field will be left blank.\r\n-----\r\nCountry Name (2 letter code) [XX]:\r\nState or Province Name (full name) []:\r\nLocality Name (eg, city) [Default City]:\r\nOrganization Name (eg, company) [Default Company Ltd]:\r\nOrganizational Unit Name (eg, section) []:\r\nCommon Name (eg, your name or your server's hostname) []:<strong>okd2.netico.pl<\/strong>\r\nEmail Address []:\r\n\r\nPlease enter the following 'extra' attributes\r\nto be sent with your certificate request\r\nA challenge password []:\r\nAn optional company name []:\r\n<\/pre>\n<p>Self-signing the certificate:<\/p>\n<pre class=\"lang:default decode:true \">$ openssl x509 -req -in tls.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out tls.crt -days 1650 -sha256\r\nSignature ok\r\nsubject=\/C=XX\/L=Default City\/O=Default Company Ltd\/CN=okd2.netico.pl\r\nGetting CA Private Key\r\nEnter pass phrase for myCA.key: [the password which was use to create CA]\r\n<\/pre>\n<p>At this point the certificate has been created and is ready to use.<\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Edge Routes<\/span><\/p>\n<ul>\n<li>Edge routes hold TLS key material so that TLS termination can occur at the router<\/li>\n<li>Connections between router and application are not encrypted, so no TLS configuration is needed at the application<\/li>\n<li>Re-encryption routes offer a variation on edge termination\n<ul>\n<li>The router terminates TLS with a certificate, and re-encrypts its connection to the endpoint (typically with a different certificate)<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Configuring an Edge Route<\/span><\/p>\n<ul>\n<li>Creating deploy, svc, route\n<ul>\n<li><code>oc new-project myproject<\/code><\/li>\n<li><code>oc create cm linginx1 --from-file linginx1.conf<\/code><\/li>\n<li>as admin: <code>oc create sa linginx-sa<\/code> creates the dedicated service account<\/li>\n<li>As administrator: <code>oc adm policy add-scc-to-user anyuid -z linginx-sa<\/code><\/li>\n<li><code>oc create -f linginx-v1.yaml<\/code><\/li>\n<li><code>oc get pods <\/code><\/li>\n<li><code>oc get svc<\/code><\/li>\n<li><code>oc create route edge linginxl --service linginxl --cert=..\/openssl\/tls.crt --key=..\/openssl\/tls.key --ca-cert=..\/openssl\/myCA.pem --hostname=okd.netico.pl --port=80<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Testing from another pod in the cluster\n<ul>\n<li><code>curl -svv https:\/\/linginx-myproject.apps-crc.testing<\/code> # will show a self-signed certificate error<\/li>\n<li><code>curl -s -k https:\/\/linginx-myproject.apps-crc.testing<\/code> # will give access<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p>First thing we need is linginx1.conf file::<\/p>\n<pre class=\"lang:default decode:true\">server {\r\n    listen       8080 default server;\r\n    listen [::]:80 default_server ipv6only=on;\r\n\r\n    root   \/usr\/share\/nginx\/html;\r\n    index  index.html index.htm;\r\n    server_name localhost;\r\n}\r\n<\/pre>\n<p>Let&#8217;s put this config to the config map:<\/p>\n<pre class=\"lang:default decode:true\">$ oc create cm linginx1 --from-file linginx1.conf\r\nconfigmap\/linginx1 created\r\n\r\n$ oc describe  cm\r\nName:         linginx1\r\nNamespace:    default\r\nLabels:       &lt;none&gt;\r\nAnnotations:  &lt;none&gt;\r\n\r\nData\r\n====\r\nlinginx1.conf:\r\n----\r\nserver {\r\n          listen 8080 default_server;\r\n          listen [::]:80 default_server ipv6only=on;\r\n\r\n          root \/usr\/share\/nginx\/html;\r\n          index index.html;\r\n\r\n      server_name localhost;\r\n  }\r\n\r\nEvents:  &lt;none&gt;\r\n<\/pre>\n<p>We are going to use the <code>linginx-v1.yaml<\/code>\u00a0 file<\/p>\n<pre class=\"lang:default decode:true\">apiVersion: apps\/v1\r\nkind: Deployment\r\nmetadata:\r\n  name: linginx1\r\n  labels:\r\n    deployment: linginx1\r\nspec:\r\n  replicas: 1\r\n  selector:\r\n    matchLabels:\r\n      deployment: linginx1\r\n  template:\r\n    metadata:\r\n      labels:\r\n        deployment: linginx1\r\n    spec:\r\n      containers:\r\n      - image: docker.io\/nginx\r\n        name: linginx1\r\n        ports:\r\n        - containerPort: 8080\r\n          protocol: TCP\r\n        volumeMounts:\r\n        - mountPath: \"\/etc\/nginx\/conf.d\"\r\n          name: configmap-volume\r\n      volumes:\r\n      - name: configmap-volume\r\n        configMap:\r\n          name: linginx1\r\n          items:\r\n          - key: linginx1.conf\r\n            path: default.conf\r\n      serviceAccount: linginx-sa\r\n      serviceAccountName: linginx-sa\r\n---\r\napiVersion: v1\r\nkind: Service\r\nmetadata:\r\n  labels:\r\n    deployment: linginx1\r\n  name: linginx1\r\nspec:\r\n  ports:\r\n  - name: http\r\n    port: 8080\r\n    protocol: TCP\r\n    targetPort: 8080\r\n  selector:\r\n    deployment: linginx1\r\n<\/pre>\n<p>It is using\u00a0 docker.io\/nginx container and exposing container port 8080. It is using config map we have\u00a0 just created. It&#8217;s also using service account linginx-sa.<\/p>\n<p>Let&#8217;s create service accont::<\/p>\n<pre class=\"lang:default decode:true \">$ oc create sa linginx-sa\r\nserviceaccount\/linginx-sa created\r\n<\/pre>\n<p>In admin shell we must define a policy as an admin user:<\/p>\n<pre class=\"lang:default decode:true \">$ oc adm policy add-scc-to-user anyuid -z linginx-sa\r\nscc \"anyuid\" added to: [\"system:serviceaccount:default:linginx-sa\"]\r\n<\/pre>\n<p>Then as an oridinary user create resouces:<\/p>\n<pre class=\"lang:default decode:true \">$ oc create -f linginx-v1.yaml\r\ndeployment.apps\/linginx1 created\r\nservice\/linginx1 created\r\n<\/pre>\n<p>Wait couple of minutes to resorces be succesfully created.<\/p>\n<pre class=\"lang:default decode:true\">$ oc get pods\r\nNAME                            READY     STATUS             RESTARTS   AGE\r\nlinginx1-dc9f65f54-6zw8j        1\/1       Running            0          1m\r\n\r\n$ oc get svc\r\nNAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                   AGE\r\nlinginx1          ClusterIP   172.30.157.239   &lt;none&gt;        8080\/TCP                  1m\r\n<\/pre>\n<p>and now can create a route:<\/p>\n<pre class=\"lang:default decode:true\">$ oc create route edge linginxl --service linginxl --cert=..\/openssl\/tls.crt --key=..\/openssl\/tls.key --ca-cert=..\/openssl\/myCA.pem --hostname=okd.netico.pl --port=80\r\nroute.route.openshift.io\/linginxl created\r\n\r\n$ oc get routes\r\nNAME       HOST\/PORT                          PATH      SERVICES   PORT       TERMINATION   WILDCARD\r\nlinginxl   okd.netico.pl                                linginxl   80         edge          None\r\n<\/pre>\n<p>Now, we can test:<\/p>\n<pre class=\"lang:default decode:true\">$ curl -svv https:\/\/okd.netico.pl\r\n* About to connect() to okd.netico.pl port 443 (#0)\r\n*   Trying 172.30.9.22...\r\n* Connected to okd.netico.pl (172.30.9.22) port 443 (#0)\r\n* Initializing NSS with certpath: sql:\/etc\/pki\/nssdb\r\n*   CAfile: \/etc\/pki\/tls\/certs\/ca-bundle.crt\r\n  CApath: none\r\n* Server certificate:\r\n*       subject: CN=okd2.netico.pl,O=Default Company Ltd,L=Default City,C=XX\r\n*       start date: lip 24 19:57:16 2023 GMT\r\n*       expire date: sty 29 19:57:16 2028 GMT\r\n*       common name: okd2.netico.pl\r\n*       issuer: CN=okd.netico.pl,O=Default Company Ltd,L=Default City,C=PL\r\n* NSS error -8172 (SEC_ERROR_UNTRUSTED_ISSUER)\r\n* Peer's certificate issuer has been marked as not trusted by the user.\r\n* Closing connection 0\r\n\r\n\r\n$ curl -s -k https:\/\/okd.netico.pl\r\n&lt;html&gt;\r\n  &lt;head&gt;\r\n...\r\n  &lt;body&gt;\r\n    &lt;div&gt;\r\n      &lt;h1&gt;Application is not available&lt;\/h1&gt;\r\n      &lt;p&gt;The application is currently not serving requests at this endpoint. It may not have been started or is still starting.&lt;\/p&gt;\r\n\r\n      &lt;div class=\"alert alert-info\"&gt;\r\n        &lt;p class=\"info\"&gt;\r\n          Possible reasons you are seeing this page:\r\n        &lt;\/p&gt;\r\n        &lt;ul&gt;\r\n          &lt;li&gt;\r\n            &lt;strong&gt;The host doesn't exist.&lt;\/strong&gt;\r\n            Make sure the hostname was typed correctly and that a route matching this hostname exists.\r\n          &lt;\/li&gt;\r\n          &lt;li&gt;\r\n            &lt;strong&gt;The host exists, but doesn't have a matching path.&lt;\/strong&gt;\r\n            Check if the URL path was typed correctly and that the route was created using the desired path.\r\n          &lt;\/li&gt;\r\n          &lt;li&gt;\r\n            &lt;strong&gt;Route and path matches, but all pods are down.&lt;\/strong&gt;\r\n            Make sure that the resources exposed by this route (pods, services, deployment configs, etc) have at least one pod running.\r\n          &lt;\/li&gt;\r\n        &lt;\/ul&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Passthrough Routes<\/span><\/p>\n<ul>\n<li>A passthrough route configures the route to pass forward the certificate to guarantee client-route-application end-to-end encryption<\/li>\n<li>To make this happen, a secret providing the certificate as well as the certificate key is created and mounted in the application<\/li>\n<li>The passthrough route type doesn&#8217;t hold any key materials, but transparently presents the key materials that are available in the application \u2014 the router doesn&#8217;t provide TLS termination<\/li>\n<li>Passthrough is the only method that supports mutual authentication between application and client<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Configuring a Passthrough Route<\/span><\/p>\n<ul>\n<li>Part 1: Creating Certificates: ensure that subject name matches name used in the route\n<ul>\n<li><code>mkdir openssl; cd openssl<\/code><\/li>\n<li><code>openssl genrsa -des3 -out myCA.key 2048<\/code><\/li>\n<li><code>openssl req -x509 -new -nodes -key myCA.key -sha256 -days 3650 -out myCA.pem<\/code><\/li>\n<li><code>openssl genrsa -out tls.key 2048<\/code> # set common name to linginx-default.apps-crc.testing<\/li>\n<li><code>openssl req -new -key tls.key -out tls.csr <\/code><\/li>\n<li><code>openssl x509 -req -in tls.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out tls.crt -days 1650 -sha256<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Part 2: Creating a Secret\n<ul>\n<li><code>oc create secret tls linginx-certs --cert tls.crt --key tls.key<\/code><\/li>\n<li><code>oc get secret linginx-certs -o yaml<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Part 3: Create a ConfigMap\n<ul>\n<li><code>oc create cm nginxconfigmap --from-file default.conf<\/code><\/li>\n<li><code>oc create sa linginx-sa <\/code>creates the dedicated service account<\/li>\n<li><code>oc adm policy add-scc-to-user anyuid -z linginx-sa<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Part 4: Starting Deployment and Service\n<ul>\n<li><code>vim linginx-v2.yaml<\/code> #check volumes<\/li>\n<li><code>oc create -f linginx-v2.yaml<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Part 5: Creating the Passhthrough Route\n<ul>\n<li><code>oc create route passthrough linginx --service linginx2 --port 8443 --hostname=linginx-defaultapps-crc.testing<\/code><\/li>\n<li><code>oc get routes<\/code><\/li>\n<li><code>oc get svc<\/code><\/li>\n<\/ul>\n<\/li>\n<li>Part 6: Testing in a Debug Pod<\/li>\n<li><code>oc debug -t deployment\/linginx2 --image registry.access.redhat.com\/ubi8\/ubi:8.0<\/code>\n<ul>\n<li><code>curl -s -k https:\/\/172.25.201.41:8443<\/code> # only works from same network<\/li>\n<\/ul>\n<\/li>\n<li><code>curl https:\/\/Iinginx-default.apps-crc.testing<\/code><\/li>\n<li><code>curl --insecure https:\/\/Iinginx-default.apps-crc.testing<\/code><\/li>\n<\/ul>\n<p>&nbsp;<\/p>\n<p>For this excersise we need to go through entire procedure again so we need to delete old openssl directory.<\/p>\n<pre class=\"lang:default decode:true \">$ cd ~\r\n$ rm -rf openssl\r\n$ mkdir openssl\r\n$ cd openssl\r\n<\/pre>\n<p>Now, let&#8217;s do the certificate stuff:<\/p>\n<pre class=\"lang:default decode:true\">$ openssl genrsa -des3 -out myCA.key 2048\r\nGenerating RSA private key, 2048 bit long modulus\r\n...........+++\r\n..........................+++\r\ne is 65537 (0x10001)\r\nEnter pass phrase for myCA.key:\r\nVerifying - Enter pass phrase for myCA.key:\r\n\r\n$ openssl req -x509 -new -nodes -key myCA.key -sha256 -days 3650 -out myCA.pem\r\nEnter pass phrase for myCA.key:\r\nYou are about to be asked to enter information that will be incorporated\r\ninto your certificate request.\r\nWhat you are about to enter is what is called a Distinguished Name or a DN.\r\nThere are quite a few fields but you can leave some blank\r\nFor some fields there will be a default value,\r\nIf you enter '.', the field will be left blank.\r\n-----\r\nCountry Name (2 letter code) [XX]:PL\r\nState or Province Name (full name) []:silesia\r\nLocality Name (eg, city) [Default City]:\r\nOrganization Name (eg, company) [Default Company Ltd]:\r\nOrganizational Unit Name (eg, section) []:\r\nCommon Name (eg, your name or your server's hostname) []:okd.netico.pl\r\nEmail Address []:\r\n<\/pre>\n<p>Now we can generate the server keys<\/p>\n<pre class=\"lang:default decode:true\">$ openssl genrsa -out tls.key 2048\r\n\r\n$ openssl req -new -key tls.key -out tls.csr\r\nYou are about to be asked to enter information that will be incorporated\r\ninto your certificate request.\r\nWhat you are about to enter is what is called a Distinguished Name or a DN.\r\nThere are quite a few fields but you can leave some blank\r\nFor some fields there will be a default value,\r\nIf you enter '.', the field will be left blank.\r\n-----\r\nCountry Name (2 letter code) [XX]:PL\r\nState or Province Name (full name) []:\r\nLocality Name (eg, city) [Default City]:\r\nOrganization Name (eg, company) [Default Company Ltd]:\r\nOrganizational Unit Name (eg, section) []:\r\nCommon Name (eg, your name or your server's hostname) []:okd.netico.pl\r\nEmail Address []:\r\n\r\nPlease enter the following 'extra' attributes\r\nto be sent with your certificate request\r\nA challenge password []:\r\nAn optional company name []:\r\n\r\n$ openssl x509 -req -in tls.csr -CA myCA.pem -CAkey myCA.key -CAcreateserial -out tls.crt -days 1650 -sha256\r\nSignature ok\r\nsubject=\/C=PL\/L=Default City\/O=Default Company Ltd\/CN=okd.netico.pl\r\nGetting CA Private Key\r\nEnter pass phrase for myCA.key:\r\n<\/pre>\n<p>Creating a Secret:<\/p>\n<pre class=\"lang:default decode:true \">$ oc create secret tls linginx-certs --cert tls.crt --key tls.key\r\nsecret\/linginx-certs created\r\n\r\n$ oc get secret linginx-certs -o yaml\r\napiVersion: v1\r\ndata:\r\n  tls.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSURRakNDQWlvQ0NRRDk4bFlaMTl2VkNUQU5CZ2txaGtpRzl3MEJBUXNGQURCc01Rc3dDUVlEVlFRR0V3SlEKVERFUU1BNEdBMVVFQ0F3SGMybHNaWE5wWVRFVk1CTUdBMVVFQnd3TVJHVm1ZWFZzZENCRGFYUjVNUnd3R2dZRApWUVFLREJORVpXWmhkV3gwSUVOdmJYQmhibmtnVEhSa01SWXdGQVlEVlFRRERBMXZhMlF1Ym1WMGFXTnZMbkJzCk1CNFhEVEl6TURjeU5qRTFOREl4TTFvWERUSTRNREV6TVRFMU5ESXhNMW93V2pFTE1Ba0dBMVVFQmhNQ1VFd3gKRlRBVEJnTlZCQWNNREVSbFptRjFiSFFnUTJsMGVURWNNQm9HQTFVRUNnd1RSR1ZtWVhWc2RDQkRiMjF3WVc1NQpJRXgwWkRFV01CUUdBMVVFQXd3TmIydGtMbTVsZEdsamJ5NXdiRENDQVNJd0RRWUpLb1pJaHZjTkFRRUJCUUFECmdnRVBBRENDQVFvQ2dnRUJBTXN0dXU1NmJHcmZ0MERYTWdrUjIrQ215MTBKNE1tV0xpWHRYVURTWXk2ZjhOQXAKVG9sZGs2NGMra0t0K0RwZXRvUFd4bldyRnU3VittcHZyVCt1eFcwSkM2U1IvRlRMRTh4bVBRNFNiRU5pbEFpTgpUeEpGaUozcVBQV2VjOTZjVlgxd1puM0Rld2xLN245NkY0V1YyWFNFSTFJOHd1Nm1YZG9GM2QwTExDWHJLWE9oCkdZOXlqdkdsa2Jjb2VpdFBJWE9BV2tFZzRJdUJ6WnF1azVCZ243cWZ0dUFGYkFlby9yb20xNGZ4ckRRNWc0UHYKRWQ2UjJ4ZmhkSnEzc3gwdVlSd0NNQ0JSYmJvYlF5K1NrclVpQ29Qemxqd2w0aU50UU4xcFNUL3VWVzZqdUlFSQpOMUZyaGQxY3JyYTliVFJETEJGMjhhQVZKY2RveGdhbnBqTEFsRzhDQXdFQUFUQU5CZ2txaGtpRzl3MEJBUXNGCkFBT0NBUUVBVFN2RkZzdGdlNkl2WXpVMHZYWkJVVzdTdmxzKzZtRTVKQ3VCWTMxNXRrNXVQUWNobUxWaW5SUFAKN3RzcmhkSHNybGNLQ2srcVp2ZXdXWGQxbDJDa21tdzlKeCtkTHBzWTRxL1pMZi91bnJIdEpJeElyL3FLbnJrVgp6cG53Ti90YjVLR0NVc3RaQmU2Q1AxQ1FySmpZNUZwWUJQbGw5cVlQYlEzWEVNd3crKzRWUEJJR2tLL1NDMDVwCmpLYWNnak9WS2MrVXB1anowQ3NuL25uZ0V1RjlZRW93VDJndSs0Q1lkYUJhVjcvMDJQNFpjUmZTVHFHODV0SmYKRi84b0NTQjZFOHV3QnJDWHZhdUtuR1JFeVpvSVgrVllQTDNiV2RVTitIZHpoNkRPTnc1bUV1bE1ibUxPdU44WgpFS3grdUxDMjZjZDdNdU8xRVJjRXB3Y1ljWFNkSXc9PQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==\r\n  tls.key: LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlFcEFJQkFBS0NBUUVBeXkyNjducHNhdCszUU5jeUNSSGI0S2JMWFFuZ3laWXVKZTFkUU5KakxwL3cwQ2xPCmlWMlRyaHo2UXEzNE9sNjJnOWJHZGFzVzd0WDZhbSt0UDY3RmJRa0xwSkg4Vk1zVHpHWTlEaEpzUTJLVUNJMVAKRWtXSW5lbzg5WjV6M3B4VmZYQm1mY043Q1VydWYzb1hoWlhaZElRalVqekM3cVpkMmdYZDNRc3NKZXNwYzZFWgpqM0tPOGFXUnR5aDZLMDhoYzRCYVFTRGdpNEhObXE2VGtHQ2Z1cCsyNEFWc0I2ait1aWJYaC9Hc05EbURnKzhSCjNwSGJGK0YwbXJlekhTNWhIQUl3SUZGdHVodERMNUtTdFNJS2cvT1dQQ1hpSTIxQTNXbEpQKzVWYnFPNGdRZzMKVVd1RjNWeXV0cjF0TkVNc0VYYnhvQlVseDJqR0JxZW1Nc0NVYndJREFRQUJBb0lCQUNCYzlXU2RIWDNjaXEwSwpXZzcxeUVjOWFqRTBySmlQa21RNkxkdHdaNW42b2ZvV1NrczVHNWZsUjd1dFNGZkwxRmlsc2xEMTRwNUNlVFBRCi9CQ2p2eERDR3hlb3BUL0FaVFB1cVJUL3ZEenppODdjNjFabXV2OGtXM2RvT042aG1rQnowZStBWHEyNVFNb1AKWVlYR3U2K0NpTG5Gc2VzZmx0MXVoOHQ0eHh4MjBxL21qZ2ZrOURrTDVaZERzaEl4WDU3NEMrWEkwWVF2d2pMUAo3ZlZJbEZaUUxNOEJnODFSR0pzTVgyRDJKb01WRzFPQU9wWlFxNUhvUXJwTW02UEdaYXE2NnpxbjNwU216bEtVCmVrTGV5aVVMdkY1d0xBT0JkMGpsTFFUVDB3SFAyeEFqSjd0MERhNDNQS2Q1TkxGYWFvUS9RNnpJdFdNL3lhRWsKeDFaazhEa0NnWUVBLzVROHZNWCtnV1ZCVmJKQnFzUy9wWXJRVVdwMjdxY3FZcFVhZ2poM29KaGljVU1mcTB5TwpURkt0bm83Y0Q4V05SK1R6d2dFK3Q3QVJMNXlvbDdZRGZyV1hoVHBWZDYvbDRkeDN2UWFNMDlhWFhHcmp0VG5CClJIRVdCZThSb1FjdEFZZjNZMXQ3b0ZoLzNmVnpxZVdTNUZ3T0RsaEQ1Mktjb0RQWjBka2Y1Y1VDZ1lFQXk0Tm0KRksxVFNXajZEcExETit3VVlZbXhLbmJibVBJRTBGQmxiZ2xsd2E1QmFkUmNqSXlWK21COElWK09DTEtRSG5qbwpZTExrbHV2VHlRbU5ZUy93bXh0N3RseDZubWVvWlVtM25lZzhkb1FFUmxIeHMvaWVqV1dSN0NLUTFWTFJnT1lnCjJFK1ptR2tuTXJ1elFqUE82YjQrVjNKaGIrNmRZbkp4QldTSXFLTUNnWUVBd2xoTHMxUnZ5cDlmaGpYTm4zaUwKTHV1V3EwSmsrK2NiNE9qMnhtei84cHZOeDhpK0RUbGl2NERqU3ozZzh4Rks5SStTR0VWd0ZxZ0krWWFMNFFsawpNUGNQS0IwS25yK2Y5QmI4NmoxUDIwcER3Ti83RlhTOGxUblZBR0Fockt4VE9lWFZaYlZNRmNzV09JY01FL1poCnM4cVlXYW9ld0pXSStuMVROaktBQWUwQ2dZQlEzWmhkVlBYSU1LOVR4UnRQQ0Q2Yzl6SnZsaVR4NUJEbm1WcUUKVzdXVVBTSis0OFFXa1BJek44MTdFVllGdkxZcGRZK1loTnp4M3lrYk0vRjZrYXNBWnU1RWF3REtHcFErRXdtago5Qmk2V3dDNzFHbS9RbVgxOTBzQlVrYk1qUWowTi8wTEZxNEljcGdCdjdXZDg2b2ZGTm4rczFObVA2Rkg4Z05ZCnlqYkhFd0tCZ1FDSEhNZ0xUZExDTjFDNjlqQ0N2MjE5T1dvdHNWL2gvbDBJbFZPT3ZHSWxhWUFSNExjQjYrRjgKSTlPam1wNFBTd1pLMVhsOE9YRWZzeWpub1lvd1A3MGJadkpCYUlzLzFxMlF3T0QyaXFheEhhQVNiMnFCMHdnZgpBZXJ2NFZhZzR6YUNwVVgrY0ZGcTczdExkNEFwVkNKeFpaenp2bTAxT2VPby9KWkhIQVNXckE9PQotLS0tLUVORCBSU0EgUFJJVkFURSBLRVktLS0tLQo=\r\nkind: Secret\r\nmetadata:\r\n  creationTimestamp: 2023-07-26T15:45:17Z\r\n  name: linginx-certs\r\n  namespace: default\r\n  resourceVersion: \"1350402\"\r\n  selfLink: \/api\/v1\/namespaces\/default\/secrets\/linginx-certs\r\n  uid: 6b86b042-2bcb-11ee-8f96-8e5760356a66\r\ntype: kubernetes.io\/tls\r\n<\/pre>\n<p>We also need to create a config map:<\/p>\n<pre class=\"lang:default decode:true\">$ cd ..\/ex280\r\n$ vi default.conf\r\n$ cat default.conf\r\nserver {\r\n          listen 8080 default_server;\r\n          listen [::]:80 default_server ipv6only=on;\r\n          listen 8443 ssl;\r\n\r\n          root \/usr\/share\/nginx\/html;\r\n          index index.html;\r\n\r\n      server_name localhost;\r\n      ssl_certificate \/etc\/nginx\/ssl\/tls.crt;\r\n      ssl_certificate_key \/etc\/nginx\/ssl\/tls.key;\r\n      ssl_session_timeout 1d;\r\n      ssl_session_cache shared:SSL:50m;\r\n      ssl_session_tickets off;\r\n      # modern configuration. tweak to your needs.\r\n      ssl_protocols TLSv1.2;\r\n      ssl_ciphers 'ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256';\r\n      ssl_prefer_server_ciphers on;\r\n      # HSTS (ngx_http_headers_module is required) (15768000 seconds = 6 months)\r\n      add_header Strict-Transport-Security max-age=15768000;\r\n      # OCSP Stapling ---\r\n      # fetch OCSP records from URL in ssl_certificate and cache them\r\n      ssl_stapling on;\r\n      ssl_stapling_verify on;\r\n      location \/ {\r\n              try_files $uri $uri\/ =404;\r\n      }\r\n  }\r\n\r\n$ oc create cm nginxconfigmap --from-file default.conf\r\nconfigmap\/nginxconfigmap created\r\n\r\n$ oc create sa linginx-sa\r\nError from server (AlreadyExists): serviceaccounts \"linginx-sa\" already exists\r\n\r\n$ oc adm policy add-scc-to-user anyuid -z linginx-sa\r\nscc \"anyuid\" added to: [\"system:serviceaccount:default:linginx-sa\"]<\/pre>\n<p>Starting Deployment and Service:<\/p>\n<pre class=\"lang:default decode:true\">$ cat linginx-v2.yaml\r\n\r\napiVersion: apps\/v1\r\nkind: Deployment\r\nmetadata:\r\n  name: linginx2\r\n  labels:\r\n    deployment: linginx2\r\nspec:\r\n  replicas: 1\r\n  selector:\r\n    matchLabels:\r\n      deployment: linginx2\r\n  template:\r\n    metadata:\r\n      labels:\r\n        deployment: linginx2\r\n    spec:\r\n      containers:\r\n      - image: docker.io\/nginx\r\n        name: linginx2\r\n        ports:\r\n        - containerPort: 8080\r\n          protocol: TCP\r\n        - containerPort: 8443\r\n          protocol: TCP\r\n        volumeMounts:\r\n        - mountPath: \"\/etc\/nginx\/ssl\"\r\n          name: tls-certs\r\n        - mountPath: \"\/etc\/nginx\/conf.d\"\r\n          name: configmap-volume\r\n      volumes:\r\n      - name: tls-certs\r\n        secret:\r\n          secretName: linginx-certs\r\n      - name: configmap-volume\r\n        configMap:\r\n          name: nginxconfigmap\r\n      serviceAccount: linginx-sa\r\n      serviceAccountName: linginx-sa\r\n\r\n---\r\napiVersion: v1\r\nkind: Service\r\nmetadata:\r\n  labels:\r\n    deployment: linginx2\r\n  name: linginx2\r\nspec:\r\n  ports:\r\n  - name: http\r\n    port: 8080\r\n    protocol: TCP\r\n    targetPort: 8080\r\n  - name: https\r\n    port: 8443\r\n    protocol: TCP\r\n    targetPort: 8443\r\n  selector:\r\n    deployment: linginx2\r\n\r\n\r\n$ oc create -f linginx-v2.yaml\r\ndeployment.apps\/linginx2 created\r\nservice\/linginx2 created\r\n\r\n$ oc get pods\r\nNAME                            READY     STATUS             RESTARTS   AGE\r\nlinginx1-dc9f65f54-6zw8j        1\/1       Running            0          2h\r\nlinginx2-69bf6fc66b-mv6wx       1\/1       Running            0          21s\r\n<\/pre>\n<p>Create a route:<\/p>\n<pre class=\"lang:default decode:true\">$ oc create route passthrough linginx --service linginx2 --port 8443 --hostname=okd.netico.pl\r\nroute.route.openshift.io\/linginx created\r\n\r\n$ oc get routes\r\nNAME       HOST\/PORT                          PATH      SERVICES   PORT       TERMINATION   WILDCARD\r\nbitginx    bitginx-default.127.0.0.1.nip.io             bitginx    8080-tcp                 None\r\nlinginx    HostAlreadyClaimed                           linginx2   8443       passthrough   None\r\nlinginxl   okd.netico.pl                                linginxl   80         edge          None\r\n\r\n$ oc delete route linginxl\r\nroute.route.openshift.io \"linginxl\" deleted\r\n\r\n$ oc get routes\r\nNAME      HOST\/PORT                          PATH      SERVICES   PORT       TERMINATION   WILDCARD\r\nbitginx   bitginx-default.127.0.0.1.nip.io             bitginx    8080-tcp                 None\r\nlinginx   okd.netico.pl                                linginx2   8443       passthrough   None\r\n\r\n$ oc get svc\r\nNAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                   AGE\r\nlinginx1          ClusterIP   172.30.157.239   &lt;none&gt;        8080\/TCP                  3h\r\nlinginx2          ClusterIP   172.30.240.91    &lt;none&gt;        8080\/TCP,8443\/TCP         5m\r\n<\/pre>\n<p>Testing in a Debug Pod<\/p>\n<pre class=\"lang:default decode:true\">$ oc debug -t deployment\/linginx2 --image registry.access.redhat.com\/ubi8\/ubi:8.0\r\n\r\nDefaulting container name to linginx2.\r\nUse 'oc describe pod\/linginx2-debug -n default' to see all of the containers in this pod.\r\n\r\nDebugging with pod\/linginx2-debug, original command: &lt;image entrypoint&gt;\r\nWaiting for pod to start ...\r\nIf you don't see a command prompt, try pressing enter.\r\n\r\n# curl -s -k https:\/\/172.30.240.91:8443\r\n&lt;!DOCTYPE html&gt;\r\n&lt;html&gt;\r\n&lt;head&gt;\r\n&lt;title&gt;Welcome to nginx!&lt;\/title&gt;\r\n&lt;style&gt;\r\nhtml { color-scheme: light dark; }\r\nbody { width: 35em; margin: 0 auto;\r\nfont-family: Tahoma, Verdana, Arial, sans-serif; }\r\n&lt;\/style&gt;\r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n&lt;h1&gt;Welcome to nginx!&lt;\/h1&gt;\r\n&lt;p&gt;If you see this page, the nginx web server is successfully installed and\r\nworking. Further configuration is required.&lt;\/p&gt;\r\n\r\n&lt;p&gt;For online documentation and support please refer to\r\n&lt;a href=\"http:\/\/nginx.org\/\"&gt;nginx.org&lt;\/a&gt;.&lt;br\/&gt;\r\nCommercial support is available at\r\n&lt;a href=\"http:\/\/nginx.com\/\"&gt;nginx.com&lt;\/a&gt;.&lt;\/p&gt;\r\n\r\n&lt;p&gt;&lt;em&gt;Thank you for using nginx.&lt;\/em&gt;&lt;\/p&gt;\r\n&lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p><code>curl -s -k https:\/\/172.25.201.41:8443<\/code> # only works from same network<\/p>\n<p><code>curl https:\/\/okd.netico.pl<\/code><\/p>\n<p><code>curl --insecure https:\/\/Iinginx-default.apps-crc.testing<\/code><\/p>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Network Policies<\/span><\/p>\n<ul>\n<li>By default, there are no restrictions to network traffic in K8s<\/li>\n<li>Pods can always communicate, even if they&#8217;re in other namespaces<\/li>\n<li>To limit this, Network Policies can be used<\/li>\n<li>If in a policy there is no match, traffic will be denied<\/li>\n<li>If no Network Policy is used, all traffic is allowed<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Network Policy Identifiers<\/span><\/p>\n<ul>\n<li>In network policies, three different identifiers can be used\n<ul>\n<li>Pods: (podSelector) note that a Pod cannot block access to itself<\/li>\n<li>Namespaces: (namespaceSelector) to grant access to specific namespaces<\/li>\n<li>IP blocks: (ipBlock) notice that traffic to and from the node where a Pod is running is always allowed<\/li>\n<\/ul>\n<\/li>\n<li>When defining a Pod- or namespace-based network policy, a selector label is used to specify what traffic is allowed to and from the Pods that match the selector<\/li>\n<li>Network policies do not conflict, they are additive<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Allowing Ingress and Monitoring<\/span><\/p>\n<ul>\n<li>If cluster monitoring or exposed routes are used, Ingress from them needs to be included in the network policy<\/li>\n<li>Use <code>spec.ingress.fronnamespaceSelectonmatchlabels<\/code> to define:\n<ul>\n<li><code> network.openshift.io\/policy-group: monitoring <\/code><\/li>\n<li><code>network.openshift.io\/policy-group: ingress<\/code><\/li>\n<\/ul>\n<\/li>\n<\/ul>\n<p><span style=\"color: #3366ff;\">Configuring Network Policy<\/span><\/p>\n<ul>\n<li><code>oc login -u admin -p password <\/code><\/li>\n<li><code>oc apply -f nwpolicy-complete-example.yaml <\/code><\/li>\n<li><code>oc expose pod nginx --port=80 <\/code><\/li>\n<li><code>oc exec -it busybox -- wget --spider --timeout=1 nginx # will fail <\/code><\/li>\n<li><code>oc label pod busybox access=true <\/code><\/li>\n<li><code>oc exec -it busybox -- wget --spider --timeout=1 nginx # will work<\/code><\/li>\n<\/ul>\n<p>Excercise<\/p>\n<pre class=\"lang:default decode:true\">$ cat nwpolicy-complete-example.yaml\r\n\r\napiVersion: networking.k8s.io\/v1\r\nkind: NetworkPolicy\r\nmetadata:\r\n  name: access-nginx\r\nspec:\r\n  podSelector:\r\n    matchLabels:\r\n      app: nginx\r\n  ingress:\r\n  - from:\r\n    - podSelector:\r\n        matchLabels:\r\n          access: \"true\"\r\n...\r\n\r\n---\r\napiVersion: v1\r\nkind: Pod\r\nmetadata:\r\n  name: nginx\r\n  labels:\r\n    app: nginx\r\nspec:\r\n  containers:\r\n  - name: nwp-nginx\r\n    image: nginx:1.17\r\n...\r\n\r\n---\r\napiVersion: v1\r\nkind: Pod\r\nmetadata:\r\n  name: busybox\r\n  labels:\r\n    app: sleepy\r\nspec:\r\n  containers:\r\n  - name: nwp-busybox\r\n    image: busybox\r\n    command:\r\n    - sleep\r\n    - \"3600\"\r\n\r\n\r\n$ oc apply -f nwpolicy-complete-example.yaml\r\nnetworkpolicy.networking.k8s.io\/access-nginx created\r\npod\/nginx created\r\npod\/busybox created\r\n\r\n$ oc get pods\r\nNAME                            READY     STATUS             RESTARTS   AGE\r\nbusybox                         1\/1       Running            0          15s\r\nnginx                           1\/1       Running            0          15s\r\n<\/pre>\n<p>Now we need to expse the nginx pod:<\/p>\n<pre class=\"lang:default decode:true \">$ oc expose pod nginx --port=80\r\nservice\/nginx exposed\r\n<\/pre>\n<p>Let&#8217;s chceck if there is a service:<\/p>\n<pre class=\"lang:default decode:true\">$ oc get svc\r\nNAME              TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                   AGE\r\nnginx             ClusterIP   172.30.188.121   &lt;none&gt;        80\/TCP                    34s\r\n<\/pre>\n<p>Let&#8217;s check the network policy:<\/p>\n<pre class=\"lang:default decode:true \">$ oc exec -it busybox -- wget --spider --timeout=1 nginx\r\nConnecting to nginx (172.30.188.121:80)\r\nwget: download timed out\r\ncommand terminated with exit code 1\r\n<\/pre>\n<p>Download timed out because network policy didn&#8217;t find any matching rules.<\/p>\n<p>So, let&#8217;s create such a rule:<\/p>\n<pre class=\"lang:default decode:true \">$ oc label pod busybox access=true\r\npod\/busybox labeled\r\n\r\n$ oc exec -it busybox -- wget --spider --timeout=1 nginx\r\nConnecting to nginx (172.30.188.121:80)\r\nremote file exists<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Advanced Network Policies<\/span><\/p>\n<ul>\n<li><code>oc login -u kubeadmin -p ... <\/code><\/li>\n<li><code>oc new-project source-project <\/code><\/li>\n<li><code>oc label ns source-project type=incoming <\/code><\/li>\n<li><code>oc create -f nginx-source1.yml <\/code><\/li>\n<li><code>oc create -f nginx-source2.yml <\/code><\/li>\n<li><code>oc project target-project <\/code><\/li>\n<li><code>oc login -u developer -p developer <\/code><\/li>\n<li><code>oc new-project target-project <\/code><\/li>\n<li><code>oc new-app --name nginx-target --docker-image quay.io\/openshifttest\/hello-openshift:openshift <\/code><\/li>\n<li><code>oc get pods -o wide\u00a0 <\/code><\/li>\n<li><code>oc login -u kubeadmin -p ... <\/code><\/li>\n<li><code>oc exec -it nginx-access -n source-project -- curl &lt;ip-of-nginx-target-pod&gt;:8080 <\/code><\/li>\n<li><code>oc exec -it nginx-noaccess -n source-project -- curl &lt;ip-of-nginx-target-pod&gt;:8080 <\/code><\/li>\n<li><code>oc create -f nwpol-allow-specific.yaml <\/code><\/li>\n<li><code>oc exec -it nginx-noaccess -n source-project -- curl &lt;ip-of-nginx-target-pod&gt;:8080 <\/code><\/li>\n<li><code>oc label pod nginx-target-1-&lt;xxxxx&gt; type=incoming <\/code><\/li>\n<li><code>oc exec -it nginx-noaccess -n source-project -- curl &lt;ip-of-nginx-target-pod&gt;:8080<\/code><\/li>\n<\/ul>\n<pre class=\"lang:default decode:true\">$ oc new-project source-project\r\nNow using project \"source-project\" on server \"https:\/\/172.30.9.22:8443\".\r\n\r\nYou can add applications to this project with the 'new-app' command. For example, try:\r\n\r\n    oc new-app centos\/ruby-25-centos7~https:\/\/github.com\/sclorg\/ruby-ex.git\r\n\r\nto build a new example application in Ruby.\r\n\r\n$ oc label ns source-project type=incoming\r\nnamespace\/source-project labeled\r\n\r\n$ cat nginx-source1.yml\r\napiVersion: v1\r\nkind: Pod\r\nmetadata:\r\n  name: nginx-access\r\n  labels:\r\n    type: access\r\n  namespace: source-project\r\nspec:\r\n  containers:\r\n  - name: nginx\r\n    image: nginx\r\n    ports:\r\n    - containerPort: 8080\r\n      protocol: TCP\r\n\r\n$ cat nginx-source2.yml\r\napiVersion: v1\r\nkind: Pod\r\nmetadata:\r\n  name: nginx-noaccess\r\n  labels:\r\n    type: noaccess\r\n  namespace: source-project\r\nspec:\r\n  containers:\r\n  - name: nginx\r\n    image: nginx\r\n    ports:\r\n    - containerPort: 8080\r\n      protocol: TCP\r\n\r\n$ oc create -f nginx-source1.yml\r\npod\/nginx-access created\r\n\r\n$ oc create -f nginx-source2.yml\r\npod\/nginx-noaccess created\r\n\r\n$ oc get all\r\nNAME                 READY     STATUS    RESTARTS   AGE\r\npod\/nginx-access     1\/1       Running   0          25s\r\npod\/nginx-noaccess   1\/1       Running   0          20s\r\n<\/pre>\n<p>Lets create a target project.<\/p>\n<pre class=\"lang:default decode:true \">$ oc new-project target-project\r\nNow using project \"target-project\" on server \"https:\/\/172.30.9.22:8443\".\r\n\r\nYou can add applications to this project with the 'new-app' command. For example, try:\r\n\r\n    oc new-app centos\/ruby-25-centos7~https:\/\/github.com\/sclorg\/ruby-ex.git\r\n\r\nto build a new example application in Ruby.\r\n\r\n\r\n$ oc new-app --name nginx-target --docker-image quay.io\/openshifttest\/hello-openshift:openshift\r\n--&gt; Found Docker image 7af3297 (5 years old) from quay.io for \"quay.io\/openshifttest\/hello-openshift:openshift\"\r\n\r\n    * An image stream tag will be created as \"nginx-target:openshift\" that will track this image\r\n    * This image will be deployed in deployment config \"nginx-target\"\r\n    * Ports 8080\/tcp, 8888\/tcp will be load balanced by service \"nginx-target\"\r\n      * Other containers can access this service through the hostname \"nginx-target\"\r\n\r\n--&gt; Creating resources ...\r\n    imagestream.image.openshift.io \"nginx-target\" created\r\n    deploymentconfig.apps.openshift.io \"nginx-target\" created\r\n    service \"nginx-target\" created\r\n--&gt; Success\r\n    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:\r\n     'oc expose svc\/nginx-target'\r\n    Run 'oc status' to view your app.\r\n\r\n$ oc get pods -o wide\r\nNAME                   READY     STATUS    RESTARTS   AGE       IP            NODE        NOMINATED NODE\r\nnginx-target-1-9kdn6   1\/1       Running   0          49s       172.17.0.21   localhost   &lt;none&gt;\r\n<\/pre>\n<p>Now, back to the admin shell:<\/p>\n<pre class=\"lang:default decode:true\">$ oc exec -it nginx-access -n source-project -- curl 172.17.0.21:8080\r\nHello OpenShift!\r\n\r\n$ oc exec -it nginx-noaccess -n source-project -- curl 172.17.0.21:8080\r\nHello OpenShift!\r\n<\/pre>\n<p>We have no network policy yet so there no traffic restrictions even it is traffic between diffrent namespaces.<\/p>\n<p>Let&#8217;s look at this network policy:<\/p>\n<pre class=\"lang:default decode:true\">$ cat nwpol-allow-specific.yaml\r\nkind: NetworkPolicy\r\napiVersion: networking.k8s.io\/v1\r\nmetadata:\r\n  name: allow-some\r\nspec:\r\n  podSelector:\r\n    matchLabels:\r\n      type: incoming\r\n  ingress:\r\n    - from:\r\n      - namespaceSelector:\r\n          matchLabels:\r\n            type: incoming\r\n        podSelector:\r\n          matchLabels:\r\n            type: access\r\n      ports:\r\n      - port: 8080\r\n        protocol: TCP\r\n\r\n$  oc get pods -n source-project --show-labels\r\nNAME READY STATUS RESTARTS AGE LABELS\r\nnginx-access 1\/1 Running 0 21m type=access\r\nnginx-noaccess 1\/1 Running 0 21m type=noaccess\r\n<\/pre>\n<p>Let&#8217;s create this network policy:<\/p>\n<pre class=\"lang:default decode:true \">$  oc create -f nwpol-allow-specific.yaml\r\nnetworkpolicy.networking.k8s.io\/allow-some created\r\n<\/pre>\n<p>And now we still can reach to the &#8220;Hello Openshift&#8221;<\/p>\n<pre class=\"lang:default decode:true \">$  oc exec -it nginx-noaccess -n source-project -- curl 172.17.0.21:8080\r\nHello OpenShift!\r\n<\/pre>\n<p>That is because the label is not set on the nginx noaccces.<\/p>\n<pre class=\"lang:default decode:true \">$ oc get pods\r\nNAME                   READY     ST\r\nATUS    RESTARTS   AGE\r\nnginx-target-1-9kdn6   1\/1       Running   0          17m\r\n\r\n$ oc get pods --show-labels\r\nNAME                   READY     STATUS    RESTARTS   AGE       LABELS\r\nnginx-target-1-9kdn6   1\/1       Running   0          18m       app=nginx-target,deployment=nginx-target-1,deploymentconfig=nginx-target\r\n\r\n$ oc label pod nginx-target-1-9kdn6 type=incoming\r\npod\/nginx-target-1-9kdn6 labeled\r\n\r\n$ oc exec -it nginx-noaccess -n source-project -- curl 172.17.0.21:8080\r\nPROBLEM\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p><span style=\"color: #3366ff;\">Lab: Creating an Edge Router<\/span><br \/>\nRun an Nginx deployment, and ensure this deployment is accessible by addressing an Edge router<\/p>\n<pre class=\"lang:default decode:true\">$ oc login -u developer -p developer\r\nLogin successful.\r\n\r\nYou have access to the following projects and can switch between them with 'oc project &lt;projectname&gt;':\r\n\r\n  * debug\r\n    myproject\r\n\r\nUsing project \"debug\".\r\n[root@okd ~]# oc new-project network-security\r\nNow using project \"network-security\" on server \"https:\/\/172.30.9.22:8443\".\r\n\r\nYou can add applications to this project with the 'new-app' command. For example, try:\r\n\r\n    oc new-app centos\/ruby-25-centos7~https:\/\/github.com\/sclorg\/ruby-ex.git\r\n\r\nto build a new example application in Ruby.\r\n\r\n$ oc new-app --name nginxlab --docker-image=bitnami\/ngninx\r\nerror: unable to locate any local docker images with name \"bitnami\/ngninx\"\r\n\r\nThe 'oc new-app' command will match arguments to the following types:\r\n\r\n  1. Images tagged into image streams in the current project or the 'openshift' project\r\n     - if you don't specify a tag, we'll add ':latest'\r\n  2. Images in the Docker Hub, on remote registries, or on the local Docker engine\r\n  3. Templates in the current project or the 'openshift' project\r\n  4. Git repository URLs or local paths that point to Git repositories\r\n\r\n--allow-missing-images can be used to point to an image that does not exist yet.\r\n\r\nSee 'oc new-app -h' for examples.\r\n[root@okd ~]# oc new-app --name nginxlab --docker-image=bitnami\/nginx\r\n--&gt; Found Docker image 1005528 (35 hours old) from Docker Hub for \"bitnami\/nginx\"\r\n\r\n    * An image stream tag will be created as \"nginxlab:latest\" that will track this image\r\n    * This image will be deployed in deployment config \"nginxlab\"\r\n    * Ports 8080\/tcp, 8443\/tcp will be load balanced by service \"nginxlab\"\r\n      * Other containers can access this service through the hostname \"nginxlab\"\r\n\r\n--&gt; Creating resources ...\r\n    imagestream.image.openshift.io \"nginxlab\" created\r\n    deploymentconfig.apps.openshift.io \"nginxlab\" created\r\n    service \"nginxlab\" created\r\n--&gt; Success\r\n    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:\r\n     'oc expose svc\/nginxlab'\r\n    Run 'oc status' to view your app.\r\n\r\n$ oc get pods\r\nNAME                READY     STATUS              RESTARTS   AGE\r\nnginxlab-1-bcgkt    0\/1       ContainerCreating   0          11s\r\nnginxlab-1-deploy   1\/1       Running             0          13s\r\n\r\n$ oc get svc\r\nNAME       TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE\r\nnginxlab   ClusterIP   172.30.146.79   &lt;none&gt;        8080\/TCP,8443\/TCP   38s\r\n\r\n$ oc create route edge ngnixlab --service ngninxlab --cert=openssl\/tls.crt --key=openssl\/tls.key --ca-cert=openssl\/myCA.crt\r\nerror: you need to provide a route port via --port when exposing a non-existent service\r\n[root@okd ~]# oc create route edge ngnixlab --service ngninxlab --cert=openssl\/tls.crt --key=openssl\/tls.key --ca-cert=openssl\/myCA.crt --port=8080\r\nerror: open openssl\/myCA.crt: no such file or directory\r\n[root@okd ~]# oc create route edge ngnixlab --service ngninxlab --cert=openssl\/tls.crt --key=openssl\/tls.key --ca-cert=openssl\/myCA.pem --port=8080\r\nroute.route.openshift.io\/ngnixlab created\r\n\r\n$ oc get routes\r\nNAME       HOST\/PORT                                    PATH      SERVICES    PORT      TERMINATION   WILDCARD\r\nngnixlab   ngnixlab-network-security.127.0.0.1.nip.io             ngninxlab   8080      edge          None\r\n\r\n$ curl -svv https:\/\/ngnixlab-network-security.127.0.0.1.nip.io\r\n* About to connect() to ngnixlab-network-security.127.0.0.1.nip.io port 443 (#0)\r\n*   Trying 127.0.0.1...\r\n* Connected to ngnixlab-network-security.127.0.0.1.nip.io (127.0.0.1) port 443 (#0)\r\n* Initializing NSS with certpath: sql:\/etc\/pki\/nssdb\r\n*   CAfile: \/etc\/pki\/tls\/certs\/ca-bundle.crt\r\n  CApath: none\r\n* Server certificate:\r\n*       subject: CN=okd.netico.pl,O=Default Company Ltd,L=Default City,C=PL\r\n*       start date: lip 26 15:42:13 2023 GMT\r\n*       expire date: sty 31 15:42:13 2028 GMT\r\n*       common name: okd.netico.pl\r\n*       issuer: CN=okd.netico.pl,O=Default Company Ltd,L=Default City,ST=silesia,C=PL\r\n* NSS error -8172 (SEC_ERROR_UNTRUSTED_ISSUER)\r\n* Peer's certificate issuer has been marked as not trusted by the user.\r\n* Closing connection 0\r\n\r\n$ curl -s -k https:\/\/ngnixlab-network-security.127.0.0.1.nip.io\r\n&lt;html&gt;\r\n  &lt;head&gt;\r\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"&gt;\r\n...\r\n  &lt;\/head&gt;\r\n  &lt;body&gt;\r\n    &lt;div&gt;\r\n      &lt;h1&gt;Application is not available&lt;\/h1&gt;\r\n      &lt;p&gt;The application is currently not serving requests at this endpoint. It may not have been started or is still starting.&lt;\/p&gt;\r\n\r\n      &lt;div class=\"alert alert-info\"&gt;\r\n        &lt;p class=\"info\"&gt;\r\n          Possible reasons you are seeing this page:\r\n        &lt;\/p&gt;\r\n        &lt;ul&gt;\r\n          &lt;li&gt;\r\n            &lt;strong&gt;The host doesn't exist.&lt;\/strong&gt;\r\n            Make sure the hostname was typed correctly and that a route matching this hostname exists.\r\n          &lt;\/li&gt;\r\n          &lt;li&gt;\r\n            &lt;strong&gt;The host exists, but doesn't have a matching path.&lt;\/strong&gt;\r\n            Check if the URL path was typed correctly and that the route was created using the desired path.\r\n          &lt;\/li&gt;\r\n          &lt;li&gt;\r\n            &lt;strong&gt;Route and path matches, but all pods are down.&lt;\/strong&gt;\r\n            Make sure that the resources exposed by this route (pods, services, deployment configs, etc) have at least one pod running.\r\n          &lt;\/li&gt;\r\n        &lt;\/ul&gt;\r\n      &lt;\/div&gt;\r\n    &lt;\/div&gt;\r\n  &lt;\/body&gt;\r\n&lt;\/html&gt;\r\n<\/pre>\n<p>That conludes the lab.<\/p>\n","protected":false},"excerpt":{"rendered":"","protected":false},"author":1,"featured_media":4918,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[93,49],"tags":[],"_links":{"self":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4916"}],"collection":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/comments?post=4916"}],"version-history":[{"count":53,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4916\/revisions"}],"predecessor-version":[{"id":5113,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/posts\/4916\/revisions\/5113"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media\/4918"}],"wp:attachment":[{"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/media?parent=4916"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/categories?post=4916"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/miro.borodziuk.eu\/index.php\/wp-json\/wp\/v2\/tags?post=4916"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}