Skip to content

Commit

Permalink
chore: Add JS Kyma On-Premise documentation (#1850)
Browse files Browse the repository at this point in the history
* add docs for kyma onpremise connectivity

* update some of the docs

* lint

* add suggest changes

* update gardener docs as well

* align content

* dodge vale

* vale
  • Loading branch information
tomfrenken authored Jul 3, 2024
1 parent ff64383 commit 2244288
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 227 deletions.
5 changes: 5 additions & 0 deletions docs-js/environments/code/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import KymaAppTabsContent from './kyma-app-tabs.mdx';
import DockerFileContent from './Dockerfile.mdx';
import OperatorDestinationServiceContent from './operator-destination-service.mdx';
import OperatorXsuaaServiceContent from './operator-xsuaa-service.mdx';
import KubernetesOnPremiseContent from './kubernetes-on-premise.mdx';

export function DockerFile() {
return <DockerFileContent />;
Expand All @@ -22,3 +23,7 @@ export function KymaApprouterTabs() {
export function KymaAppTabs() {
return <KymaAppTabsContent />;
}

export function KubernetesTransparentProxy() {
return <KubernetesOnPremiseContent />;
}
140 changes: 140 additions & 0 deletions docs-js/environments/code/kubernetes-on-premise.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

### Prerequisites

This guide assumes you have both the **Transparent Proxy** (version `>= 1.4.0`) and **Connectivity Proxy** (version `>= 2.11.0`) installed in your cluster.
For Kyma the Transparent Proxy is available as a module that can be enabled as described [here](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/transparent-proxy-in-kyma-environment).
The Connectivity Proxy can be installed as described [here](https://help.sap.com/docs/connectivity/sap-btp-connectivity-cf/operations-via-helm).

### Background Information

When using the Transparent Proxy your app performs requests against the Transparent Proxy without explicit authentication, relying on the secure network communication provided by Kyma via Istio.
The Transparent Proxy will obtain the relevant destination from the destination service and use it to forward the request via the Connectivity Proxy to the On-Premise system.
Consequently, your app itself does not interact with destination or connectivity service at all and thus your application pods do not require bindings to these two services.

Please note that the current implementation of the Transparent Proxy does not yet cover all use cases.

<details>
<summary>Constraints when using the Transparent Proxy</summary>

- Private Link not yet supported

</details>

:::tip
This approach is conceptually different from what you may be used to from a Cloud Foundry environment.
The official [documentation of the Transparent Proxy](https://help.sap.com/docs/CP_CONNECTIVITY/cca91383641e40ffbe03bdc78f00f681/e661713ef7d14373b57e3e26b0b03b86.html) gives more information on the architecture.
:::

### Create a Kubernetes Resource

For the creation of the necessary Kubernetes Resources, please refer to our [Java documentation](/docs/java/environments/kubernetes-kyma#create-a-kubernetes-resource).

### Executing Requests

In your application you can now configure a destination to execute requests:

<Tabs
groupId="dynamic-dest"
defaultValue="single"
values={[
{label: "Individual Destination", value: "single"},
{label: "Dynamic Desitnations", value: "dynamic"}
]}>
<TabItem value="single">

```ts
import {
registerDestination,
getTenantId,
retrieveJwt
} from '@sap-cloud-sdk/connectivity';

const destination = {
name: 'registered-destination',
url: 'http://my-destination.namespace/'
// for principal propagation make sure to set the forwardAuthToken flag to true:
// forwardAuthToken: true
};
await registerDestination(destination, options);

const result = await myEntityApi
.requestBuilder()
.getAll()
// for a subscriber tenant make sure to send the tenant header:
// .addCustomHeaders({ 'X-Tenant-Id': getTenantId(retrieveJwt(request)) })
.execute({ destinationName: 'registered-destination' });
// for principal propagation make sure the send the auth token:
// .execute({ destinationName: 'registered-destination', jwt: retrieveJwt(request) });
```

</TabItem>
<TabItem value="dynamic">

```ts
import {
registerDestination,
getTenantId,
retrieveJwt
} from '@sap-cloud-sdk/connectivity';

const destination = {
name: 'registered-destination',
url: 'http://dynamic-destination.namespace/'
// for principal propagation make sure to set the forwardAuthToken flag to true:
// forwardAuthToken: true
};
await registerDestination(destination, options);

const result = await myEntityApi
.requestBuilder()
.getAll()
.addCustomHeaders({ 'X-Destination-Name': '<CF-DESTINATION-NAME>' })
// for a subscriber tenant make sure to send the tenant header:
// .addCustomHeaders({ 'X-Tenant-Id': getTenantId(retrieveJwt(request)) })
.execute({ destinationName: 'registered-destination' });
// for principal propagation make sure the send the auth token:
// .execute({ destinationName: 'registered-destination', jwt: retrieveJwt(request) });
```

</TabItem>
</Tabs>

- Replace `namespace` in the URL with the namespace you installed the Transparent Proxy into.

The code above shows an example how you can then use the `destination` object to perform an OData request against the system.

:::tip Connecting to Cloud systems
The above approach is not limited to destinations of proxy type `ON_PREMISE`, `INTERNET` destinations are supported as well.
:::

### Troubleshooting

When using proxy servers it can be difficult to troubleshoot issues as it is often not obvious where exactly the error occurred.
For example, with the Transparent Proxy errors might occur on the target system (e.g. OData service), the Destination Service or the Transparent Proxy itself.

To make troubleshooting easier the Transparent Proxy adds additional response headers to provide more information about where an error occurred.
For the above example of executing OData requests you can access the response headers as follows:

```ts
const result = await myEntityApi
.requestBuilder()
.getAll()
.execute('registered-destination')
.catch(err => {
console.error('Error Headers:', err.rootCause?.response?.headers);
});
```

<details>
<summary>List of headers added by the Transparent Proxy</summary>

- `X-Error-Origin` - the source of the error
- `X-Proxy-Server` - the proxy server (Transparent Proxy)
- `X-Error-Message` - thorough error message
- `X-Error-Internal-Code` - set only when the source of the error is the XSUAA or Destination service.
The value is the HTTP code returned from one of these services.
- `X-Request-Id` is sent with the response in all requests, both successful and failed

</details>
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ keywords:
import {
DockerFile,
OperatorDestinationService,
OperatorXsuaaService
OperatorXsuaaService,
KubernetesTransparentProxy
} from './code';

# Migrate a Cloud Foundry Application to a Kubernetes Cluster
Expand Down Expand Up @@ -284,7 +285,7 @@ The field `secretName: secret-tls` in the configuration implies that all TLS cer
Finally, look at how to serve the Service at the root of your subdomain.
This way the Service is exposed to the internet using TLS.
### Expose the Application using Approuter
### Expose the Application using approuter
In the next steps, you will configure and deploy an approuter so that only users authenticated by your identity provider can access the application endpoints.
For that, create a [simple application](https:/SAP-samples/cloud-sdk-js/tree/main/samples/k8s-sample-application/approuter) that uses the @sap/approuter.
Expand Down Expand Up @@ -395,217 +396,6 @@ spec:
number: 8080
```
## Principal Propagation
Just as in SAP BTP Cloud Foundry, you have to collect the principal's JSON web token (JWT) from the authentication header after executing one of the requests with the typed client libraries.
The example below uses the `retrieveJwt()` function:
<!-- vale Vale.Spelling = NO -->
```ts
import { Injectable } from '@nestjs/common';
import { retrieveJwt } from '@sap-cloud-sdk/connectivity';
import { Request } from 'express';
import { businessPartnerService } from './generated/business-partner-service';

@Injectable()
export class PrincipalBusinessPartnerService {
async getFiveBusinessPartners(request: Request): Promise<BusinessPartner[]> {
return BusinessPartner.requestBuilder()
.getAll()
.top(5)
.execute({
destinationName: 'MY-DESTINATION',
jwt: retrieveJwt(request)
});
}
}
```
<!-- vale Vale.Spelling = YES -->
## On-Premise Connectivity
:::danger
On-Premise connectivity in Kubernetes is not available for SAP customers as of July 2023.
:::
You need a connectivity proxy to connect to on-premise systems inside a Kubernetes cluster.
The following steps show how to create and use it.
1. Create a route with TLS enabled for the connectivity proxy.
To enable TLS on SAP Gardener, refer to the ["Configure TLS and Obtain a Domain in SAP Gardener"](#configure-tls-and-obtain-a-domain-in-sap-gardener) section.
If your cluster is not backed by SAP Gardener, refer to the official [Kubernetes documentation](https://kubernetes.io/docs/tasks/tls/managing-tls-in-a-cluster/).
This example shows how to add the custom domain `connectivitytunnel.*` to the TLS section in SAP Gardener.
This creates a certificate for this domain automatically.
```yaml
spec:
tls:
- hosts:
- cloud-sdk-js.sdktests.shoot.canary.k8s-hana.ondemand.com
- e2e.ingress.cloud-sdk-js.sdktests.shoot.canary.k8s-hana.ondemand.com
- connectivitytunnel.ingress.cloud-sdk-js.sdktests.shoot.canary.k8s-hana.ondemand.com
secretName: secret-tls
```
2. Add your CA certificate to the JVM trust store of the SAP Cloud Connector.
The CA certificate is stored in the TLS secret, in this case the name is `secret-tls`.
To access the information inside a secret, use the following code snippet:
```bash
kubectl get secret YOUR_SECRET -o go-template='{{range $k,$v := .data}}{{printf "%s: " $k}}{{if not $v}}{{$v}}{{else}}{{$v | base64decode}}{{end}}{{"\n"}}{{end}}'
```
Inside the secret should be a block prefixed with `ca.crt`.
Copy this certificate into a file and then follow [this guide](https://connect2id.com/blog/importing-ca-root-cert-into-jvm-trust-store) to add it to the JVM trust store of your SAP Cloud Connector.
3. Create and bind the connectivity service with the `connectivty_proxy` plan following the steps shown [before](#bind-the-destination-service).
Additionally, to bind the secret represented by Kubernetes native YAML format, you need to convert it to JSON format for the connectivity proxy to consume it.
Use the code snippet in step (2) above to retrieve the values of the secret.
Here is an example JSON secret:
```yaml
apiVersion: v1
kind: Secret
metadata:
name: connectivity-proxy-service-key
type: Opaque
stringData:
connectivity_key: '{
"clientid": "YOUR_CLIENT_ID",
"clientsecret": "YOUR_CLIENT_SECRET",
"xsappname": "YOUR_XS_APP_NAME",
"connectivity_service": {
"CAs_path":"/api/v1/CAs",
"CAs_signing_path":"/api/v1/CAs/signing",
"api_path":"/api/v1",
"tunnel_path":"/api/v1/tunnel",
"url":"https://connectivity.cf.sap.hana.ondemand.com"
},
"subaccount_id": "YOUR_SUBACCOUNT_ID",
"subaccount_subdomain": "YOUR_SUBACCOUNT_SUBDOMAIN",
"token_service_domain": "YOUR_TOKEN_SERVICE_DOMAIN",
"token_service_url": "YOUR_TOKEN_SERVICE_URL",
"token_service_url_pattern": "https://{tenant}.authentication.sap.hana.ondemand.com/oauth/token",
"token_service_url_pattern_tenant_key": "subaccount_subdomain"
}'
```
:::info
Note that the example used the `stringData` field type instead of the default `data` field type to benefit from automatic base64 encoding.
This is a requirement of the connectivity proxy since it can't consume the data of the secret in YAML format.
:::
4. Download the CA certificate of the connectivity service and create a secret that includes:
- the CA certificate of the connectivity service
- your private key
- your public certificate
The private key and public certificate are also stored in the TLS secret.
Use the previous code snippet to retrieve it from the secret and save them in separate files.
Finally, download the CA certificate with the following line:
```bash
curl https://connectivity.cf.sap.hana.ondemand.com/api/v1/CAs/signing -o connectivity_ca.crt
```
Now, create the secret with this command:
```bash
kubectl create secret generic connectivity-tls --from-file=ca.crt=YOUR_CONNECTIVITY_CA_KEY.crt --from-file=tls.key=YOUR_PRIVATE_KEY.key --from-file=tls.crt=YOUR_PUBLIC_KEY.crt --namespace default
```
5. Create a secret that contains credentials to access the docker image which the connectivity proxy is using.
6. Create a `values.yaml` file containing the configuration that suits your desired operational mode of the connectivity proxy, for the available operational modes refer to the [documentation](https://help.sap.com/viewer/cca91383641e40ffbe03bdc78f00f681/Cloud/en-US/f3c1ef45db77489c8d039acc9530358f.html).
For the specific content of the configuration refer to the [configuration guide](https://help.sap.com/viewer/cca91383641e40ffbe03bdc78f00f681/Cloud/en-US/eaa8204fc7484df984b3c321624827ff.html).
Here is an example for the "single tenant in a trusted environment" mode:
```yaml
deployment:
replicaCount: 1
image:
pullSecret: 'proxy-secret'
ingress:
tls:
secretName: 'connectivity-tls'
config:
integration:
auditlog:
mode: console
connectivityService:
serviceCredentialsKey: 'connectivity_key'
tenantMode: dedicated
subaccountId: 'YOUR_SUBACCOUNT_ID'
subaccountSubdomain: 'YOUR_SUBACCOUNT_SUBDOMAIN'
servers:
businessDataTunnel:
externalHost: 'connectivitytunnel.ingress.cloud-sdk-js.sdktests.shoot.canary.k8s-hana.ondemand.com'
externalPort: 443
proxy:
rfcAndLdap:
enabled: true
enableProxyAuthorization: false
http:
enabled: true
enableProxyAuthorization: false
enableRequestTracing: true
socks5:
enableProxyAuthorization: false
```
7. For your application to reach on-premise destinations, it needs to provide the proxy settings and the token URL.
Add them manually to the `secret` containing the service binding using the following code snippet:
```bash
kubectl edit secret YOUR_BINDING
```
Now add the fields `onpremise_proxy_host` and `onpremise_proxy_port` and `url`.
The host has the pattern `connectivity-proxy.YOUR_NAMESPACE` which in this case is `connectivity-proxy.default`.
The default port is `20003`.
The `url` field should contain the same value as `token_service_url`.
Be aware that the values have to be encoded in base64, for example:
```yaml
onpremise_proxy_host: Y29ubmVjdGl2aXR5LXByb3h5LmRlZmF1bHQ=
onpremise_proxy_port: MjAwMDM=
url: aHR0cHM6Ly9teS1hcGkuYXV0aGVudGljYXRpb24uc2FwLmhhbmEub25kZW1hbmQuY29tCg==
```
8. Finally, add the binding to your `deployment.yml`, the same way you would add any other binding.
## Create a Continuous Integration Pipeline
You can create a simple CI/CD pipeline with GitHub Actions or change your existing pipeline.
Follow the steps below to create a service account and obtain the authentication tokens and certificates needed to interact with your cluster within the pipeline.
1. [Create a service account](https://kubernetes.io/docs/reference/access-authn-authz/service-accounts-admin/) in your cluster.
2. Bind the `cluster-admin` cluster role to the service account.
Alternatively, create a more strict role.
3. Obtain the token and CA certificate from the secret that is automatically created for that service account.
4. Obtain the cluster API endpoint via command `kubectl cluster-info`.
Use the service account in your automation to connect to the cluster:
```bash
kubectl config set-cluster gardener --server=YOUR_CLUSTER_API_ENDPOINT
kubectl config set-context gardener --cluster=gardener
kubectl config use-context gardener
kubectl config view
kubectl --token=${{ secrets.KUBERNETES_SERVICE_TOKEN }} --certificate-authority YOUR_CA_CERT_PATH cluster-info
```
After completing the previous steps, run the command below to shutdown and restart all the Pods.
```bash
kubectl --token=${{ secrets.KUBERNETES_SERVICE_TOKEN }} --certificate-authority YOUR_CA_CERT_PATH rollout restart deployment/YOUR_DEPLOYMENT
```
### On-Premise Connectivity
If the Deployment is configured with `ImagePullStrategy: Always`, it will pull the updated image and use it.
<KubernetesTransparentProxy />
Loading

0 comments on commit 2244288

Please sign in to comment.