Greenion is a Proof of Concept (PoC) of a VDI solution based off of the sanzu remote desktop program. It consists of a web application where users can list and remote into their authorized machines, and client/server agents that carry the remote desktop part of the solution.
For greenion to work, you must deploy the web application and install two pieces of software : GreenionClient on the client computer and GreenionServer on the server. GreenionClient and GreenionServer support Windows and Linux.
In this guide, we will walk through the necessary steps to have a running development environment. It will only cover Linux.
Configuration steps rely on curl and jq. Install them with apt:
apt install curl jqFirst install latest LTS version of node.js (including npm) by following steps from nodesource/distributions.
Then install docker and docker-compose. You may use the following docs to do so:
You then need to specify the address the users will use to access the web application.
In a local network, this may be the IP address of the server (e.g "192.168.1.43") or it's domain name if you have a DNS redirection setup. In this example, we will assume that the web server is reachable using the server.local domain name.
- Export the following environment variable
export DOMAIN="server.local"- Replace all mentions of
greenion.localindocker-resources/kratos/kratos.ymlbyserver.local - Replace all mentions of
greenion.localindocker-resources/env.sample.appbyserver.local - Replace all mentions of
greenion.localindocker-resources/env.sample.commonbyserver.local
From now on, you can either setup the project manually or use an automated script that sets everything up for you.
In the root of this repo, run the following script :
./scripts/setup-dev.shAfter the setup, the webapp should be available at http://server.local:5001/.
npm ci
sudo npm i -g pino-pretty yarnConfiguring the project will rely heavily on running npm scripts provided by the package.json file.
- Run this script to copy and paste all environment sample files.
npm run config:copy:allYou should see environmment files for hydra, kratos, web app, auth app, catalog api and monorepo being copied.
- Now we will generate the root CA private key and the root CA certificate. Those file are necessary so rest-auth can sign certificates for machines. Run:
npm run ca:createIt should output two files: ./rest-auth/certs/rootCA.key.pem (root CA private key) and ./rest-auth/certs/rootCA.crt.pem (root CA certificate).
- To continue, we need to have hydra started. Run this script to only start ory hydra:
npm run debug:hydra- To have authentification and authorization working, we need to create a oauth2 client for our web app. Let's do this by running:
npm run hydra:oauth2:create-clientAs a result, you should see something like this:
CLIENT ID d0e844b7-c594-458e-a40d-612aa7dad3fe
CLIENT SECRET dM~45Upt~UDsxNL8Hv5cxlHaNP
GRANT TYPES authorization_code, refresh_token, client_credentials
RESPONSE TYPES code
SCOPE openid profile email offline admin
AUDIENCE rest-app,rest-catalog
REDIRECT URIS http://127.0.0.1:5001/callback- Open
docker-resources/.env.appfile. Copy value ofCLIENT_ID(of previous step) toHYDRA_CLIENT_IDand valueCLIENT_SECRET(of previous step) toHYDRA_CLIENT_SECRET. With previous result, your env file would contain:
[...]
HYDRA_CLIENT_ID=d0e844b7-c594-458e-a40d-612aa7dad3fe
HYDRA_CLIENT_SECRET=dM~45Upt~UDsxNL8Hv5cxlHaNP
[...]- Now we will generate a new json web key set (JWKS). We do this because we want to have a key dedicated to sign jwt aim to be used as vdi session. Run:
npm run hydra:jwks:create-session-vdiIt should create a new file named jwks.json in ./rest-auth/src/config/ folder.
- Now we need to get the key identifier of the generated jwks. We will copy this value in
.env.commonfile becauserest-authandrest-catalogneed to know this variable.rest-authneeds it to choice the correct JSON Web Key to sign jwt session vdi andrest-catalogneeds it to allow authentification from client agent. Run:
cat ./rest-auth/src/config/jwks.json | jq -r '.keys[0].kid'it should output the kid. For example:
$ cat ./rest-auth/src/config/jwks.json | jq -r '.keys[0].kid'
f7ff5ffc-ae6e-4fe2-8dc0-0df7ce65a3f7- Open
docker-resources/.env.commonand fillHYDRA_JWKS_SESSION_VDI_KIDwith the previous value. With the previous example, it would look like this:
[...]
HYDRA_JWKS_SESSION_VDI_KID=f7ff5ffc-ae6e-4fe2-8dc0-0df7ce65a3f7
[...]- Now that we have updated all the needed environment variable we can start the full docker compose. Stop hydra first with:
docker stop greenion-hydra-1- Start the docker compose with all the service using this npm script:
npm run debug- To have a working solution, we still need to create an admin user. Run the following script to create one. It will create an admin user in ory kratos (the identity provider) and in api catalog (user from kratos are synced in this api).
npm run greenion:create-user -- -u admin@example.com -p admin123 -r adminA new admin user with email admin@example.com and password admin123 should have been created
-
Head over web app to be redirected to
auth appto login as an admin (use the credentials previously cited) -
If you need to perform administrative actions go to rest-app openapi or rest-catalog openapi.
Now that the web application is up, you can add users and machines by running the commands specified in the section named "Guide" of this README document.
For remote desktop to work, you must also install the server and client agents to your server(s) and client(s). For this, please use the installers that are available in the "Releases" tab in github.
Please read here in order to configure the agents properly.
When deploying using the production setup, the setup differs for the most part from development by building apps before serving them.
Configuration steps rely on curl and jq. Install them with apt:
apt install curl jqAs before, export the following environment variable
export DOMAIN="server.local"- Replace all mentions of
greenion.localindocker-resources/kratos/kratos.ymlbyserver.local - Replace all mentions of
greenion.localindocker-resources/env.sample.appbyserver.local - Replace all mentions of
greenion.localindocker-resources/env.sample.commonbyserver.local
First install the latest LTS version of node.js (including npm) by following steps from nodesource/distributions.
Then install docker and docker-compose by following related docs:
npm cisudo npm i -g pino-pretty yarnConfiguring the project will rely heavily on running npm scripts provided by the package.json file.
- Run this script to copy and paste all environment sample files.
npm run config:copy:allYou should see environmment files for hydra, kratos, web app, auth app, catalog api and monorepo being copied.
- Now we will generate the root CA private key and the root CA certificate. Those file are necessary so rest-auth can sign certificates for machines. Run:
npm run ca:createIt should output two files: ./rest-auth/certs/rootCA.key.pem (root CA private key) and ./rest-auth/certs/rootCA.crt.pem (root CA certificate).
-
The root CA certificate needs to be referenced by the client agent. To configure it, go over this documentation
-
To continue, we need to have hydra started. Run this script to only start ory hydra:
npm run prod:hydra- To have authentification and authorization working, we need to create a oauth2 client for our web app. Let's do this by running:
npm run hydra:oauth2:create-clientAs a result, you should see something like this:
CLIENT ID d0e844b7-c594-458e-a40d-612aa7dad3fe
CLIENT SECRET dM~45Upt~UDsxNL8Hv5cxlHaNP
GRANT TYPES authorization_code, refresh_token, client_credentials
RESPONSE TYPES code
SCOPE openid profile email offline admin
AUDIENCE rest-app,rest-catalog
REDIRECT URIS http://127.0.0.1:5001/callback- Open
docker-resources/.env.appfile. Copy value ofCLIENT_ID(of previous step) toHYDRA_CLIENT_IDand valueCLIENT_SECRET(of previous step) toHYDRA_CLIENT_SECRET. With previous result, your env file would contain:
[...]
HYDRA_CLIENT_ID=d0e844b7-c594-458e-a40d-612aa7dad3fe
HYDRA_CLIENT_SECRET=dM~45Upt~UDsxNL8Hv5cxlHaNP
[...]7 Now we will generate a new json web key set (JWKS). We do this because we want to have a key dedicated to sign jwt aim to be used as vdi session. Run:
npm run hydra:jwks:create-session-vdiIt should create a new file named jwks.json in ./rest-auth/src/config/ folder.
- Now we need to get the key identifier of the generated jwks. We will copy this value in
.env.commonfile becauserest-authandrest-catalogneed to know this variable.rest-authneeds it to choice the correct JSON Web Key to sign jwt session vdi andrest-catalogneeds it to allow authentification from client agent. Run:
cat ./rest-auth/src/config/jwks.json | jq -r '.keys[0].kid'it should output the kid. For example:
$ cat ./rest-auth/src/config/jwks.json | jq -r '.keys[0].kid'
f7ff5ffc-ae6e-4fe2-8dc0-0df7ce65a3f7- Open
docker-resources/.env.commonand fillHYDRA_JWKS_SESSION_VDI_KIDwith the previous value. With the previous example, it would look like this:
[...]
HYDRA_JWKS_SESSION_VDI_KID=f7ff5ffc-ae6e-4fe2-8dc0-0df7ce65a3f7
[...]- Now that we have updated all the needed environment variable we can start the full docker compose. Stop hydra first with:
docker stop greenion-hydra-1- Start the docker compose with all the service using this npm script:
npm run prod- To have a working solution, we still need to create an admin user. Run the following script to create one. It will create an admin user in ory kratos (the identity provider) and in api catalog (user from kratos are synced in this api).
npm run greenion:create-user -- -u admin@example.com -p admin123 -r adminA new user with email admin@example.com and password admin123 should have been created
-
Head over web app to be redirected to
auth appto login as an admin (use the credentials previously cited) -
If you need to perform administrative actions go to rest-app openapi or rest-catalog openapi.
In the root of the repo :
# To see available options
npm run greenion:create-user
# Example :
npm run greenion:create-user -- -u admin@example.com -p admin123 -r admin- Log into webapp with admin credential
- Open openapi documentation to configure request to create an user
- Click on
Try it outbutton - Fill password, email and role (can be "user" or "admin") fields.
- Click on
Executebutton
In the root of the repo :
# To see available options
npm run greenion:create-machine
# Example :
npm run greenion:create-machine -- -n WindowsDesktop -e 192.168.3.1 -u 7671 -i 192.168.122.25 -p 9447- Log into webapp with admin credential
- Open openapi documentation to configure request to create a new machine
- Click on
Try it outbutton - Fill all the field
- Click on
Executebutton - In response, you should have a signed certificate and a private key.
- To configure the machine/server you just added, copy them to it and configure your server following these instructions
# To see available options
npm run greenion:link-machine
# Example :
npm run greenion:link-machine -- -u 1 -m 2- Log into webapp
- Open openapi documentation to allow user to access user machine
- Fill userId and machineId field
- Click on
Executebutton - If you only have the uuid of the user (meaning its kratos id) you can use this route to get id of user (internal to api catalog) based on its uuid
- Use jwt session vdi to authenticate your request
- Request this route to close a session by filling the
closedAtfield
| Name | description |
|---|---|
| HYDRA_JWKS_ACCESS_TOKEN_ALG | alg used to create jwt vdi session (default is RS256) |
| HYDRA_JWKS_SESSION_VDI_KID | Id of jwks used to create jwt vdi session |
| COOKIE_NAME | name of cookie that act as session cookie |
| COOKIE_SECRET | secret for session cookie |
| DOMAIN | domain name used to access the web application (e.g "greenion.local", "192.168.1.45") |
| Service name | url in docker | exposed url | Usage |
|---|---|---|---|
| auth | 127.0.0.1:5002 | http://127.0.0.1:5002 | Web auth app will be serve on this endpoint |
| app | 127.0.0.1:5001 | http://127.0.0.1:5001 | Web app will be serve on this endpoint |
| app | 127.0.0.1:5001/api_catalog | http://127.0.0.1:5001/api_catalog | rest-catalog is exposed on this port so it can be use directly by frontend |
| hydra (public port) | 172.17.0.1:4444 | http://172.17.0.1:5004 | As hydra sets cookies during oauth2 flow, you may need to delete them from this address if you want to purge cookies |
| hydra (admin port) | 172.17.0.1:4445 | http://172.17.0.1:4445 | Endpoints to create oauth2 clients |
| kratos (public port) | 172.17.0.1:4433 | http://172.17.0.1:4433 | Self services flows will be created using this address |
| kratos (admin port) | 172.17.0.1:4434 | http://172.17.0.1:4434 | You can create ressources administratively with this endpoint |
| mailslurper | 127.0.0.1:4436 | http://127.0.0.1:4436 | Check emails emitted by ory kratos on this endpoint (not in use) |
| kratos self service ui | 127.0.0.1:4455 | http://127.0.0.1:4455 | frontend provided by ory (may be broken) |
To interact with openapi doc, you need to retrieve the access token generated thanks to the authorization code flow.
- Go to the web app at http://127.0.0.1:5001
- You will be redirected by the browser to http://127.0.0.1:5002 with the correct query params
- Login with your credential (you can use the admin user defined during installation steps)
- Then, you are redirected to the web app.
- An access token is set in cookie
- Tap F12 to open developer console and navigate to Storage > Cookies
- Get value of the web_app_session cookies.
- This access token can be use to interact with rest-app and rest-catalog as they are defined as audience of the jwt whereas rest-auth does not check audience of the token.
(more info here). By default, hydra generates one key for access token and another one for id token:
npm run hydra:get-well-known-jwksnpm run hydra:jwks:get-access-token-kidIt calls the hydra admin endpoint with the name of the json web key set (hydra.jwt.access-token).
The url of the login page must be populated with oauth2 params. To achieve that, you need to head over to 127.0.0.1:5001 first. You will then be redirected to the login page with the correct params and should be able to login.
Here is an extract of https://gist.github.com/Hakky54/b30418b25215ad7d18f978bc0b448d81
# Check and read content of certificate
openssl x509 -noout -text -in rest-auth/certs/output/machine-123.crt.pem
# Check that certificate has been signed by the specified CA certificate
openssl verify -CAfile rest-auth/certs/rootCA.crt.pem rest-auth/certs/output/machine-123.crt.pem
# Check validity of private key
openssl rsa -in rest-auth/certs/output/machine-123.key -check
# Check that private key match certificate (outputs must be the same)
openssl x509 -noout -modulus -in rest-auth/certs/output/machine-123.crt.pem | openssl md5
openssl rsa -noout -modulus -in rest-auth/certs/output/machine-123.key | openssl md5If your encounter this error when running project on macOS, do the following
- delete
node_modulesandyarn.lockin those two folderswww-app/,www-auth/
www-auth-1 | Error: Cannot find module @rollup/rollup-linux-arm64-gnu.Acts as the identity provider. it is an open source and self hostable solution.
Go over the github page for an in depth description.
Acts as the authorization server which allows us to perform authorization code flow for login and to issue JWT for VDI session.
Go over the github page for an in depth description.
These subrepo (respectively the frontend and the backend) refers to the webapp where user can connect and starts sessions
These subrepo (respectively the frontend and the backend) refers to the application which only provide a login page and a backend which interfaces with ory products
This backend store the information about the machines added by an admin and the relationship between these machines and users (provided by ory kratos).
This project is licensed under the terms of the AGPLv3 license, but the agents installers include Sanzu, which is licensed under the GPLv3 license.
