deploy the abcdesktop demo platform on GCP
Requirements
kubectlcommand line- a Google cloud account
gcloudcommand line interface gcloud cli
Create a Kubernetes cluster
This documentation describes how to create the https://demo.gcp.abcdesktop.com service on Google Cloud plateform
Prerequisites
- set up a VPC
- create 2 internal ranges, one for the subnet and one for the pods, before creating your cluster's subnet. See this documentation for more information
- create a NAT gateway for the region you will deploy your cluster, check Cloud NAT documentation for more inforation
Configure gcloud cli
First you will need to authenticate to your GCP account though the following command.
gcloud auth login
Then you will need to configure gcloud to set your GCP project as current project by running the following command.
gcloud config set project <YOUR_PROJECT_ID>
Deploying the cluster with gcloud cli
After that you can deploy your Kubenetes cluster by running the following command.
gcloud beta container \
--project \
"ino-abcdesktop-prd" clusters create "abcdesktop-demo-cluster" \
--region \
"europe-west9" \
--no-enable-basic-auth \
--cluster-version \
"1.35.3-gke.1389000" \
--release-channel \
"regular" \
--machine-type \
"n4-standard-4" \
--image-type \
"COS_CONTAINERD" \
--disk-type \
"hyperdisk-balanced" \
--disk-size \
"100" \
--metadata \
disable-legacy-endpoints=true \
--service-account \
"default" \
--scopes \
"https://www.googleapis.com/auth/devstorage.read_only","https://www.googleapis.com/auth/logging.write","https://www.googleapis.com/auth/monitoring","https://www.googleapis.com/auth/service.management.readonly","https://www.googleapis.com/auth/servicecontrol","https://www.googleapis.com/auth/trace.append" \
--max-pods-per-node \
"110" \
--num-nodes \
"6" \
--logging=SYSTEM,WORKLOAD \
--monitoring=SYSTEM,STORAGE,HPA,POD,DAEMONSET,DEPLOYMENT,STATEFULSET,CADVISOR,KUBELET,DCGM,JOBSET \
--enable-private-nodes \
--enable-ip-alias \
--network \
"projects/ino-abcdesktop-prd/global/networks/vpc-demo-abcdesktop-network" \
--subnetwork \
"projects/ino-abcdesktop-prd/regions/europe-west9/subnetworks/gke-abcdesktop-demo-cluster-subnet-a91bbd71" \
--enable-intra-node-visibility \
--default-max-pods-per-node \
"110" \
--enable-autoscaling \
--min-nodes \
"0" \
--max-nodes \
"6" \
--location-policy \
"BALANCED" \
--enable-ip-access \
--enable-authorized-networks-on-private-endpoint \
--security-posture=standard \
--workload-vulnerability-scanning=disabled \
--enable-dataplane-v2 \
--enable-dataplane-v2-metrics \
--enable-dataplane-v2-flow-observability \
--no-enable-google-cloud-access \
--addons \
HorizontalPodAutoscaling,HttpLoadBalancing,NodeLocalDNS,GcePersistentDiskCsiDriver \
--enable-autoupgrade \
--enable-autorepair \
--max-surge-upgrade \
1 \
--max-unavailable-upgrade \
0 \
--binauthz-evaluation-mode=DISABLED \
--enable-managed-prometheus \
--enable-shielded-nodes \
--shielded-integrity-monitoring \
--shielded-secure-boot \
--node-locations \
"europe-west9-b","europe-west9-a","europe-west9-c"
Note
The configurations in the command above are the recommended ones but you can change those if you need. Just make sure to have : - secure boot enabled - private nodes enabled - dataplane V2 enabled
Finally click on the Connect button and paste the given command into your terminal
Run the abcdesktop install script
Download and install the latest release automatically
curl -sL https://raw.githubusercontent.com/abcdesktopio/conf/main/kubernetes/install-4.4.sh | bash
To get more details about the install process, please read the Setup guide
Expose your service
Now you can expose publicly your abcdesktop if you want. To proceed please follow the dedicated chapter here
Apply network policies
To reinforce security inside your cluster, you can apply abcdesktop network policies by running the following command.
kubectl apply -f https://raw.githubusercontent.com/abcdesktopio/conf/main/kubernetes/netpol-default-4.4.yaml -n abcdesktop
If you want more information about abcdesktop network policies, please refer to our netpol documentation
Add a garbage collector
In order to avoid your cluster being full to capacity, you can add a garbage collector to remove unused pods after 15 minutes.
Please follow the dedicated chapter on garbage collector configuration
Add a PersistenVolumeClaim for mongo
This measure prevents your platform to delete all desktop applications in case of mongo pods restart.
First you need to update mongo StatefulSet to add a PersistenVolumeClaim in abcdesktop.yaml file :
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: mongodb-od
[........]
# --- Non-persistent volume for MongoDB data
- name: data
persistentVolumeClaim:
claimName: pvc-mongo
Then create a PersistentVolumeClaim file, let's call it pvc-mongo.yaml :
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: pvc-mongo
spec:
storageClassName: dynamic-rwo
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 16Gi
Finally apply it to the cluster
NAMESPACE=abcdesktop
kubectl create -f pvc-mongo.yaml -n $NAMSPACE
Add external providers
You may want users to properly authenticate to your demo platform. To proceed, you will need to remove default LDAP authentication and anonymous authentication.
Edit od.config file and go to the AUTH SECTION and empty all dictionaries inside authmanager.
authmanagers: {
'external': {},
'metaexplicit': {},
'explicit': {},
'implicit': {} }
Now you cannot authenticate anymore, so you need to add external providers using Oauth2.0 protocol. To proceed, please follow our documentation on how to add external authentication providers to abcdesktop
od.config example file
# od.config
# abcdesktop Configuration File
#
# This file is a cherrypy config file
# Global config is stored in the cherrypy.config dict
# Syntax must be Python builtin ConfigParser
#
# To create your own config file :
# update this file, then
# run the kubectl create configmap command :
#
# to delete previous abcdesktop-config configmap
# kubectl delete configmap abcdesktop-config -n abcdesktop
#
# to create a abcdesktop-config configmap
# kubectl create configmap abcdesktop-config --from-file=od.config -n abcdesktop
#
# same commands in one line
# kubectl create -n abcdesktop configmap abcdesktop-config --from-file=od.config -o yaml --dry-run=client | kubectl replace -n abcdesktop -f -
#
# to restart pyos
# quick and dirty
# kubectl delete pods -l run=pyos-od -n abcdesktop
# or rollout
# kubectl rollout restart deployment pyos-od -n abcdesktop
#
# to detect non-ASCII characters in file
# perl -ne 'if (/[^[:ascii:]]/) { print $. . ": " . $_ }' od.config
#######
# data
[global]
# abcdesktop is the default namespace
# pyos read the environment variable POD_NAMESPACE
# in most case, you don't need to override the environment variable POD_NAMESPACE
# namespace: 'abcdesktop'
# DEFAULT HOST URL
# public host url of the service
# change this with your URL or
# set the external URL service if you use a reverse proxy
# default_host_url : 'https://external.domain.org'
default_host_url : 'http://localhost'
# END OF DEFAULT HOST URL
# THREAD_POOL
# the default server.thread_pool is 10
# increase this value to add more cherrypy threads
# server.thread_pool: 16
# END OF THREAD_POOL
# WEBSOCKETROUTING
# describe which url is returned by od.py to reach the WebSocket server
# the more secured value is default_host_url
# websocketrouting: permit value are ['bridge', 'default_host_url', 'host','http_origin']
# websocketrouting describe how the web browser can establish web socket to the user container
#
# the default websocketrouting value is http_origin
# default_host_url : the default_host_url value is used as the wss or ws connect
# host : use the hostname in the requested url
# http_origin : use the hostname set in the recievied http Header request
# this is less secure than default_host_url
# but it always works
# bridge : use if the user's container need to bridge the host's ethernet interface
# bridge is only used if user container can bind a local network (level 2)
# this value is experimental and is not yet avalaible
websocketrouting: 'http_origin'
# END OF WEBSOCKETROUTING
# BIND_SECTION
#
# od.py need an ip address and tcp port to listen
# ip addr to listen is set by default to 0.0.0.0
# this option is only used if you run od.py without a docker container
# this option is only used for developers
# if you run abcdesktop.io in a container,
# the common usage, keep the default value to 0.0.0.0
server.socket_host: '0.0.0.0'
# TCP PORT
# the default tcp port to listen is 8000
# this tcp port is used by nginx to forward HTTP request to od.py
# if you change the default TCP port value, you have to change it to the nginx config file
server.socket_port: 8000
#
# END OF BIND_SECTION
#
# EXTERNAL IP ADDRESS SECTION
# THIS IS NOT THE BINDING IP ADDR
# server.geolocation_ipaddr is only used to locate the external ip of the service
# server.geolocation_ipaddr is used by geoip and Active Directory site subnet queries
# the default value is a dummy value '127.0.0.1'
# change this value to help geoip to locate your service or for Active Directory site and subnet query
server.geolocation_ipaddr: '127.0.0.1'
# END OF EXTERNAL IP ADDRESS SECTION
# JWT SECTION #
#
# JWT Token for /API URL
# exp : time in seconds, None for unlimited
jwt_token_user : {
'exp': 360,
'jwtuserprivatekeyfile': '/config.usersigning/abcdesktop_jwt_user_signing_private_key.pem',
'jwtuserpublickeyfile' : '/config.usersigning/abcdesktop_jwt_user_signing_public_key.pem' }
#
# JWT RSA SIGNING ANS PAYLOAD KEYS
# od.py use two RSA keys to sign jwt and encrypt payload's jwt
# Use OpenSSL to generate the RSA Keys
#
# command to build rsa kay pairs for jwt payload
# 512 bits is a small value, change here if need
# >openssl genrsa -out abcdesktop_jwt_desktop_payload_private_key.pem 512
# >openssl rsa -in abcdesktop_jwt_desktop_payload_private_key.pem -outform PEM -pubout -out _abcdesktop_jwt_desktop_payload_public_key.pem
# >openssl rsa -pubin -in _abcdesktop_jwt_desktop_payload_public_key.pem -RSAPublicKey_out -out abcdesktop_jwt_desktop_payload_public_key.pem
#
# command build rsa kay pairs for jwt signing
# >openssl genrsa -out abcdesktop_jwt_desktop_signing_private_key.pem 1024
# >openssl rsa -in abcdesktop_jwt_desktop_signing_private_key.pem -outform PEM -pubout -out abcdesktop_jwt_desktop_signing_public_key.pem
#
# ! IMPORTANT
# ! the same key files are used by nginx
# ! you have to copy the key file to nginx container image
#
jwt_token_desktop : {
'exp': 420,
'jwtdesktopprivatekeyfile': '/config.signing/abcdesktop_jwt_desktop_signing_private_key.pem',
'jwtdesktoppublickeyfile' : '/config.signing/abcdesktop_jwt_desktop_signing_public_key.pem',
'payloaddesktoppublickeyfile' : '/config.payload/abcdesktop_jwt_desktop_payload_public_key.pem' }
# END OF JWT SECTION #
# CONTROLLERS SECTION #
#
controllers: { 'ManagerController': { 'apikey': [ 'demoabcdesktop' ], 'permitip': [ '161.105.208.3/32', '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8', '169.254.0.0/16', '127.0.0.0/8' ] },
'AccountingController': { 'apikey': [ 'demo' ], 'permitip': [ '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8', '169.254.0.0/16', '127.0.0.0/8' ] },
'StoreController': { 'wrapped_key': {} },
'ComposerController' : { 'requestsallowed' : { 'getdesktopdescription': False } },
'DesktopController' : { 'requestsallowed' : { 'dns': False }, 'permitip': [ '10.0.0.0/8', '172.16.0.0/12', '192.168.0.0/16', 'fd00::/8', '169.254.0.0/16', '127.0.0.0/8' ] } }
# END OF CONTROLLERS SECTION #
### AUTH SECTION ###
# Complete AUTH Sample dictionnary
# The authmanagers is defined as a dictionnary object :
#
#
# authmanagers: {
# 'external': { },
# 'explicit': { },
# 'implicit': { }
# }
# The od.config defines 3 kinds of entries in the authmanagers object :
# external: use for OAuth 2.0 Authentification
# explicit: use for LDAP, LDAPS and ActiveDirectory Authentification
# implicit: use for Anonymous Authentification
#
# external: use for OAuth 2.0 Authentification
# 'external': {
# 'providers': {
# 'google': {
# 'displayname': 'Google',
# 'textcolor': '#000000',
# 'backgroundcolor': '#FFFFFF',
# 'icon': 'img/auth/google_icon.svg',
# 'enabled': True,
# 'client_id': 'YYYYYY',
# 'client_secret': 'XXXXXX',
# 'scope': 'https://www.googleapis.com/auth/userinfo.email',
# 'dialog_url': 'https://accounts.google.com/o/oauth2/v2/auth?client_id={client_id}&redirect_uri={callback_url}&response_type=code&scope={scope}',
# 'auth_url': 'https://oauth2.googleapis.com/token?code={code}&grant_type=authorization_code&redirect_uri={callback_url}&scope={scope}&client_id={client_id}&client_secret={client_secret}',
# 'userinfo_url': 'https://openidconnect.googleapis.com/v1/userinfo?access_token={access_token}',
# 'callback_url': 'https://FQDN/API/auth/oauth?manager={manager.name}&provider={name}'
# }
# }
#
# explicit: use for LDAP, LDAPS and ActiveDirectory Authentification
#
# 'explicit': {
# 'show_domains': True,
# 'providers': {
# 'LDAP': {
# 'config_ref': 'ldapconfig',
# 'enabled': True
# }
# }}
# ldapconfig : { 'planet': {
# 'default' : True,
# 'ldap_timeout' : 15,
# 'ldap_protocol' : 'ldap',
# 'ldap_basedn' : 'ou=people,dc=planetexpress,dc=com',
# 'servers' : [ '192.168.1.69' ],
# 'secure' : False
# }}
#
# explicit with ActiveDirectory Authentification
# 'explicit': {
# 'show_domains': True,
# 'providers': {
# 'AD': {
# 'config_ref': 'adconfig',
# 'enabled': True
# }
# }
# adconfig : { 'AD': { 'default' : True,
# 'ldap_timeout' : 15,
# 'ldap_protocol' : 'ldap',
# 'ldap_basedn' : 'DC=ad,DC=domain,DC=local',
# 'ldap_fqdn' : '_ldap._tcp.ad.domain.local',
# 'domain' : 'AD',
# 'domain_fqdn': 'AD.DOMAIN.LOCAL',
# 'servers' : [ '192.168.7.12' ],
# 'kerberos_realm': 'AD.DOMAIN.LOCAL',
# 'query_dcs' : False
# }
# }
#
# implicit: use for Anonymous Authentification
# 'implicit': {
# 'providers': {
# 'anonymous': {
# 'displayname': 'Anonymous',
# 'caption': 'Have a look !',
# 'userid': 'anonymous',
# 'username': 'Anonymous'
# }
# }
# }
authmanagers: {
'external': {
'providers': {
'google': {
'icon': 'img/auth/google_icon.svg',
'displayname': 'Google',
'textcolor': '#000000',
'backgroundcolor': '#FFFFFF',
'enabled': True,
'client_id': 'XXXXXXXXXXXX',
'client_secret': 'YYYYYYYYYYY',
'userinfo_auth': True,
'scope': [ 'https://www.googleapis.com/auth/userinfo.email', 'openid' ],
'userinfo_url': 'https://www.googleapis.com/oauth2/v1/userinfo',
'redirect_uri_prefix' : 'https://demo.gcp.abcdesktop.com/API/auth/oauth',
'redirect_uri_querystring': 'manager=external&provider=google',
'authorization_base_url': 'https://accounts.google.com/o/oauth2/v2/auth',
'token_url': 'https://oauth2.googleapis.com/token'
},
'orange': {
'displayname': 'Orange',
'icon': 'img/auth/orange_icon.svg',
'textcolor': '#000000',
'backgroundcolor': '#FFFFFF',
'enabled': True,
'basic_auth': True,
'userinfo_auth': True,
'scope' : [ 'openid', 'form_filling' ],
'client_id': 'XXXXXXXXXXXX',
'client_secret': 'YYYYYYYYYYY',
'redirect_uri_prefix' : 'https://demo.gcp.abcdesktop.com/API/auth/oauth',
'redirect_uri_querystring': 'manager=external&provider=orange',
'authorization_base_url': 'https://api.orange.com/openidconnect/fr/v1/authorize',
'token_url': 'https://api.orange.com/openidconnect/fr/v1/token',
'userinfo_url': 'https://api.orange.com/formfilling/fr/v1/userinfo'
},
'github': {
'icon': 'img/auth/github_icon.svg',
'textcolor': '#000000',
'backgroundcolor': '#FFFFFF',
'displayname': 'Github',
'enabled': True,
'basic_auth': True,
'userinfo_auth': True,
'scope' : [ 'read:user' ],
'client_id': 'XXXXXXXXXXXX',
'client_secret': 'YYYYYYYYYYY',
'redirect_uri_prefix' : 'https://demo.gcp.abcdesktop.com/API/auth/oauth',
'redirect_uri_querystring': 'manager=external&provider=github',
'authorization_base_url': 'https://github.com/login/oauth/authorize',
'token_url': 'https://github.com/login/oauth/access_token',
'userinfo_url': 'https://api.github.com/user',
},
'linkedin': {
'displayname': 'LinkedIn',
'textcolor': 'white',
'backgroundcolor': '#527edb',
'icon': 'img/auth/linkedin.svg',
'enabled': True,
'userinfo_auth': True,
'include_client_id': True,
'scope': ['openid', 'profile', 'email'],
'client_id': 'XXXXXXXXXXXX',
'client_secret': 'YYYYYYYYYYY',
'redirect_uri_prefix': 'https://demo.gcp.abcdesktop.com/API/auth/oauth',
'redirect_uri_querystring': 'manager=external&provider=linkedin',
'authorization_base_url': 'https://www.linkedin.com/oauth/v2/authorization',
'token_url': 'https://www.linkedin.com/oauth/v2/accessToken',
'userinfo_url': 'https://api.linkedin.com/v2/userinfo'
},
'discord': {
'displayname': 'Discord',
'textcolor': 'white',
'backgroundcolor': '#6e17e7',
'icon': 'img/auth/discord.svg',
'enabled': True,
'userinfo_auth': True,
'scope': ['identify'],
'client_id': 'XXXXXXXXXXXX',
'client_secret': 'YYYYYYYYYYY',
'redirect_uri_prefix': 'https://demo.gcp.abcdesktop.com/API/auth/oauth',
'redirect_uri_querystring': 'manager=external&provider=discord',
'authorization_base_url': 'https://discord.com/oauth2/authorize',
'token_url': 'https://discord.com/api/oauth2/token',
'userinfo_url': 'https://discord.com/api/users/@me'
}
}
},
'metaexplicit': {},
'explicit': {},
'implicit': {} }
#
#'spotify': {
# 'displayname': 'Spotify',
# 'textcolor': 'black',
# 'backgroundcolor': '#32c832',
# 'icon': 'img/auth/spotify.svg',
# 'enabled': True,
# 'userinfo_auth': True,
# 'scope': ['user-read-email'],
# 'client_id': 'XXXXXXXXXXXX',
# 'client_secret': 'YYYYYYYYYYY',
# 'redirect_uri_prefix': 'https://demo.gcp.abcdesktop.com/API/auth/oauth',
# 'redirect_uri_querystring': 'manager=external&provider=spotify',
# 'authorization_base_url': 'https://accounts.spotify.com/authorize',
# 'token_url': 'https://accounts.spotify.com/api/token',
# 'userinfo_url': 'https://api.spotify.com/v1/me'
#
# 'metaexplicit': {},
# 'explicit': {
# 'show_domains': True,
# 'providers' : {
# 'planet': {
# 'config_ref': 'ldapconfig',
# 'enabled': True }
# }
# },
# 'implicit': {
# 'providers' : {
# 'anonymous': {
# 'displayname': 'Anonymous',
# 'textcolor': 'black',
# 'backgroundcolor': '#FFFFFF',
# 'icon': 'img/auth/anonymous_icon.svg',
# 'caption': 'Have a look !',
# 'userid': 'anonymous',
# 'username': 'Anonymous',
# 'policies': {
# 'acl' : { 'permit': [ 'all' ] },
# 'rules' : {
# 'rule-net-home': {
# 'conditions' : [ { 'network': '10.0.0.0/8', 'expected' : True } ],
# 'expected' : True,
# 'label' : 'tennetwork'
# }
# }
# }
# }
# } } }
# Note serviceaccount is optional
#ldapconfig : {
# 'planet': {
# 'default' : True,
# 'ldap_timeout' : 15,
# 'ldap_protocol' : 'ldap',
# 'ldap_basedn' : 'ou=people,dc=planetexpress,dc=com',
# 'servers' : [ 'openldap' ],
# 'secure' : False,
# 'serviceaccount': { 'login': 'cn=admin,dc=planetexpress,dc=com', 'password': 'GoodNewsEveryone' },
# 'policies': {
# 'acls': None,
# 'rules' : {
# 'rule-dummy' : {
# 'conditions' : [ {'boolean':True, 'expected':True } ],
# 'expected' : True,
# 'label':'labeltrue'
# },
# 'rule-ship': {
# 'conditions' : [ { 'memberOf': 'cn=ship_crew,ou=people,dc=planetexpress,dc=com', 'expected' : True } ],
# 'expected' : True,
# 'label':'shipcrew'
# },
# 'rule-test': {
# 'conditions' : [ { 'memberOf': 'cn=admin_staff,ou=people,dc=planetexpress,dc=com', 'expected' : True } ],
# 'expected' : True,
# 'label': 'adminstaff'
# }
# }
# } } }
# For external provider only
# OAuthLib params
# source https://oauthlib.readthedocs.io/en/latest/oauth2/security.html
#
# OAuthLib will raise an InsecureTransportError if you attempt to use OAuth2 over HTTP, rather than HTTPS.
# Setting this environment variable will prevent this error from being raised.
# This is mostly useful for local testing, or automated tests.
# If you set the OAUTHLIB_INSECURE_TRANSPORT environment variable, it will not check for secure HTTPS,
# and allow the OAuth dance to continue whether or not it is secure.
# Disabling this check is intended only for development, not for production
# in production, you must configure secure HTTPS to make sure communication happen securely.
OAUTHLIB_INSECURE_TRANSPORT: True
#
# Prevent "Scope has changed" warning
# requests_oauthlib library this generates a Warning, rather than an Exception
# If a user only approved read only scope but the code is now using different scope.
# when a scope mismatch occurs, user must be prompted to reauthorize via oauth.
# Fix request user authorization from a Microsoft account
# you can (and probably should) set OAUTHLIB_RELAX_TOKEN_SCOPE when running in production.
OAUTHLIB_RELAX_TOKEN_SCOPE: True
# END OF AUTH SECTION
### FAIL2BAN SECTION ###
#
fail2ban : { 'enable' : False,
'banexpireafterseconds': 600,
'failsbeforeban' : 5,
'protectednetworks' : ['192.168.1.0/24'] }
# END OF FAIL2BAN SECTION
### LOGMEIN PRELOGON SECTION ###
#
auth.logmein : { 'enable' : False,
'network_list' : ['0.0.0.0/0'],
'permit_querystring' : True,
'http_attribut' : 'ABCDESKTOPUSERCERT' }
auth.prelogin : { 'enable' : False,
'url' : 'https://FQHN/index.session.mustache.html',
'network_list' : ['0.0.0.0/0'],
'http_attribut ' : 'abcuserid',
'http_attribut_to_force_auth_prelogin': 'MUST_USE_PRELOGIN' }
# END OF LOGMEIN PRELOGON SECTION
# MEMCACHE SECTION
# memcache server
# describe how od.py can reach the memcached server
# memcacheserver is the name (FQDN) of the memcached server
# memcacheserver default value is None
# memcacheserver SHOULD BE SET TO None
# od.py build the default :
# in standalone mode the build value is 'memcached'
# in kubernetes mode the build value is 'memcached.abcdesktop.svc.cluster.local'
# change it if you need or if you have to run od.py in developer env
# memcacheserver: 'localhost'
# memcacheserver: 'memcached'
# memcacheserver: 'memcached.abcdesktop.svc.cluster.local'
#
#
# memcachedport is the tcp port of the memcached server
# the default value is 11211
# memcachedport: 11211
# END OF MEMCACHE SECTION
# MONGO SECTION
# mongodb url
# describe how od.py can reach the mongodb server
# mongodburi is the URI name of the mongodb server
# the same var name mongodbserver support connection string URI format
# read https://docs.mongodb.com/manual/reference/connection-string/#mongodb-uri
# the format is:
# mongodb://[username:password@]host1[:port1][,...hostN[:portN]][/[defaultauthdb][?options]]
# mongodburl SHOULD BE SET TO None
# od.py build the default :
# in standalone mode the build value is 'mongodb://mongodb:27017'
# in kubernetes mode the build value is 'mongodb://mongodb.abcdesktop.svc.cluster.local:27017'
# change it if you need or if you have to run od.py in developer env
# mongodburl: None
# mongodburl: 'mongodb://localhost:27017'
# mongodburl: 'mongodb://pyos:Az4MeYWUjZDg4Zjhk@mongodb.abcdesktop.svc.cluster.local:32017'
# mongodburl: 'mongodb://mongodb.abcdesktop.svc.cluster.local:32017'
# END OF MONGO SECTION
# LANGUAGE SECTION
# list of default supported language on oc.user images
# this option is used for filter request.headers.get('Accept-Language')
# user container image MUST have the supported language packages installed
# else the default fallback language is en_US
# by default abcdesktop oc.user image embeded languages support [ 'German', 'English', 'French' ]
# de_* - German - GERMANY (DE) (de_DE)
# en_* - English - UNITED KINGDOM (GB) (en_GB) - UNITED STATES OF AMERICA (US) (en_US)
# fr_* - French - FRANCE (FR) (fr_FR)
# to add new supported languages update oc.user's Dockerfile commands
# repository https://github.com/abcdesktopio/oc.user
#
# RUN apt-get update && \
# apt-get install -y --no-install-recommends locales language-pack-en language-pack-fr language-pack-de && \
# locale-gen && apt-get clean && rm -rf /var/lib/apt/lists/*
#
#
language : [ 'de_AT', 'de_BE', 'de_CH', 'de_DE', 'de_LI', 'de_LU', 'en_AG', 'en_AU', 'en_BW', 'en_CA', 'en_DK', 'en_GB', 'en_HK', 'en_IE', 'en_IN', 'en_NG', 'en_NZ', 'en_PH', 'en_SG', 'en_US', 'en_ZA', 'en_ZM', 'en_ZW', 'fr_BE', 'fr_CA', 'fr_CH', 'fr_FR', 'fr_LU' ]
# END OF LANGUAGE SECTION
# WebRTC configuration
# webrtc.rtc_constraints
# webrtc.rtc_constraints: { 'video': False, 'audio': True }
# For both video and audio, its value is either a boolean or an object.
# define if 'audio' should be send using the user's microphone, the default value is True
# keep in mind that a user can refuse local microphone access
# define if 'video' should be send using the user's webcam, the default value is False
# keep in mind that a user can refuse local camera access
#
# This value is used by navigator.mediaDevices.getUserMedia(rtc_constraints)
# https://developer.mozilla.org/en-US/docs/Web/API/MediaDevices/getUserMedia
#
webrtc.rtc_constraints: { 'video': False, 'audio': True }
# END OF WebRTC SECTION
# K8S TIMEOUT
# Kubernetes timeout
# default timeout to bound a persistentVolumeClaim
K8S_BOUND_PVC_TIMEOUT_SECONDS: 60
K8S_BOUND_PVC_MAX_EVENT: 5
# default timeout to create a pod
# include time to pull users images
# this can take a while to pull all pod's user images
# 300 = 5 * 60
K8S_CREATE_POD_TIMEOUT_SECONDS: 300
# default timeout to create an ephemeral container
# include time to pull image
K8S_CREATE_EPHEMERALCONTAINER_TIMEOUT_SECONDS: 120
# END OF K8S TIMEOUT
#
# THIS IS WHERE THE RESOURCES ARE ACTUALLY DEFINED FOR THE POD DESKTOP.
# Application execute class defined
# You can add kubernetes values to this dict, values are copy to the pod's spec
executeclasses : {
'default':{
'nodeSelector':None,
'description': 'default: up to 4 CPU cores and 8Gi',
'runtimeClassName': None,
'resources':{
'requests':{'memory':"4Gi",'cpu':"1000m"},
'limits': {'memory':"8Gi",'cpu':"4000m"}
},
'containers': {
'graphical': { 'resources': { 'requests':{'memory':"256Mi",'cpu':"200m"}, 'limits': {'memory':"4Gi", 'cpu':"1000m" }} },
'sound': { 'resources': { 'requests':{'memory':"64Mi",'cpu':"200m"}, 'limits': {'memory':"256Mi", 'cpu':"400m" }} },
'filer': { 'resources': { 'requests':{'memory':"64Mi",'cpu':"200m"}, 'limits': {'memory':"256Mi", 'cpu':"300m" }} },
'printer': { 'resources': { 'requests':{'memory':"64Mi", 'cpu':"200m"}, 'limits': {'memory':"256Mi", 'cpu':"300m" }} }
}
},
'bronze':{
'nodeSelector':None,
'runtimeClassName': None,
'description': 'bronze: up to 2 CPU cores and 8Gi',
'resources':{
'requests':{'memory':"576Mi",'cpu':"220m"},
'limits': {'memory':"8Gi",'cpu':"2000m"}
}
},
'silver':{
'nodeSelector': None,
'description': 'silver: 4 CPU cores and 32Gi RAM',
'runtimeClassName': None,
'resources':{
'requests':{'memory':"2Gi",'cpu':"2000m"},
'limits':{'memory':"32Gi",'cpu':"4000m"}
}
},
'gold':{
# to give a gpu to graphical container, add 'containers' entry
'containers' : { 'graphical': { 'resources': { 'limits': { 'nvidia.com/gpu':'1' } } } },
'nodeSelector':{'nvidia.com/gpu.present': 'true'},
'description': 'gold: 4 CPU cores, 32Gi RAM and 1 GPU',
'runtimeClassName': 'nvidia',
'resources':{
'requests':{'memory':"2Gi",'cpu':"4000m"},
'limits': {'memory':"32Gi",'cpu':"4000m"}
}
},
'platinum':{
# to give a gpu to graphical container, add 'containers' entry
'containers' : { 'graphical': { 'resources': { 'limits': { 'nvidia.com/gpu':'1' } } } },
# nodeselector optional
'nodeSelector':{'nvidia.com/gpu.present': 'true'},
# this appears only on web interface
'description': 'platinum: 8 CPU cores, 128G RAM and 1 GPU',
'runtimeClassName': 'nvidia',
'resources':{
'requests':{'memory':"4Gi",'cpu':"4000m"},
'limits':{'memory':"128Gi",'cpu':"8000m"} } } }
# features_permissions
# read executeclasses and permit a user to set a dedicated class name as desktop features
# 'read' features_permissions is exposed to the frontend
# 'submit' features_permissions can be set to create a desktop
# uncomment this section to allow frontweb resources user's choice
# desktop.features_permissions : [ 'read', 'submit' ]
# choose theme
# desktop.theme: [ 'auto', 'linux', 'macosx', 'windows' ]
# if 'auto' read the http header User-Agent to set the theme value
# if None use default value from bash script, no change
desktop.theme: 'auto'
# define the pulseaudiosocketpath
# become the env var PULSE_SERVER
desktop.pulseaudiosocketpath: '/tmp/.pulse.sock'
desktop.pod : {
# default spec for all containers
# can be overwritten on dedicated container spec
# value inside mustrache like {{ uidNumber }} is replaced by context run value
# for example {{ uidNumber }} is the uid number define in ldap server
'spec' : {
'shareProcessNamespace': False,
'shareProcessMemory': True,
'securityContext': {
'supplementalGroups': [ '{{ supplementalGroups }}' ],
'runAsUser': '{{ uidNumber }}',
'runAsGroup': '{{ gidNumber }}'
},
'tolerations': []
},
'default_volumes': {
'shm': { 'name': 'shm', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '2Gi' } },
'run': { 'name': 'run', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '1Mi' } },
'tmp': { 'name': 'tmp', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8Gi' } },
'log': { 'name': 'log', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8Gi' } },
'rundbus': { 'name': 'rundbus', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8Mi' } },
'runuser': { 'name': 'runuser', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8Mi' } },
'x11socket': { 'name': 'x11socket', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '1Ki' } },
'sudoers': { 'name': 'sudoers', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '1Mi' } }
},
'default_volumes_mount': {
'shm': { 'name': 'shm', 'mountPath' : '/dev/shm' },
'run': { 'name': 'run', 'mountPath': '/var/run/desktop' },
'tmp': { 'name': 'tmp', 'mountPath': '/tmp' },
'log': { 'name': 'log', 'mountPath': '/var/log/desktop' },
'rundbus': { 'name': 'rundbus', 'mountPath': '/var/run/dbus' },
'runuser': { 'name': 'runuser', 'mountPath': '/run/user/' },
'x11socket': { 'name': 'x11socket', 'mountPath': '/tmp/.X11-unix' },
'sudoers': { 'name': 'sudoers', 'mountPath': '/etc/sudoers.d' }
},
# graphical is the main abcdesktop container
# include x11 service
'graphical' : {
'image': { 'default': 'ghcr.io/abcdesktopio/oc.user.ubuntu.sudo.24.04:4.4' },
'volumes': [ 'sudoers', 'x11socket', 'tmp', 'run', 'log', 'rundbus', 'runuser', 'shm' ],
'imagePullPolicy': 'IfNotPresent',
'enable': True,
'acl': { 'permit': [ 'all' ] },
'waitportbin' : '/composer/node/wait-port/node_modules/.bin/wait-port',
'securityContext': {
'readOnlyRootFilesystem': False,
'allowPrivilegeEscalation': True,
'supplementalGroups': [ '{{ supplementalGroups }}' ],
'runAsUser': '{{ uidNumber }}',
'runAsGroup': '{{ gidNumber }}',
'runAsNonRoot': True
},
'tcpport': 6081,
'secrets_requirement' : [ 'abcdesktop/vnc', 'abcdesktop/kerberos'],
'waitfor_services' : [ 'xserver', 'novnc', 'spawner-service', 'plasmashell' ],
'waitfor_processes' : [ 'kwin_x11', 'plasmashell' ],
'waitfor_listeningservices': [ 'graphical', 'spawner' ]
},
# spawner core service to configure desktop
# run inside graphical container
'spawner' : {
'enable': True,
'tcpport': 29786,
'waitportbin' : '/composer/node/wait-port/node_modules/.bin/wait-port',
'acl': { 'permit': [ 'all' ] }
},
# broadcast core service for notification
# run inside graphical container
'broadcast' : {
'enable': True,
'tcpport': 29784,
'acl': { 'permit': [ 'all' ] }
},
# webshell is no a container, just a service
# run inside graphical container
# usefull to debug application and troubleshooting
'webshell' : {
'enable': True,
'tcpport': 29781,
'acl': { 'permit': [ 'all' ] }
},
# container printer
# printer is a cupsd service
'printer' : {
'volumes': [ 'tmp' ],
'image': 'ghcr.io/abcdesktopio/oc.cupsd:4.4',
'imagePullPolicy': 'IfNotPresent',
'enable': False,
'tcpport': 681,
'securityContext': { 'runAsUser': 0, 'runAsGroup': 0 },
'acl': { 'permit': [ 'all' ] }
},
# allow to download file in the printer queue
# use to print file from the web browser
# printerfile is no a container, just a service
'printerfile' : {
'enable': True,
'tcpport': 29782,
'acl': { 'permit': [ 'all' ] }
},
# container filer
# filer provide upload and download files features
'filer' : {
'volumes': [ 'tmp', 'home', 'log' ],
'image': 'ghcr.io/abcdesktopio/oc.filer:4.4',
'imagePullPolicy': 'IfNotPresent',
'enable': True,
'tcpport': 29783,
'acl': { 'permit': [ 'all' ] }
},
# container sound
# sound is a pulseaudio service instance
'sound': {
'volumes': [ 'tmp', 'home', 'log', 'shm' ],
'image': 'ghcr.io/abcdesktopio/oc.pulseaudio:4.4',
'imagePullPolicy': 'IfNotPresent',
'enable': True,
'tcpport': 29788,
'acl': { 'permit': [ 'all' ] }
},
# container init
# a simple busybox to chowner and chmod of homedir
# by defaul homedir belongs to root
# replace
# 'command': [ 'sh', '-c', 'chmod 750 ~ && chown {{ uidNumber }}:{{ gidNumber }} ~' ]
# if you disable sudo command into to your user pods, and remove 'sudoers' volume
#
'init': {
'volumes': [ 'sudoers', 'tmp', 'home' ],
'image': 'busybox',
'enable': True,
'imagePullPolicy': 'IfNotPresent',
'securityContext': { 'runAsUser': 0 },
'acl': { 'permit': [ 'all' ] },
'command': [
'sh',
'-c',
'echo "$LOGNAME ALL=(ALL:ALL) ALL" > /etc/sudoers.d/$LOGNAME && \
chmod 440 /etc/sudoers.d/* && \
chown 0:0 /etc/sudoers.d/* && \
chmod 755 /etc/sudoers.d && \
chown 0:0 /etc/sudoers.d && \
chmod 750 ~ && \
chown {{ uidNumber }}:{{ gidNumber }} ~' ]
},
#
# application can run as ephemeral_container or as pod_application
# - ephemeral_container is linked to graphical_container
# - pod_application run as pod
#
'ephemeral_container': {
'enable': True,
'imagePullPolicy': 'IfNotPresent',
'volumes': [ 'sudoers', 'x11socket', 'tmp', 'run', 'log', 'rundbus', 'runuser', 'shm' ],
'acl': { 'permit': [ 'all' ] },
'securityContext': {
'supplementalGroups': [ '{{ supplementalGroups }}' ],
'readOnlyRootFilesystem': False,
'allowPrivilegeEscalation': True,
'runAsUser':'{{ uidNumber }}',
'runAsGroup':'{{ gidNumber }}'
}
},
'pod_application' : {
'enable': True,
'imagePullPolicy': 'IfNotPresent',
'volumes': [ 'sudoers', 'tmp', 'run', 'log', 'rundbus', 'runuser' ],
# 'imagePullSecrets': [ { 'name': name_of_secret } ]
'securityContext': {
'supplementalGroups': [ '{{ supplementalGroups }}' ] ,
'readOnlyRootFilesystem': False,
'allowPrivilegeEscalation': True,
'runAsUser':'{{ uidNumber }}',
'runAsGroup':'{{ gidNumber }}'
},
'tolerations': [],
'acl': {'permit':['all'] } } }
desktop.policies: { 'rules': { } }
#
# desktop.homedirectorytype define how to create user's homedirectory
#
# if desktop.homedirectorytype is set to None, the homedirectory volume is an empty dir
# { 'name': volume_home_name, 'emptyDir': {} }
#
# if desktop.homedirectorytype is set to 'hostPath', the homedirectory volume is a hostpath
# and read the desktop.hostPathRoot value
# { name':volume_home_name, 'hostPath': { 'path': desktop.hostPathRoot + '/' + subpath_name, 'type':'DirectoryOrCreate' } }
#
# if desktop.homedirectorytype is set to 'persistentVolumeClaim', the homedirectory volume is a 'persistentVolumeClaim'
# and read the desktop.persistentvolumeclaim value
# read https://www.abcdesktopio.io/3.1/config/persistentvolumes/ to get more informations about pv and pvc
#
# values can be :
# desktop.homedirectorytype: None # use a empty dir
# desktop.homedirectorytype: 'persistentVolumeClaim'
# desktop.homedirectorytype: 'hostPath'
desktop.homedirectorytype: None # use a empty dir
#
# desktop.hostPathRoot set the hostPath root directory
# desktop.hostPathRoot is read only if desktop.homedirectorytype: 'hostPath'
# the user's home directory is located in host '/tmp' directory
# desktop.hostPathRoot: '/tmp'
#
# for desktop.homedirectorytype: 'persistentVolumeClaim'
# if desktop.homedirectorytype is set to persistentVolumeClaim
# replace mystorageclass by your own storageClassName
#
# desktop.homedirectorytype: 'persistentVolumeClaim'
#
# desktop.persistentvolume: {
# 'metadata': { 'name': '{{ provider }}-{{ userid }}-{{ uuid }}' },
# 'spec': {
# 'storageClassName': 'nfs-csi',
# 'mountOptions': [
# 'nfsvers=3'
# ],
# 'capacity': {
# 'storage': '10Gi'
# },
# 'accessModes': [ 'ReadWriteOnce' ],
# 'csi': {
# 'driver': 'nfs.csi.k8s.io',
# 'readOnly': False,
# 'volumeHandle': '192.168.7.101#volume1#homedir#{{ userid }}',
# 'volumeAttributes': {
# 'server': '192.168.7.101',
# 'share': '/volume1/homedir/{{ userid }}'
# } } } }
# desktop.persistentvolumeclaim: {
# 'metadata': {
# 'name': '{{ provider }}-{{ userid }}-{{ uuid }}',
# },
# 'spec': {
# 'volumeName': '{{ provider }}-{{ userid }}-{{ uuid }}',
# 'storageClassName': 'nfs-csi',
# 'resources': {
# 'requests': {
# 'storage': '1Gi'
# }
# },
# 'accessModes': [ 'ReadWriteOnce' ] } }
#
#
# only if you need to append a path to mounthomevolume
# the defaut value is ''
# it can be appendpathtomounthomevolume: '..'
# the path is always normalized
# source code
# user_homedirectory = os.path.join( self.get_user_homedirectory(authinfo, userinfo), oc.od.settings.desktop.get('appendpathtomounthomevolume','') )
# user_homedirectory = os.path.normpath( user_homedirectory )
desktop.appendpathtomounthomevolume: ''
#
#
# Cache home directory entries
#
# To reduce I/O to the storage backend, or to prevent abcdesktop to rewrite data in '.config' user directory
# you can defined entries in the desktop.directorytomemoryemptydir option.
#
# The volumes are an an emptyDir (a temporary directory that shares a pod's lifetime) of Memory with a SizeLimit to 8Gi, by default.
# desktop.directorytomemoryemptydir: [ '.cache', '.config' ]
# desktop.directorytomemory: { 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8Gi' } }
# Mounts:
# /home/fry/.cache from cache (rw)
# /home/fry/.config from config (rw)
#
# Volumes:
# cache:
# Type: EmptyDir (a temporary directory that shares a pod's lifetime)
# Medium: Memory
# SizeLimit: 8Gi
# config:
# Type: EmptyDir (a temporary directory that shares a pod's lifetime)
# Medium: Memory
# SizeLimit: 8Gi
#
# the default value is desktop.directorytomemoryemptydir: []
#
#
# desktop.nodeselector
# This option permits to assign user pods to nodes
#
# - dict :
# description: It specifies a map of key-value pairs.
# For the pod to be eligible to run on a node, the node must have each of the indicated key-value pairs as labels (it can have additional labels as well).
# The most common usage is one key-value pair.
# On the cluster set label to node
# kubectl label node YOURNODE abcdesktoprole=worker
# for example
# desktop.nodeselector : { 'abcdesktoprole': 'worker' }
desktop.nodeselector: {}
#
# ClusterRole must be bind to pyos-service account to permit list cluster nodes
# ClusterRole is defined on https://raw.githubusercontent.com/abcdesktopio/conf/main/kubernetes/rbac-cluster.yaml
# use ClusterRoleBinding and ClusterRole
# if pyos-service account does NOT have ClusterRole image pulling is not guarantee
#
# the default service account role is RoleBinding
# set at https://raw.githubusercontent.com/abcdesktopio/conf/main/kubernetes/rbac-role.yaml
#
#
# /composer/overwrite_environment_variable_for_application.sh
# This script is hosted by the pod in the graphical container.
# overwrite_environment_variable_for_application.sh must return a list of dict
# Then a application starts ( it can be 'ephemeral_container' or 'pod_application' ),
# if desktop.overwrite_environment_variable_for_application is set to a string,
# then pyos execute the script and expect for a list of dict ( json formatted )
# ./overwrite_environment_variable_for_application.sh
# [ { "SAMPLE_ENV_VAR_NAME" : "samplevalue" } ]
# It adds or overwrite the variables to the env var list
# The defautl value for desktop.overwrite_environment_variable_for_application is None
#
# You need to enable this to share the same nvidia GPU UUID from the pods to ephemeral container
#
# desktop.overwrite_environment_variable_for_application : "/composer/overwrite_environment_variable_for_application.sh"
# Add default environment vars
# desktop.envlocal is a dictionary.
# desktop.envlocal contains a (key,value) added by default as environment variables to oc.user.
# Only static variables are defined here.
# Dynamics values are set by python code
#
desktop.envlocal: { 'WEBSOCKIFY_HEARTBEAT':'30', 'LIBOVERLAY_SCROLLBAR':'0', 'X11LISTEN':'tcp', 'XDG_RUNTIME_DIR': '/tmp/runtime', 'DISABLE_RTKIT': 'y', 'ABCDESKTOP_FORCE_OVERWRITE_PLASMA_CONFIG': 'true' }
#
# to update session timeout
# change WEBSOCKIFY_HEARTBEAT
# use 'WEBSOCKIFY_HEARTBEAT':'30'
# the new WEBSOCKIFY_HEARTBEAT the value is in second
# if you can't update the proxy-read-timeout and proxy-send-timeout
# read https://github.com/abcdesktopio/oc.pyos/issues/2 to get more details
# desktop.envlocal: { 'LIBOVERLAY_SCROLLBAR':'0', 'UBUNTU_MENUPROXY':'0', 'X11LISTEN':'tcp', 'WEBSOCKIFY_HEARTBEAT':'30' }
#
# for demo or kiosk mode
# remove all files in user's home directory
# add ['lifecycle'] = { 'preStop': { 'exec': { 'command': [ "/bin/bash", "-c", "rm -rf ~/{*,.*}" ] } }
# to the graphical container
desktop.removehomedirectory : False
# remove persistent volume default value
desktop.removepersistentvolume: False
# remove persistent volume claim default value
desktop.removepersistentvolumeclaim: False
#
# desktop default generic user like guest
#
# if auth manager can't get uid number or gid number
# This is a default posix account
# when anonymous user is selected or if the OpenID provider doesn't provide this information
#
# balloon is the default generic user name.
desktop.username : 'balloon'
# default user id of desktop.username
desktop.userid : 4096
# default group id of desktop.username
desktop.groupid : 4096
# default home directory of desktop.username
desktop.userhomedirectory : '/home/balloon'
# default password if provider is implicit or external
desktop.userpasswd : 'lmdpocpetit'
# END OF DESKTOP OPTIONS
#
# default dock config
# dock option describes which default application are show by default
# dock option is a dictionary
# 'terminal' : Terminal application
# terminal': {
# 'args': '',
# 'acl': { 'permit': [ 'all' ] },
# 'name': u'TerminalBuiltin',
# 'keyword': u'terminal,shell,bash,builtin,pantheon',
# 'launch': u'qterminal.qterminal',
# 'displayname': u'Terminal Builtin',
# 'execmode': u'builtin',
# 'cat': u'utilities,development',
# 'id': u'terminalbuiltin.d',
# 'hideindock': True,
# 'icon': u'pantheon-terminal-builtin-icons.svg'
# },
#
# 'webshell' : HTML 5, terminal application based on xterm.js
# The values are parsed by javascript front
#
dock : {
'webshell': {
'name': u'WebShell',
'acl': { 'permit': [ 'all' ] },
'keyword': u'terminal,shell,webshell,bash,cmd',
'showinview': u'dock',
'launch': u'frontendjs.webshell',
'displayname': u'Web Shell',
'execmode': u'frontendjs',
'cat': u'utilities,development',
'id': u'webshell.d',
'icon': u'webshell.svg' } }
# FRONT START OPTIONS
# welcomeinfo
# Show a welcome message to front for maintenance or whatever
# welcomeinfo: {
# 'welcome': [
# { 'notbefore': '04 Dec 2023 00:12:00 GMT',
# 'notafter': '08 Dec 2023 00:12:00 GMT',
# 'title': 'Platform scheduled maintenance',
# 'information': 'Scheduled service maintenance from 04 Dec 2023 00:12:00 GMT to 08 Dec 2023 00:12:00 GMT.'
# } ] }
welcomeinfo: {
'welcome': [
{ 'notbefore': '04 Dec 2023 00:12:00 GMT',
'notafter': '08 Dec 2050 00:12:00 GMT',
'script' : {
'async': True,
'src': 'https://www.googletagmanager.com/gtag/js?id=G-VS25TGNTRZ'
}
},
{ 'notbefore': '01 Dec 2023 00:00:00 GMT',
'notafter': '01 Dec 2050 00:12:00 GMT',
'script': {
'data': 'window.dataLayer = window.dataLayer || []; \
function gtag() { dataLayer.push(arguments); }\
gtag(\'js\', new Date());\
gtag(\'config\', \'G-VS25TGNTRZ\'); '
}
},
{ 'notbefore': '01 Dec 2023 00:12:00 GMT',
'notafter': '01 Dec 2050 00:12:00 GMT',
'title': 'Information',
'information': 'The desktops are removed after 15 minutes of use'
} ] }
# front zoom value
# set <number> values
# default is 1 render element at its normal size.
# use document.body.style.zoom non-standard zoom CSS property can be used to control the magnification level of an element.
# zoom has not been implemented by Firefox
# desktop.zoom: 1.1;
# desktop.zoom: 0.7;
desktop.zoom : 1
# front.menuconfig is a dictionary to show or hide menu entries
# at the to rignt corner
# in front js
# 'grabmouse': False,
front.menuconfig : { 'settings': True, 'appstore': True, 'screenshot':True, 'download': True, 'logout': True, 'disconnect': True }
# show a notification when image takes time to create
# like pulling, pulled,
# this does not include the top bar status, but only notifications popup
front.imagenotification : { 'ephemeral_container' : False, 'pod_application' : False }
#
# desktop.defaultbackgroundcolors
# list of string color
# example [ '#6EC6F0', '#333333' ]
# The desktop.defaultbackgroundcolors allow you to change the desktop default background color.
# The default value is a list of string
# [ '#6EC6F0', '#333333', '#666666', '#CD3C14', '#4BB4E6', '#50BE87', '#A885D8', '#FFB4E6' ]
# The desktop.defaultbackgroundcolors length can contain up to 8 entries.
desktop.defaultbackgroundcolors : [ '#6EC6F0', '#333333', '#666666', '#CD3C14', '#4BB4E6', '#50BE87', '#A885D8', '#FFB4E6' ]
# tips info
# display a custom network information page
tipsinfo : { 'networkmap': False }
# FRONT END OPTIONS
#
# GEOLOCATION Geolocation
# params used for https://developer.mozilla.org/en-US/docs/Web/API/Geolocation/getCurrentPosition
# An optional object including the following parameters:
#
# maximumAge A positive long value indicating the maximum age in milliseconds of a possible cached position that is acceptable to return. If set to 0, it means that the device cannot use a cached position and must attempt to retrieve the real current position. If set to Infinity the device must return a cached position regardless of its age. Default: 0.
# timeout A positive long value representing the maximum length of time (in milliseconds) the device is allowed to take in order to return a position. The default value is Infinity, meaning that getCurrentPosition() won't return until the position is available.
# enableHighAccuracy A boolean value that indicates the application would like to receive the best possible results. If true and if the device is able to provide a more accurate position, it will do so. Note that this can result in slower response times or increased power consumption (with a GPS chip on a mobile device for example). On the other hand, if false, the device can take the liberty to save resources by responding more quickly and/or using less power. Default: false.
# geolocation : { 'enableHighAccuracy': True, 'timeout': 5000, 'maximumAge': 0 }
# GEOLOCATION END OPTIONS
#
# LOGGING SECTION
# The logging configuration is a dictionnary object.
# The logging configuration describes where and how log message information have to been send.
# The syslog and graylog protocol messaging are supported too.
# The default features for each handlers are :
# handler Features
# stdout log message using a StreamHandler to the stream: ext://sys.stdout formatted as standard
# stderr log message using a StreamHandler to the stream: ext://sys.stderr formatted as standard
# trace log message using a RotatingFileHandler to the file logs/trace.log formatted as standard
# cherrypy_access log message using a RotatingFileHandler to the file logs/access.log formatted as access
#
# Sub modules used by od.py can log information too.
#
# Sub module Default Values
# docker.utils.config { 'level': 'INFO' },
# urllib3.connectionpool { 'level': 'ERROR'},
#
# logging configuration
# come from https://docs.python.org/3.8/library/logging.config.html
# you need to double %% to escape %
#
# graylog https://github.com/severb/graypy
# use handler class name as
# graypy.GELFUDPHandler - UDP log forwarding
# graypy.GELFTCPHandler - TCP log forwarding
# graypy.GELFTLSHandler - TCP log forwarding with TLS support
# graypy.GELFHTTPHandler - HTTP log forwarding
# graypy.GELFRabbitHandler - RabbitMQ log forwarding
#
# type socket.SOCK_DGRAM and socket.SOCK_STREAM
# socket.SOCK_STREAM <SocketKind.SOCK_STREAM: 1>
# socket.SOCK_DGRAM <SocketKind.SOCK_DGRAM: 2>
#
# Add entry in 'handlers' send log to syslog or graylog
# in 'handlers': {
# 'gelftcp': {
# 'class': 'graypy.GELFTCPHandler',
# 'filters': [ 'odcontext' ],
# 'level': 'INFO',
# 'port': '12201',
# 'host': '192.168.1.6'
# },
# 'syslog': {
# 'class': 'logging.handlers.SysLogHandler',
# 'filters': [ 'odcontext' ],
# 'level': 'INFO',
# 'formatter': 'syslog',
# 'socktype': socket.SOCK_STREAM,
# 'address': [ '192.168.1.7', 5140 ]
# },
# ... }
logging: {
"version": 1,
"disable_existing_loggers": False,
'formatters': {
'access': {
'format': '%%(message)s - user: %%(userid)s',
'datefmt': '%%Y-%%m-%%d %%H:%%M:%%S'
},
'standard': {
'format': '%%(asctime)s %%(nodename)s %%(thread)d %%(module)s [%%(levelname)-7s] %%(name)s.%%(funcName)s:%%(userid)s %%(message)s',
'datefmt': '%%Y-%%m-%%d %%H:%%M:%%S'
},
'syslog': {
'format': '%%(asctime)s %%(nodename)s %%(thread)s %%(levelname)s %%(module)s %%(process)d %%(name)s.%%(funcName)s:%%(userid)s %%(message)s',
'datefmt': '%%Y-%%m-%%d %%H:%%M:%%S'
},
'graylog': {
'format': '%%(levelname)s %%(nodename)s %%(thread)s %%(module)s %%(process)d %%(name)s.%%(funcName)s:%%(userid)s %%(message)s'
}
},
'filters': {
'odcontext': {
'()': 'oc.logging.OdContextFilter'
}
},
'handlers': {
'stdout': {
'class': 'logging.StreamHandler',
'filters': [ 'odcontext' ],
'level': 'DEBUG',
'formatter': 'standard',
'stream': 'ext://sys.stdout'
},
'stderr': {
'class': 'logging.StreamHandler',
'filters': [ 'odcontext' ],
'level': 'ERROR',
'formatter': 'standard',
'stream': 'ext://sys.stderr'
},
'trace': {
'class': 'logging.handlers.RotatingFileHandler',
'level': 'DEBUG',
'filters': [ 'odcontext' ],
'formatter': 'standard',
'filename': 'logs/trace.log',
'maxBytes': 10485760,
'backupCount': 20,
'encoding': 'utf8',
'mode': 'w'
},
'cherrypy_access': {
'class': 'logging.handlers.RotatingFileHandler',
'filters': [ 'odcontext' ],
'formatter': 'access',
'filename': 'logs/access.log',
'maxBytes': 10485760,
'backupCount': 20,
'encoding': 'utf8'
}
},
'loggers': {
# dedicated python modules
'urllib3.connectionpool': {
'level': 'ERROR',
},
'kubernetes': {
'handlers': [ 'stderr', 'stdout', 'trace' ],
'level': 'ERROR',
'propagate': False
},
'cherrypy.access': {
'handlers': [ 'cherrypy_access' ],
'level': 'INFO',
'propagate': False
},
'requests_oauthlib' : {
'handlers': [ 'stderr', 'stdout', 'trace' ],
'level': 'ERROR',
'propagate': False
},
'cherrypy' : {
'handlers': [ 'stderr', 'stdout', 'trace' ],
'level': 'ERROR',
}
},
'root': {
# put the level to 'DEBUG', each handler fix the level value
'level': 'DEBUG',
'handlers': [ 'stderr', 'stdout', 'trace' ]
}}
Great ! Now you have your own abcdesktop demo platform running on GCP !