Error 400 when Accessing Openshift 4 Route

Just had this intermittent error when accessing my application which is being deployed to Openshift 4,

<html><body><h1>400 Bad request</h1>
Your browser sent an invalid request.
</body></html>

At first, we tought that issue happens at application level. But after further debugging, it is shown that there is no logs captured at all from the application’s perspective. After further debugging, we realized that issue happens on Openshift’s Router level, where logs can be seen below.

2023-09-30T18:00:50.097282+00:00 infra-0 infra-0.ocp.local haproxy[46]: 127.0.0.1:41722 [30/Sep/2023:18:00:50.096] public openshift_default/<NOSRV> 0/-1/-1/-1/0 503 157 - - SC-- 1/1/0/0/0 0/0 "HEAD / HTTP/1.1"
2023-09-30T18:00:52.385991+00:00 infra-0 infra-0.ocp.local haproxy[46]: 10.20.24.80:36248 [30/Sep/2023:18:00:52.375] fe_no_sni~ fe_no_sni/<NOSRV> -1/-1/-1/-1/10 400 211 - - PR-- 2/1/0/0/0 0/0 "<BADREQ>"
2023-09-30T18:00:52.387088+00:00 infra-0 infra-0.ocp.local haproxy[46]: 10.20.24.80:36248 [30/Sep/2023:18:00:52.375] public_ssl be_no_sni/fe_no_sni 1/0/11 2440 SD 1/1/0/0/0 0/0
2023-09-30T18:00:53.915337+00:00 infra-0 infra-0.ocp.local haproxy[46]: 10.20.24.80:39454 [30/Sep/2023:18:00:53.914] public public/<NOSRV> -1/-1/-1/-1/0 400 211 - - PR-- 1/1/0/0/0 0/0 "<BADREQ>"
2023-09-30T18:00:56.155389+00:00 infra-0 infra-0.ocp.local haproxy[46]: 10.20.24.80:36306 [30/Sep/2023:18:00:56.144] public_ssl be_tcp:openshift-authentication:oauth-openshift/pod:oauth-openshift-69bc64d75b-r5z8t:oauth-openshift:https:10.130.1.123:6443 1/1/10 3687 -- 1/1/0/0/0 0/0
2023-09-30T18:00:57.368604+00:00 infra-0 infra-0.ocp.local haproxy[46]: 10.20.24.80:36322 [30/Sep/2023:18:00:57.366] fe_no_sni~ fe_no_sni/<NOSRV> -1/-1/-1/-1/2 400 211 - - PR-- 2/1/0/0/0 0/0 "<BADREQ>"
2023-09-30T18:00:57.369754+00:00 infra-0 infra-0.ocp.local haproxy[46]: 10.20.24.80:36322 [30/Sep/2023:18:00:57.365] public_ssl be_no_sni/fe_no_sni 1/0/3 404 SD 1/1/0/0/0 0/0
2023-09-30T18:00:58.847737+00:00 infra-0 infra-0.ocp.local haproxy[46]: 10.20.24.80:39522 [30/Sep/2023:18:00:58.847] public public/<NOSRV> -1/-1/-1/-1/0 400 211 - - PR-- 1/1/0/0/0 0/0 "<BADREQ>"

Where some requests were given error 400 BADREQ. And it seems that rootcause is haproxy blocking big http headers, we can see the sample below where i simulate a very big cookies when accessing my application thru Openshift Router.

$ curl -kv  https://my.apps.ocp.local --cookie "LELE=$(perl -e 'print "x"x25000')"
* Rebuilt URL to: https://my.apps.ocp.local/
*   Trying 10.20.20.135...
* TCP_NODELAY set
* Connected to my.apps.ocp.local (10.20.20.135) port 443 (#0)
* ALPN, offering h2
* ALPN, offering http/1.1
* successfully set certificate verify locations:
*   CAfile: /etc/pki/tls/certs/ca-bundle.crt
  CApath: none
* TLSv1.3 (OUT), TLS handshake, Client hello (1):
* TLSv1.3 (IN), TLS handshake, Server hello (2):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Encrypted Extensions (8):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Certificate (11):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, CERT verify (15):
* TLSv1.3 (IN), TLS handshake, [no content] (0):
* TLSv1.3 (IN), TLS handshake, Finished (20):
* TLSv1.3 (OUT), TLS change cipher, Change cipher spec (1):
* TLSv1.3 (OUT), TLS handshake, [no content] (0):
* TLSv1.3 (OUT), TLS handshake, Finished (20):
* SSL connection using TLSv1.3 / TLS_AES_128_GCM_SHA256
* ALPN, server did not agree to a protocol
* Server certificate:
*  subject: C=ID; ST=Daerah Khusus Ibukota Jakarta; L=Jakarta Selatan; O=xxxx; CN=*.xxx
*  start date: Nov  8 00:00:00 2022 GMT
*  expire date: Dec  9 23:59:59 2023 GMT
*  issuer: C=US; O=DigiCert Inc; CN=DigiCert TLS RSA SHA256 2020 CA1
*  SSL certificate verify ok.
* TLSv1.3 (OUT), TLS app data, [no content] (0):
> GET / HTTP/1.1
> Host: my.apps.ocp.local
> User-Agent: curl/7.61.1
> Accept: */*
> Cookie: LELE=xxxxxx.....xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

TLSv1.3 (OUT), TLS app data, [no content] (0):
* TLSv1.3 (IN), TLS app data, [no content] (0):
< HTTP/1.1 400 Bad request
< content-length: 90
< cache-control: no-cache
< content-type: text/html
< connection: close
<
<html><body><h1>400 Bad request</h1>
Your browser sent an invalid request.
</body></html>
* Closing connection 0
* TLSv1.3 (OUT), TLS alert, [no content] (0):
* TLSv1.3 (OUT), TLS alert, close notify (256):

Workaround is pretty much simple, we can see it on below document

https://docs.openshift.com/container-platform/4.13/networking/ingress-operator.html#nw-ingress-controller-configuration-parameters_configuring-ingress

And that is to increase headerBufferBytes,

$ oc -n openshift-ingress-operator patch ingresscontroller/default \
	--type=merge -p '{"spec":{"tuningOptions": {"headerBufferBytes": 50000}}}'

XA-Datasource Configuration for MySql

This is the configuration that is needed when deploying MySql XA Datasource connection on JBoss EAP

<xa-datasource jndi-name="java:jboss/datasources/mysqlXADS" pool-name="mysqlXADS">
	<driver>mysql</driver>
	<xa-datasource-property name="ServerName">localhost</xa-datasource-property>
	<xa-datasource-property name="DatabaseName">db_test</xa-datasource-property>
	<security>
	  <user-name>root</user-name>
	  <password>password</password>
	</security>
	<validation>
	  <valid-connection-checker 
		   class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLValidConnectionChecker">
	  </valid-connection-checker>
	  <exception-sorter 
		   class-name="org.jboss.jca.adapters.jdbc.extensions.mysql.MySQLExceptionSorter">
	  </exception-sorter>
	</validation>
</xa-datasource>

<drivers>
	<driver name="mysql" module="com.mysql">
		<xa-datasource-class>com.mysql.cj.jdbc.MysqlXADataSource</xa-datasource-class>		
	</driver>			
</drivers>

Deploy and Use SealedSecret and KubeSeal on Openshift 4.x

Sealed Secrets are a way to encrypt Kubernetes Secrets value that can be created by anyone, but can only be decrypted by the controller running in the target cluster recovering the original object. This is a good way if we want to store our sensitive configuration values into a git repository, especially when doing a gitops approach.

First is we need to install helm and add sealed-secret repo to it,

$ brew install helm

$ helm repo add sealed-secrets https://bitnami-labs.github.io/sealed-secrets

Next is we need to create a specific Namespace and install our sealed-secret there,

$ oc project sealed-secrets

$ helm install my-sealed-secret  \
         --set containerSecurityContext.enabled=false \
		 --set podSecurityContext.enabled=false \
		 sealed-secrets/sealed-secrets	

Lets try to create a simple Kubernetes secret

$ oc create secret generic app-cred-secret \
		--from-literal=username=username123 \ 
		--from-literal=password=password123 \ 
		--dry-run=client -n edwin-ns -o yaml  > secret.yaml

Where the result would be like this,

apiVersion: v1
data:
  password: cGFzc3dvcmQxMjM=
  username: dXNlcm5hbWUxMjM=
kind: Secret
metadata:
  creationTimestamp: null
  name: app-cred-secret
  namespace: edwin-ns

Now lets try to use Kubeseal to generate a secret which is being encrypted. We can specify “controller-name” based on generated service name within “sealed-secrets” namespace.

$ brew install kubeseal

$ kubeseal --controller-name=my-sealed-secret-sealed-secrets \
       --controller-namespace=sealed-secrets -o yaml < secret.yaml > secret.sealed.yaml

We can see the result of the encrypted yaml,

apiVersion: bitnami.com/v1alpha1
kind: SealedSecret
metadata:
  creationTimestamp: null
  name: app-cred-secret
  namespace: edwin-ns
spec:
  encryptedData:
    password: AgBXkADvKsjAHS31UwWFT+........eJtODYDQw==
    username: AgAP40ssm84PhmNYDKPfDf/Cf......JDBQDtQ==
  template:
    metadata:
      creationTimestamp: null
      name: app-cred-secret
      namespace: edwin-ns

After that, we can implement it directly using “oc apply” command

$ oc apply -f secret.sealed.yaml -n edwin-ns

and we can validate by running below command,

$ oc get sealedsecrets

NAME               AGE
app-cred-secret    53m

We can see that our secret is succesfully created in our namespace

$ oc get secret app-cred-secret -n edwin-ns

NAME              TYPE     DATA   AGE
app-cred-secret   Opaque   2      55m

Deploying a Dot Net Core Apps into Openshift 4

The goal of this article is to display a simple hello-world apps build on top of a .net core 7 that can be use to test a deployment to Openshift 4 platform. And we can start it by using a git clone command

$ git clone https://github.com/edwin/hello-world-dot-net-core

Go to the corresponding folder,

$ cd hello-world-dot-net-core

Create a namespace for this app,

$ oc new-project dot-net-ns

And run this command within the sourcecode folder,

$ oc new-app dotnet:7.0-ubi8~.

It will generate logs like this,

warning: Cannot check if git requires authentication.
--> Found image 4466483 (2 months old) in image stream "openshift/dotnet" under tag "7.0-ubi8" for "dotnet:7.0-ubi8"

    .NET 7
    ------
    Platform for building and running .NET 7 applications

    Tags: builder, .net, dotnet, dotnetcore, dotnet-70

    * A source build using source code from https://github.com/edwin/hello-world-dot-net-core#master will be created
      * The resulting image will be pushed to image stream tag "hello-world-dot-net-core:latest"
      * Use 'oc start-build' to trigger a new build

--> Creating resources ...
    imagestream.image.openshift.io "hello-world-dot-net-core" created
    buildconfig.build.openshift.io "hello-world-dot-net-core" created
    deployment.apps "hello-world-dot-net-core" created
    service "hello-world-dot-net-core" created
--> Success
    Build scheduled, use 'oc logs -f buildconfig/hello-world-dot-net-core' to track its progress.
    Application is not exposed. You can expose services to the outside world by executing one or more of the commands below:
     'oc expose service/hello-world-dot-net-core'
    Run 'oc status' to view your app.

And finally we can create a route for this service,

$ oc create route edge --service=hello-world-dot-net-core

We can try to do some changes on Index.cshtml file,

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://redhat.com">Red Hat loves dotnet</a>.</p>
</div>

Save and redeploy it by running below command in the root sourcecode folder,

$ oc start-build hello-world-dot-net-core --from-dir=.

The result would be something like this,

Lets do some more changes, and deploy it to Openshift

@{
    ViewData["Title"] = "Home Page";
}

<div class="text-center">
    <h1 class="display-4">Welcome</h1>
    <p>Learn about <a href="https://redhat.com">Red Hat loves alot of programming language but we loves dotnet more</a>.</p>
</div>

And we instantly can see changes within the web page,

Code can be seen here,

https://github.com/edwin/hello-world-dot-net-core

How to Expose Openshift Route into a Secure Endpoint

We can do below command to expose a specific Openshift Service into a route or URL

$ oc expose svc <service-name>

but it would create a regular not-secure http endpoint, which sometimes not sufficient enough to fulfil our requirements. Therefore we need to find a way to create a secure route endpoint, and we can achieve that condition by using below command

$ oc create route edge --service <service-name>

It would create a route with an edge TLS termination.