├── images ├── tls.png ├── kc11.png ├── kc12.png ├── kc13.png ├── kc14.png ├── ktls.png ├── whoami.png ├── keycloakok.png ├── keycloakauth.png ├── keycloaklogin.png └── whoami_authenticated.png ├── keycloak-gatekeeper ├── keycloak-gatekeeper.conf └── keycloak-gatekeeper.conf.sample ├── docker-compose.yml └── README.md /images/tls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/tls.png -------------------------------------------------------------------------------- /images/kc11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/kc11.png -------------------------------------------------------------------------------- /images/kc12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/kc12.png -------------------------------------------------------------------------------- /images/kc13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/kc13.png -------------------------------------------------------------------------------- /images/kc14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/kc14.png -------------------------------------------------------------------------------- /images/ktls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/ktls.png -------------------------------------------------------------------------------- /images/whoami.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/whoami.png -------------------------------------------------------------------------------- /images/keycloakok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/keycloakok.png -------------------------------------------------------------------------------- /images/keycloakauth.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/keycloakauth.png -------------------------------------------------------------------------------- /images/keycloaklogin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/keycloaklogin.png -------------------------------------------------------------------------------- /images/whoami_authenticated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/asatrya/keycloak-traefik-tutorial/HEAD/images/whoami_authenticated.png -------------------------------------------------------------------------------- /keycloak-gatekeeper/keycloak-gatekeeper.conf: -------------------------------------------------------------------------------- 1 | # is the url for retrieve the OpenID configuration - normally the /auth/realm/ 2 | discovery-url: https://auth.lab.com/auth/realms/demo-realm 3 | # skip tls verify 4 | skip-openid-provider-tls-verify: true 5 | # the client id for the 'client' application 6 | client-id: demo-client 7 | # the secret associated to the 'client' application 8 | client-secret: CLIENT-SECRETE-HERE 9 | # the interface definition you wish the proxy to listen, all interfaces is specified as ':', unix sockets as unix://| 10 | listen: :3000 11 | # whether to enable refresh tokens 12 | enable-refresh-tokens: true 13 | # the location of a certificate you wish the proxy to use for TLS support 14 | tls-cert: 15 | # the location of a private key for TLS 16 | tls-private-key: 17 | # the redirection url, essentially the site url, note: /oauth/callback is added at the end 18 | redirection-url: https://service1.lab.com 19 | # the encryption key used to encode the session state 20 | encryption-key: vGcLt8ZUdPX5fXhtLZaPHZkGWHZrT6aa 21 | # the upstream endpoint which we should proxy request 22 | upstream-url: http://service_1:80/ 23 | # additional scopes to add to add to the default (openid+email+profile) 24 | scopes: 25 | #- vpn-user 26 | # a collection of resource i.e. urls that you wish to protect 27 | # ====================================================================== 28 | resources: 29 | - uri: /* 30 | methods: 31 | - GET 32 | 33 | - uri: /admin/* 34 | methods: 35 | - GET 36 | roles: 37 | - openvpn:vpn-user 38 | - openvpn:prod-vpn 39 | 40 | - uri: /admin/* 41 | methods: 42 | - GET 43 | roles: 44 | - openvpn:vpn-user 45 | - openvpn:commons-prod-vpn 46 | 47 | 48 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '2' 2 | services: 3 | 4 | traefik: 5 | image: traefik:v1.7.4-alpine 6 | hostname: traefik.lab.com 7 | environment: 8 | TZ: Asia/Jakarta 9 | ports: 10 | - "80:80" 11 | - "443:443" 12 | volumes: 13 | - /var/run/docker.sock:/var/run/docker.sock 14 | command: > 15 | --logLevel='DEBUG' 16 | --api.dashboard=true 17 | --InsecureSkipVerify=true 18 | --entryPoints='Name:http Address::80 Redirect.EntryPoint:https' 19 | --entryPoints='Name:https Address::443 TLS' 20 | --defaultentrypoints='http,https' 21 | --docker 22 | --docker.exposedbydefault=true 23 | --docker.watch=true 24 | --docker.swarmmode=false 25 | --docker.endpoint='unix:///var/run/docker.sock' 26 | networks: 27 | auth_net: 28 | service_net: 29 | labels: 30 | traefik.enable: true 31 | 32 | keycloak_db: 33 | image: postgres:11.2-alpine 34 | environment: 35 | - POSTGRES_DB=keycloak 36 | - POSTGRES_USER=keycloak 37 | - POSTGRES_PASSWORD=password 38 | - POSTGRES_ROOT_PASSWORD=password 39 | networks: 40 | auth_net: 41 | ports: 42 | - "5432:5432" 43 | volumes: 44 | - keycloak_db_data_vol:/var/lib/postgresql/data 45 | labels: 46 | - "traefik.enable=false" 47 | 48 | keycloak: 49 | image: jboss/keycloak:7.0.1 50 | hostname: keycloak 51 | environment: 52 | - DB_VENDOR=POSTGRES 53 | - DB_ADDR=keycloak_db 54 | - DB_DATABASE=keycloak 55 | - DB_PORT=5432 56 | - DB_USER=keycloak 57 | - DB_SCHEMA=public 58 | - DB_PASSWORD=password 59 | - PROXY_ADDRESS_FORWARDING=true 60 | - KEYCLOAK_LOGLEVEL=INFO 61 | - KEYCLOAK_USER=admin 62 | - KEYCLOAK_PASSWORD=password 63 | networks: 64 | auth_net: 65 | depends_on: 66 | - traefik 67 | - keycloak_db 68 | labels: 69 | - traefik.port=8443 70 | - traefik.frontend.rule=Host:auth.lab.com 71 | - traefik.protocol=https 72 | command: ["-b", "0.0.0.0", "-Dkeycloak.profile.feature.docker=enabled"] 73 | 74 | keycloak-gatekeeper: 75 | image: keycloak/keycloak-gatekeeper:7.0.0 76 | labels: 77 | - "traefik.port=3000" 78 | - "traefik.frontend.rule=Host:service1.lab.com" 79 | - "traefik.protocol=http" 80 | restart: always 81 | depends_on: 82 | - keycloak 83 | networks: 84 | service_net: 85 | external_links: 86 | - traefik:auth.lab.com 87 | volumes: 88 | - ./keycloak-gatekeeper/keycloak-gatekeeper.conf:/etc/keycloak-gatekeeper.conf 89 | entrypoint: 90 | - /opt/keycloak-gatekeeper 91 | - --config=/etc/keycloak-gatekeeper.conf 92 | 93 | service_1: 94 | image: containous/whoami 95 | networks: 96 | service_net: 97 | depends_on: 98 | - traefik 99 | labels: 100 | - "traefik.enable=false" 101 | 102 | networks: 103 | service_net: 104 | auth_net: 105 | 106 | volumes: 107 | keycloak_db_data_vol: 108 | -------------------------------------------------------------------------------- /keycloak-gatekeeper/keycloak-gatekeeper.conf.sample: -------------------------------------------------------------------------------- 1 | # is the url for retrieve the openid configuration - normally the /auth/realm/ 2 | discovery-url: https://keycloak.example.com/auth/realms/commons 3 | # the client id for the 'client' application 4 | client-id: 5 | # the secret associated to the 'client' application - note the client_secret is optional, required for 6 | # oauth2 access_type=confidential i.e. the client is being verified 7 | client-secret: 8 | # the interface definition you wish the proxy to listen, all interfaces is specified as ':' 9 | listen: 127.0.0.1:3000 10 | # whether to request offline access and use a refresh token 11 | enable-refresh-tokens: true 12 | # log all incoming requests 13 | enable-logging: true 14 | # log in json format 15 | enable-json-logging: true 16 | # should the access token be encrypted - you need an encryption-key if 'true' 17 | enable-encrypted-token: false 18 | # do not redirec the request, simple 307 it 19 | no-redirects: false 20 | # the location of a certificate you wish the proxy to use for TLS support 21 | tls-cert: 22 | # the location of a private key for TLS 23 | tls-private-key: 24 | # the public key for the ca, used for mutual TLS 25 | tls-ca-certificate: 26 | # the redirection url, essentially the site url, note: /oauth/callback is added at the end 27 | redirection-url: http://127.0.0.3000 28 | # the encryption key used to encode the session state 29 | encryption-key: vGcLt8ZUdPX5fXhtLZaPHZkGWHZrT6T8xKHWf5RPfqAocuiQ6nUbNHyc3oF2toO2tr 30 | # the name of the access cookie, defaults to kc-access 31 | cookie-access-name: 32 | # the name of the refresh cookie, default to kc-state 33 | cookie-refresh-name: 34 | # the upstream endpoint which we should proxy request 35 | upstream-url: http://127.0.0.1:80 36 | # upstream-keepalives specified wheather you want keepalive on the upstream endpoint 37 | upstream-keepalives: true 38 | # skip the tls verification of the upstream url 39 | skip-upstream-tls-verify: true|false 40 | # additional scopes to add to add to the default (openid+email+profile) 41 | scopes: [] 42 | # enables a more extra secuirty features 43 | enable-security-filter: true 44 | # headers permits you to inject custom headers into all requests 45 | headers: 46 | myheader_name: my_header_value 47 | # a map of claims that MUST exist in the token presented and the value is it MUST match 48 | # So for example, you could match the audience or the issuer or some custom attribute 49 | match-claims: 50 | aud: openvpn 51 | iss: https://keycloak.example.com/auth/realms/commons 52 | # a list of claims to inject into the authentication headers i.e. given_name -> X-Auth-Given-Name 53 | add-claims: 54 | - given_name 55 | - family_name 56 | - name 57 | # a collection of resource i.e. urls that you wish to protect 58 | resources: 59 | - uri: /admin/test* 60 | # the methods on this url that should be protected, if missing, we assuming all 61 | methods: 62 | - GET 63 | # a list of roles the user must have in order to accces urls under the above 64 | roles: 65 | - openvpn:vpn-test 66 | - uri: /admin/white_listed 67 | # permits a url prefix through, bypassing the admission controls 68 | white-listed: true 69 | - uri: /admin/* 70 | methods: 71 | - GET 72 | roles: 73 | - openvpn:vpn-user 74 | - openvpn:prod-vpn 75 | 76 | # an array of origins (Access-Control-Allow-Origin) 77 | cors-origins: [] 78 | # an array of headers to apply (Access-Control-Allow-Headers) 79 | cors-headers: [] 80 | # an array of expose headers (Access-Control-Expose-Headers) 81 | cors-exposed-headers: [] 82 | # an array of methods (Access-Control-Allow-Methods) 83 | cors-methods: [] 84 | # the credentials flag (Access-Control-Allow-Credentials) 85 | cors-credentials: true|false 86 | # the max age (Access-Control-Max-Age) 87 | cors-max-age: 1h 88 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Keycloak and Traefik Tutorial 2 | 3 | This repo is made based on https://github.com/ibuetler/docker-keycloak-traefik-workshop 4 | 5 | For the sake of this tutorial I have chosen the whoami Docker image we want to add authentication using Keycloak. The whoami application is a tiny Go webserver that prints os information and HTTP request to output. The whoami sample application is not asking for a username and password. You can grab the whoami docker from https://hub.docker.com/r/containous/whoami . The whoami web port is listening on port `80`. 6 | 7 | ## Test Running Application 8 | 9 | ```sh 10 | docker pull containous/whoami 11 | docker run --rm -i -p 80:80 containous/whoami 12 | CTRL+C will stop the docker 13 | ``` 14 | 15 | See the screenshot below how to pull and run and test whoami 16 | 17 | ![whoami1](images/whoami.png) 18 | 19 | Once you're good, please stop the docker in the same terminal you have executed "docker run..." by pressing CTRL-C. This will shutdown the whoami docker service. It must be shutdown for the next steps. 20 | 21 | ## Step 1: Add three hosts into /etc/hosts 22 | 23 | Before you can test the traefik and whoami daemon, you must add three host entries into the /etc/hosts file. This is, because we do the demo without real DNS names. 24 | 25 | ```sh 26 | echo "127.0.0.1 service1.lab.com" >> /etc/hosts 27 | echo "127.0.0.1 auth.lab.com" >> /etc/hosts 28 | echo "127.0.0.1 traefik.lab.com" >> /etc/hosts 29 | ``` 30 | 31 | ## Step 2: Run via Docker Compose 32 | 33 | This command will run Traefik, Keycloak (and it's database), Keycloak-gateway, and Whoami service. 34 | 35 | ```sh 36 | docker-compose up -d 37 | ``` 38 | 39 | ## Step 3: Keycloak Setup 40 | 41 | For the sake of this tutorial I use keycloak, an open-source identity provider `IdP` that runs smoothly with docker. If you don’t know keycloak, I encourage you to get into this project. It is the open source version of the RedHat RH-SSO solution. 42 | 43 | Afterwards, you should be able to use Firefox to reach your newly created IdP. 44 | 45 | * https://auth.lab.com/ 46 | 47 | Traefik is issuing another self-signed TLS certificate. 48 | 49 | ![ktls](images/ktls.png) 50 | 51 | Please proceed again and you should see the IdP login prompt. 52 | 53 | ![keycloakauth](images/keycloakauth.png) 54 | 55 | ```txt 56 | username: admin 57 | password: password 58 | ``` 59 | 60 | ![keycloaklogin](images/keycloaklogin.png) 61 | 62 | And voilà, your keycloak IdP should be up and working 63 | 64 | ![keycloakok](images/keycloakok.png) 65 | 66 | ### Create New Realm in Keycloak 67 | 68 | Follow steps below: 69 | 70 | 1. Click "Add realm" button on the top left of the admin dashboard. Create a new realm with this data: 71 | * Name = `demo-realm` 72 | 1. Click "Login" tab, then configure this value: 73 | * User registration = `ON` 74 | 1. Click "Create" 75 | 76 | ### Create New Client in Keycloak 77 | 78 | Follow steps below: 79 | 80 | 1. Click on "Clients" in the left menu 81 | 1. Click on "Create", then configure these values: 82 | * Client ID = `demo-client` 83 | 1. Click "Save" 84 | 1. Edit this field: 85 | * Access Type = `confidential` 86 | * Valid Redirect URIs = `https://service1.lab.com/*` 87 | 1. After saving, please click the "Credentials" menu item where you will find the secret we need for keycloak-gatekeeper. Copy the Secret as you need it later when configuring `keycloak-gatekeeper` 88 | 89 | ### Create Client Audience and Scope 90 | 91 | With the new Keycloak software, a user must be assigned to a valid audience and scope before he or she can use a keycloak enabled service. Thus, let's configure the audience and scope. 92 | 93 | Follow steps below: 94 | 95 | 1. Click on "Client Scopes" in the left menu and press "Create" 96 | 1. Use this data: 97 | * Name = `demo-scopes` 98 | 1. Click "save" 99 | 1. Click on "Mappers" tab and click "Create" button. Please configure the mapper the same as in the list below. 100 | * Name = `demo-client-mapper` 101 | * Mapper Type = `Audience` 102 | * Included Client Audience = `demo-client` 103 | * Add to ID token = `ON` 104 | * Add to access token = `ON` 105 | 106 | ### Apply Mapper to Client 107 | 108 | Last, you must apply the newly created mapper to your `demo-client` client configuration. 109 | 110 | Follow steps below: 111 | 112 | 1. Click on "Clients" in the left menu 113 | 1. Click "Edit" button next to `demo-client` 114 | 1. Click "Client Scopes" tab 115 | 1. On "Default Client Scopes" section, select `demo-scopes` on "Available Client Scopes" list and click "Add selected" button so that it will move to "Assigned Default Client Scopes" list 116 | 117 | Now you have successfully finished the keycloak configuration for the new client application. 118 | 119 | ### Testing User Self-Registration 120 | 121 | Please start in Firefox a "New Private Window" and connect to the following URL 122 | 123 | https://auth.lab.com/auth/realms/demo-realm/account 124 | 125 | 126 | ![kc11](images/kc11.png) 127 | 128 | Please register a new account 129 | 130 | ![kc12](images/kc12.png) 131 | 132 | Enter your data here 133 | 134 | ![kc13](images/kc13.png) 135 | 136 | Use your Firefox instance where you are logged-in as `admin` and check if the user has been created. 137 | 138 | PS: you can setup the user directly within keycloak, if you want. This steps were more to say: "hey, users can self-register in keycloak" 139 | 140 | ![kc14](images/kc14.png) 141 | 142 | ## Step 4: Keycloak Gatekeeper Setup 143 | 144 | ### Configure Client Secret 145 | 146 | Please configure your keycloak-gatekeeper with your client secret. 147 | 148 | ```sh 149 | nano keycloak-gatekeeper.conf 150 | ``` 151 | 152 | Please specify: 153 | 154 | * discovery-url: https://auth.lab.com/auth/realms/demo-realm 155 | * client-id: `demo-client` 156 | * client-secret: `` 157 | * redirection-url: https://service1.lab.com 158 | * upstream-url: http://service_1:80/ 159 | 160 | If you don't remember, the client secret comes from the client configuration tab. Copy your value from there. 161 | 162 | ## Step 5: Refresh `keycloak-gatekeeper` Service 163 | 164 | The `keycloak-gatekeeper` needs to read new configuration file. So, we have to restart the service using this command 165 | 166 | ```sh 167 | docker-compose restart keycloak-gatekeeper 168 | ``` 169 | 170 | ## Step 6: Testing whoami via traefik 171 | 172 | Ok, then let's see how it works using the browser. 173 | 174 | Please open Firefox and point your browser to https://service1.lab.com 175 | 176 | Follow registration step and then login using the new username and password. 177 | 178 | You can see the `whoami` service is running and also displaying the token information. 179 | 180 | ![whoami_authenticated](images/whoami_authenticated.png) 181 | 182 | ## Step 7: Authenticate Using API Endpoint 183 | 184 | Use Postman and make this request 185 | 186 | ```curl 187 | curl -X POST \ 188 | https://auth.lab.com/auth/realms/demo-realm/protocol/openid-connect/token \ 189 | -H 'Accept: */*' \ 190 | -H 'Accept-Encoding: gzip, deflate' \ 191 | -H 'Cache-Control: no-cache' \ 192 | -H 'Connection: keep-alive' \ 193 | -H 'Content-Length: 127' \ 194 | -H 'Content-Type: application/x-www-form-urlencoded' \ 195 | -H 'Host: auth.lab.com' \ 196 | -H 'Postman-Token: 24099870-cba6-41a8-8d85-dde5408faa2b,963f8ac0-23e1-430d-b69a-aadf452959bb' \ 197 | -H 'User-Agent: PostmanRuntime/7.19.0' \ 198 | -H 'cache-control: no-cache' \ 199 | -d 'client_id=demo-client&grant_type=password&username=asatrya&password=password&client_secret=2a3d0e8d-d605-49ce-b65d-c244399d15e3' 200 | ``` 201 | 202 | You will get `access_token` as the response. Copy this token as you will need in next steps. 203 | 204 | ## Step 8: Access Service Using Endpoint API 205 | 206 | Use Postman to make this request: 207 | 208 | ```curl 209 | curl -X GET \ 210 | https://service1.lab.com \ 211 | -H 'Accept: */*' \ 212 | -H 'Accept-Encoding: gzip, deflate' \ 213 | -H 'Authorization: Bearer YOUR-ACCESS-TOKEN' \ 214 | -H 'Cache-Control: no-cache' \ 215 | -H 'Connection: keep-alive' \ 216 | -H 'Host: service1.lab.com' \ 217 | -H 'Postman-Token: e77b0a50-42df-4503-9d34-34262f1bc61d,6547e1d8-4af4-455f-a72d-dcbe3ae6c5b7' \ 218 | -H 'User-Agent: PostmanRuntime/7.19.0' \ 219 | -H 'cache-control: no-cache' 220 | ``` 221 | 222 | ## Debugging Notes 223 | 224 | ### Accees to Keycloak Dabatabse 225 | 226 | Use this credentials to access 227 | 228 | * Host: `auth.lab.com` 229 | * Port: 5432 230 | * Database: `keycloak` 231 | * User: `keycloak` 232 | * Password: `password` 233 | 234 | ## THE END 235 | 236 | After this tutorial you should have an application (whoami) that comes without authentication and authorization secured using traefik, keycloak and keycloak-gateeeper. The steps taken in this tutorial hopefully have guided you to the end - to a working setup. 237 | --------------------------------------------------------------------------------