Skip to content

Commit 295d7c1

Browse files
committed
Bring app auth proxy back
1 parent cab8bde commit 295d7c1

14 files changed

Lines changed: 1586 additions & 0 deletions

File tree

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ Running various samples requires access to the Kyma runtime. There are also othe
111111
| ----------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- | ---------- |
112112
| [Sample to extend SAP Cloud for Customer with user propagation](./user-propagation/README.md) | This sample provides details on how a user propagation flow can be achieved when extending SAP Cloud for Customer(C4C) via IAS | - |
113113
| [Sample to extend SAP Cloud for Customer with user propagation via XSUAA](./user-propagation-via-xsuaa/README.md) | This sample demonstrates how a user propagation flow can be achieved when extending SAP Cloud for Customer(C4C) via XSUAA | - |
114+
| [App Reverse Proxy with OIDC Authentication Middleware](./app-auth-proxy/README.md) | This sample provides a reverse proxy feature which dispatches requests to other microservice running in Kyma | - |
114115
| [Standalone approuter on SAP BTP, Kyma runtime](./standalone-approuter/README.md) | This Sample demonstrates deploying a standalone app router on Kyma runtime and use it to securely expose microservices and functions | - |
115116
| [Principal Propagation to on premise](./principal-prop-on-prem/README.md) | This sample provides details on how a principal propagation flow can be achieved when extending an on-prem system using SAP BTP, Kyma runtime | - |
116117
| [Configure Auth0 as IDP for Kyma access](./kyma-access-auth0-as-idp/) | This sample provide details on how Auth0 can be configured as an Identity Provider for accessing Kyma runtime | - |

app-auth-proxy/README.md

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,164 @@
1+
# App Reverse Proxy with OIDC Authentication Middleware
2+
3+
## Overview
4+
5+
This sample provides a reverse proxy feature which dispatches requests to other microservices running in Kyma. It includes a middleware to handle authentication which is based on Open ID Connect and can be configured using XSUAA or SAP IAS. The authentication middleware creates a server side session which is referenced by a cookie provided to the client. It also includes a middleware to validate user scopes based on HTTP methods. By default the app will use a memory store for storing user sessions which is meant for development only. It also contains a Redis implementation for storing session which is the preferred usage. See [store-implementations](https://github.com/gorilla/sessions#store-implementations) for other options.
6+
7+
This sample demonstrates how to:
8+
9+
- Create a development Namespace in the Kyma runtime.
10+
- Consume the SCP service XSUAA
11+
- Deploy the following Kubernetes resources:
12+
- API deployment written in GO
13+
- API Rule
14+
- Service
15+
- Configmap
16+
- ServiceBinding
17+
- ServiceBindingUsage
18+
19+
## Prerequisites
20+
21+
- SAP BTP, Kyma runtime instance
22+
- [Docker](https://www.docker.com/)
23+
- [Go](https://golang.org/doc/install)
24+
- [kubectl](https://kubernetes.io/docs/tasks/tools/install-kubectl/) configured to use the `KUBECONFIG` file downloaded from the Kyma runtime
25+
26+
## Steps
27+
28+
### Create XSUAA Service Instance
29+
30+
1. Create a new `dev` Namespace:
31+
32+
```shell script
33+
kubectl create namespace dev
34+
kubectl label namespaces dev istio-injection=enabled
35+
```
36+
37+
2. Open the file `k8s/xsuaa-instance.yaml` and adjust the value `<cluster domain>` and then apply the file
38+
39+
```shell script
40+
kubectl -n dev apply -f ./k8s/xsuaa-instance.yaml
41+
```
42+
43+
> > For a complete list of parameters visit [Application Security Descriptor Configuration Syntax](https://help.sap.com/viewer/4505d0bdaf4948449b7f7379d24d0f0d/2.0.04/en-US/6d3ed64092f748cbac691abc5fe52985.html)
44+
45+
3. Once the instance is provisioned choose the menu option `Service Management -> BTP Service Bindings` within the `dev` namespace.
46+
4. Choose the `Secret` which should display the instance secret in a dialog. Choose `Decode` to view the values. These will be needed if running the sample locally.
47+
48+
### Run the API locally
49+
50+
1. Optionally set the environment variables required to connect with the XSUAA instance which can be found in the `Secret` generated with the service instance:
51+
52+
```shell script
53+
export IDP_clientid='<instance clientid>'
54+
export IDP_clientsecret=<instance clientsecret>
55+
export IDP_url=<instance url>
56+
export IDP_xsappname=<xsappname>
57+
```
58+
59+
2. Adjust the config.json which contains the following properties. The provided `config.json` is configured to use the examples
60+
61+
- [React frontend MS SQL](../frontend-react-mssql/README.md)
62+
- Requires the configmap API_URL to point to `https://app-auth-proxy.<cluster domain>`
63+
- [Golang MS SQL database API](../api-mssql-go/README.md)
64+
65+
| Property | Description | Remarks |
66+
| ------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |
67+
| routes | An array of routes to be proxied | |
68+
| routes.path | The incoming path | |
69+
| routes.priority | The priority of the path with 1 be the highest | |
70+
| routes.protected | If the auth middleware is required on the path | |
71+
| routes.remove_from_path | If assigned, this value will be removed from routes.path before the call is proxied | |
72+
| routes.target | The target of the proxied route which can be a service url | |
73+
| routes.http_method_scopes | An array containing HTTP methods and thier associated user scopes | For no restrictions this can be obmitted or assigned: http\-method: "\*", "scope": "\*" |
74+
| routes.http_method_scopes.http_method | An HTTP methods for example GET | |
75+
| routes.http_method_scopes.scope | A scope which is allowed the call the given http_method on the route path | Use $XSAPPNAME for the application name, for example using a Kyma scopes - $XSAPPNAME.runtimeDeveloper |
76+
| idp_config | Optionally set IDP config if not using a service binding | |
77+
| idp_config.url | The IDP url | If this value is not set, the environment variables will be used |
78+
| idp_config.clientsecret | The IDP client secret | |
79+
| idp_config.clientid | The IDP client ID | |
80+
| idp_config.token_endpoint_auth_method | The htttp method used to during authentication | For XSUAA use client_secret_post, for SAPIAS us client_secret_basic |
81+
| redirect_uri | The registered redirect_uri to be called | |
82+
| debug | Toggle debug on or off | |
83+
| redis_store | When configure app will you redis to store the sessions, otherwise a memory store is used which should only be used for evaluation. | |
84+
| redis_store.addr | The service address of the Redis database | If this value is not set, memory storage will be used to store the session |
85+
| redis_store.password | The password of the Redis database | |
86+
| redis_store.db | The database index | |
87+
| cookie.session_name | The name of the session cookie | |
88+
| cookie.max_age_seconds | The max age of the session cookie | |
89+
| cookie.key | The key used to encrypt the session cookie | |
90+
| cookie.httponly | If the cookie can be accessed with Javascript or only http | |
91+
92+
3. Run the application:
93+
94+
```shell script
95+
go run ./cmd/proxy
96+
```
97+
98+
4. Accessible endpoints include
99+
- http://localhost:8000/
100+
- http://localhost:8000/auth/user
101+
- http://localhost:8000/auth/groups
102+
103+
### Build the Docker image
104+
105+
1. Build and push the image to your Docker repository:
106+
107+
```shell script
108+
docker build -t {your-docker-account}/app-auth-proxy -f docker/Dockerfile .
109+
docker push {your-docker-account}/app-auth-proxy
110+
```
111+
112+
2. To run the image locally adjust the config.json and either set the env variables individually, or copy them from your environment:
113+
114+
```shell script
115+
docker run -p 8000:8000 --env-file ./env.list --mount type=bind,source=$(pwd)/config/config.json,target=/app/config/config.json -d jcawley5/app-auth-proxy:latest
116+
OR
117+
docker run -p 8000:8000 --env-file <(env | grep IDP) --mount type=bind,source=$(pwd)/config/config.json,target=/app/config/config.json -d jcawley5/app-auth-proxy:latest
118+
```
119+
120+
### Deploy the APP
121+
122+
1. Create a new `dev` Namespace:
123+
124+
```shell script
125+
kubectl create namespace dev
126+
```
127+
128+
2. Within `./k8s/configmap.yaml` adjust the values and then apply the ConfigMap:
129+
130+
```shell script
131+
kubectl -n dev apply -f ./k8s/configmap.yaml
132+
```
133+
134+
3. Get the name of the ServiceInstance:
135+
136+
```shell script
137+
kubectl -n dev get serviceinstances
138+
```
139+
140+
For example:
141+
142+
| NAME | CLASS | PLAN | STATUS | AGE |
143+
| ---------------------- | ------------------------- | ----------- | ------ | --- |
144+
| **_xsuaa-showy-yard_** | ClusterServiceClass/xsuaa | application | Ready | 63m |
145+
146+
4. Within `./k8s/deployment.yaml` adjust the value of `<Service Instance Name>` to the XSUAA service instance name and the apply the Deployment:
147+
148+
```shell script
149+
kubectl -n dev apply -f ./k8s/deployment.yaml
150+
```
151+
152+
5. Apply the APIRule:
153+
154+
```shell script
155+
kubectl -n dev apply -f ./k8s/apirule.yaml
156+
```
157+
158+
6. Verify that the Deployment is up and running:
159+
160+
```shell script
161+
kubectl -n dev get deployment app-auth-proxy
162+
```
163+
164+
7. Use the APIRule `https://app-auth-proxy.{cluster-domain}`

app-auth-proxy/cmd/proxy/main.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package main
2+
3+
import (
4+
"context"
5+
"net/http"
6+
7+
"github.com/go-redis/redis/v8"
8+
"github.com/rbcervilla/redisstore/v8"
9+
10+
"github.com/quasoft/memstore"
11+
log "github.com/sirupsen/logrus"
12+
13+
"github.com/gorilla/mux"
14+
"github.com/gorilla/sessions"
15+
16+
"github.com/SAP-samples/kyma-runtime-extension-samples/app-auth-proxy/internal/auth"
17+
"github.com/SAP-samples/kyma-runtime-extension-samples/app-auth-proxy/internal/proxy"
18+
19+
appconfig "github.com/SAP-samples/kyma-runtime-extension-samples/app-auth-proxy/internal/config"
20+
)
21+
22+
func main() {
23+
24+
appconfig := appconfig.GetConfig()
25+
oidcConfig := setOIDCConfig(appconfig)
26+
sessionName := appconfig.Cookie.SessionName
27+
28+
var authOIDC *auth.OIDCConfig
29+
if len(appconfig.RedisStore.Addr) > 0 {
30+
store := setRedisStore(appconfig)
31+
authOIDC = auth.InitOIDC(oidcConfig, store, sessionName)
32+
} else {
33+
store := setMemStore(appconfig)
34+
authOIDC = auth.InitOIDC(oidcConfig, store, sessionName)
35+
}
36+
37+
router := mux.NewRouter().StrictSlash(true)
38+
proxy.SetRoutes(router, appconfig, authOIDC)
39+
40+
log.Fatal(http.ListenAndServe(":8000", router))
41+
}
42+
43+
func setRedisStore(appconfig *appconfig.Config) *redisstore.RedisStore {
44+
45+
log.Info("--------USING REDIS STORAGE--------")
46+
47+
client := redis.NewClient(&redis.Options{
48+
Addr: appconfig.RedisStore.Addr,
49+
Password: appconfig.RedisStore.Password,
50+
DB: appconfig.RedisStore.DB,
51+
})
52+
53+
store, err := redisstore.NewRedisStore(context.Background(), client)
54+
if err != nil {
55+
log.Fatalf("failed to create redis store with address: %s, error: %s ", appconfig.RedisStore.Addr, err)
56+
}
57+
58+
store.Options(sessions.Options{
59+
Path: "/",
60+
MaxAge: appconfig.Cookie.MaxAgeSeconds,
61+
HttpOnly: appconfig.Cookie.HttpOnly,
62+
})
63+
64+
return store
65+
}
66+
67+
//See https://github.com/gorilla/sessions#store-implementations
68+
func setMemStore(appconfig *appconfig.Config) *memstore.MemStore {
69+
70+
log.Warn("--------USING MEMORY STORAGE - THIS IS NOT RECOMMENDED!--------")
71+
store := memstore.NewMemStore([]byte(appconfig.Cookie.Key))
72+
73+
store.Options = &sessions.Options{
74+
Path: "/",
75+
MaxAge: appconfig.Cookie.MaxAgeSeconds,
76+
HttpOnly: appconfig.Cookie.HttpOnly,
77+
}
78+
79+
return store
80+
}
81+
82+
func setOIDCConfig(appconfig *appconfig.Config) *auth.InitConfig {
83+
84+
oidcConfig := &auth.InitConfig{}
85+
oidcConfig.ClientID = appconfig.IDPConfig.ClientID
86+
oidcConfig.ClientSecret = appconfig.IDPConfig.ClientSecret
87+
oidcConfig.URL = appconfig.IDPConfig.URL
88+
oidcConfig.RedirectURL = appconfig.RedirectURI
89+
oidcConfig.Token_endpoint_auth_method = appconfig.IDPConfig.TokenEndpointAuthMethod
90+
oidcConfig.XSAppName = appconfig.IDPConfig.XSAppName
91+
92+
return oidcConfig
93+
}

app-auth-proxy/config/config.json

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
{
2+
"routes": [{
3+
"path": "/headers",
4+
"priority": 1,
5+
"protected": true,
6+
"remove_from_path": "",
7+
"target": "https://httpbin.org"
8+
},{
9+
"path": "/anything",
10+
"priority": 2,
11+
"protected": true,
12+
"remove_from_path": "",
13+
"target": "https://httpbin.org",
14+
"http_method_scopes": [{
15+
"http_method": "*",
16+
"scope": "*"
17+
}]
18+
},{
19+
"path": "/",
20+
"priority": 10,
21+
"protected": true,
22+
"remove_from_path": "",
23+
"target": "https://httpbin.org",
24+
"http_method_scopes": [{
25+
"http_method": "GET",
26+
"scope": "$XSAPPNAME.runtimeDeveloper"
27+
},{
28+
"http_method": "POST",
29+
"scope": "$XSAPPNAME.runtimeNamespaceAdmin"
30+
}]
31+
}],
32+
"redirect_uri": "http://localhost:8000/oauth/callback",
33+
"idp_config": {
34+
"url": "",
35+
"clientsecret": "",
36+
"clientid": "",
37+
"xsappname": ""
38+
},
39+
"debug": true,
40+
"redis_store": {
41+
"addr": "",
42+
"password":"",
43+
"db": 0
44+
},
45+
"cookie":{
46+
"session_name": "sample-session",
47+
"max_age_seconds": 900,
48+
"key": "asecurekeyvalue",
49+
"httponly": true
50+
}
51+
}
52+
53+

app-auth-proxy/docker/Dockerfile

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
FROM golang:1.14 as builder
2+
3+
ENV GO111MODULE=on
4+
5+
WORKDIR /app
6+
COPY go.mod .
7+
COPY go.sum .
8+
9+
RUN go mod download
10+
11+
COPY cmd ./cmd
12+
COPY internal ./internal
13+
14+
RUN ls /app/
15+
RUN CGO_ENABLED=0 GOOS=linux go build -v -a -o app-auth-proxy ./cmd/proxy
16+
17+
COPY config ./config
18+
19+
FROM alpine:3.8 as certs
20+
RUN apk add -U --no-cache ca-certificates
21+
22+
FROM scratch
23+
WORKDIR /app
24+
COPY --from=builder /app/app-auth-proxy /app/
25+
COPY --from=builder /app/config /app/config/
26+
COPY --from=certs /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/
27+
28+
EXPOSE 8000
29+
ENTRYPOINT ["/app/app-auth-proxy"]

app-auth-proxy/go.mod

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
module github.com/SAP-samples/kyma-runtime-extension-samples/app-auth-proxy
2+
3+
go 1.14
4+
5+
require (
6+
github.com/coreos/go-oidc v2.2.1+incompatible
7+
github.com/go-redis/redis/v8 v8.4.2
8+
github.com/gorilla/mux v1.7.4
9+
github.com/gorilla/sessions v1.2.1
10+
github.com/pquerna/cachecontrol v0.0.0-20200921180117-858c6e7e6b7e // indirect
11+
github.com/quasoft/memstore v0.0.0-20191010062613-2bce066d2b0b
12+
github.com/rbcervilla/redisstore/v8 v8.0.0
13+
github.com/sirupsen/logrus v1.7.0
14+
github.com/vrischmann/envconfig v1.3.0
15+
golang.org/x/net v0.0.0-20201006153459-a7d1128ccaa0
16+
golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43
17+
gopkg.in/square/go-jose.v2 v2.5.1 // indirect
18+
)

0 commit comments

Comments
 (0)