# 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': { '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': {} }, '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.enable : False # by default no sound # # install a coturn server first with TLS support # /etc/turnserver.conf # # minimal configurtion file for /etc/turnserver.conf # # use TLS/SSL # lt-cred-mech # use-auth-secret # static-auth-secret=SUPERSECRETFORABCDESKTOPUSERS # # Certificate file. # # Use an absolute path or path relative to the # # configuration file. # # Use PEM file format. # cert=/etc/certs/fullchain.pem # # Private key file. # # Use an absolute path or path relative to the # # configuration file. # # Use PEM file format. # pkey=/etc/certs/privkey.pem # # in most cases, you need a stun and turn servers # # webrtc.rtc_configuration:{ 'iceServers':[ {'urls':'stun:stun.l.google.com:19302'} ] } # webrtc.coturn: { # 'coturn_static_auth_secret': 'SUPERSECRETFORABCDESKTOPUSERS', # 'ttl':3600, # 'protocol': 'turns', # 'url': 'turnserver.domain.local:3478' } # # 'coturn_static_auth_secret' is the sahred secret with coturn server # 'ttl' : ttl for auth # 'protocol': can be 'turn' or 'turns' # 'url': turn server or turns server for the web browser # # pyos will build the rtc_configuration json conf # # webrtc.rtc_configuration becomes # # { # # "iceServers": [ # # { # # "urls": "stun:stun.l.google.com:19302" # # }, # # { # # "urls": "turns:nturns.domain.local:3478", # # "username": "1703086872", # # "credential": "+BuFkb0hFf8pAoFwpp0A0UbO+1k=" # # } # # ] # # } # # 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 # # Application execute class defined # define some executeclasses name with # resources requests and limits value # then map executeclasses to application executeclasses : { 'default':{ 'description': '1 CPU cores and 2Gi', 'nodeSelector':None, 'resources':{ 'requests':{'memory':"512Mi",'cpu':"100m"},        'limits': {'memory':"2Gi",'cpu':"1000m"} } }, 'bronze':{ 'description': '1 core / 2 Gi / 0 GPU', 'nodeSelector':None, 'resources':{ 'requests':{'memory':"64Mi",'cpu':"100m"},        'limits': {'memory':"2Gi",'cpu':"1000m"} } }, 'silver':{ 'description': '2 cores / 4 Gi / 1 GPU', 'nodeSelector':None, 'resources':{ 'requests':{'memory':"64Mi",'cpu':"100m"},        'limits': {'memory':"4Gi",'cpu':"2000m", 'nvidia.com/gpu':'1' } } }, 'gold':{ 'description': '4 cores / 8 Gi / 1 GPU', 'nodeSelector':None, 'resources':{ 'requests':{'memory':"128Mi",'cpu':"100m"},        'limits':{'memory':"8Gi",'cpu':"4000m", 'nvidia.com/gpu':'1' } } } } # 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 # # desktop.features_permissions : [ 'read', 'submit' ] # # where autogenerated files: passwd shadow group gshadow # # mount localaccount secrets in desktop['secretslocalaccount'] eq: /etc/localaccount # oc.user container image and applcations use a symbolic link to # - /etc/passwd -> desktop['secretslocalaccount']/passwd # - /etc/shadow -> desktop['secretslocalaccount']/shadow # - /etc/group -> desktop['secretslocalaccount']/group # - /etc/gshadow -> desktop['secretslocalaccount']/gshadow # files are linked to desktop['secretslocalaccount'] desktop.secretslocalaccount : '/etc/localaccount' desktop.appendpathtomounthomevolume: '' 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': '512Mi' } }, 'run': { 'name': 'run', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '1M' } }, 'tmp': { 'name': 'tmp', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8Gi' } }, 'log': { 'name': 'log', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8Gi' } }, 'rundbus': { 'name': 'rundbus', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8M' } }, 'runuser': { 'name': 'runuser', 'emptyDir': { 'medium': 'Memory', 'sizeLimit': '8M' } }, 'x11socket': { 'name': 'x11socket', 'emptyDir': { 'medium': 'Memory' } }, 'pulseaudiosocket' : { 'name': 'pulseaudiosocket', 'emptyDir': { 'medium': 'Memory' } }, 'cupsdsocket': { 'name': 'cupsdsocket', 'emptyDir': { 'medium': 'Memory' } } }, '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' }, 'pulseaudiosocket': { 'name': 'pulseaudiosocket', 'mountPath': '/tmp/.pulseaudio' }, 'cupsdsocket': { 'name': 'cupsdsocket', 'mountPath': '/tmp/.cupsd' } }, # graphical is the main abcdesktop container # include x11 service 'graphical' : { 'image': { 'default': 'ghcr.io/abcdesktopio/oc.user.ubuntu.sudo.24.04:4.2' }, 'imagePullPolicy': 'Always', 'enable': True, 'acl': { 'permit': [ 'all' ] }, 'waitportbin' : '/composer/node/wait-port/node_modules/.bin/wait-port', 'resources': { 'requests': { 'memory': "256Mi", 'cpu': "100m" }, 'limits' : { 'memory': "4Gi", 'cpu': "2000m" } }, 'securityContext': { 'readOnlyRootFilesystem': False, 'allowPrivilegeEscalation': True }, 'tcpport': 6081, 'secrets_requirement' : [ 'abcdesktop/vnc', 'abcdesktop/kerberos'], 'waitfor_services' : [ 'xserver', 'novnc', 'spawner-service', 'plasmashell' ], 'waitfor_processes' : [ 'kwin_x11', 'plasmashell', 'kactivitymanagerd', 'kded5', 'kscreen_backend_launcher' ], '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' : { 'image': 'ghcr.io/abcdesktopio/oc.cupsd:4.2', 'imagePullPolicy': 'IfNotPresent', 'enable': True, 'tcpport': 681, 'securityContext': { 'runAsUser': 0, 'runAsGroup': 0 }, 'resources': { 'requests': { 'memory': "64Mi", 'cpu': "5m" }, 'limits' : { 'memory': "512Mi", 'cpu': "200m" } }, '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' : { 'image': 'ghcr.io/abcdesktopio/oc.filer:4.2', 'imagePullPolicy': 'IfNotPresent', 'enable': True, 'tcpport': 29783, 'resources': { 'requests': { 'memory': "32Mi", 'cpu': "5m" }, 'limits' : { 'memory': "256Mi",'cpu': "100m" } }, 'acl': { 'permit': [ 'all' ] } }, # container storage # storage is just a pause go get all volumes 'storage' : { 'image': 'k8s.gcr.io/pause:3.8', 'imagePullPolicy': 'IfNotPresent', 'enable': True, 'acl': { 'permit': [ 'all' ] }, 'resources': { 'requests': { 'memory': "8Mi", 'cpu': "5m" }, 'limits' : { 'memory': "32Mi", 'cpu': "5m" } } }, # container sound # sound is a pulseaudio service instance # can forword sound to webrtc gateway or as http stream # http stream can have glitch and does not proviede image and sound sync # webrtc gateway with rtp sound stream is better 'sound': { 'image': 'ghcr.io/abcdesktopio/oc.pulseaudio:4.2', 'imagePullPolicy': 'IfNotPresent', 'enable': True, 'tcpport': 29788, 'acl': { 'permit': [ 'all' ] }, 'resources': { 'requests': { 'memory': "64Mi", 'cpu': "50m" }, 'limits' : { 'memory': "256Mi", 'cpu': "500m" } } }, # container init # a simple busybox to chowner and chmod of homedir # by defaul homedir belongs to root 'init': { 'image': 'busybox', 'enable': True, 'imagePullPolicy': 'IfNotPresent', 'securityContext': { 'runAsUser': 0 }, 'acl': { 'permit': [ 'all' ] }, 'resources': { 'requests': { 'memory': "8Mi", 'cpu': "5m" }, 'limits' : { 'memory': "32Mi", 'cpu': "10m" } }, 'command': [ 'sh', '-c', 'chmod 750 ~ && chown {{ uidNumber }}:{{ gidNumber }} ~' ] }, # # container x11overlay # uncomment this section to add wartermarking on X11 display # it creates a X11 window stay on top as root # 'x11overlay': { # 'image': 'abcdesktopio/x11-overlay:main', # 'pullpolicy': 'IfNotPresent', # 'enable': True, # 'securityContext': { 'runAsUser': 0, 'runAsGroup': 0 }, # 'acl': { 'permit': [ 'all' ] }, # 'resources': { # 'requests': { 'memory': "12Mi", 'cpu': "50m" }, # 'limits' : { 'memory': "64Mi", 'cpu': '500m" } # } # }, # application can run # as ephemeral_container # ephemeral_container are binded to graphical_container # as pod_application # pod_application run as pod # 'ephemeral_container': { 'enable': True, 'acl': { 'permit': [ 'all' ] }, 'securityContext': { 'supplementalGroups': [ '{{ supplementalGroups }}' ] , 'readOnlyRootFilesystem': False, 'allowPrivilegeEscalation': True, 'runAsUser':'{{ uidNumber }}', 'runAsGroup':'{{ gidNumber }}' } }, 'pod_application' : { 'enable': True, # '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 # desktop.homedirectorytype: 'persistentVolumeClaim' # desktop.homedirectorytype: 'hostPath' # 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' ] } } # # # 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 # 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', 'UBUNTU_MENUPROXY':'0', 'X11LISTEN':'tcp', 'ABCDESKTOP_BG_COLOR': '#7fb3cf' } # # 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 # balloon is the default generic user name. # The user is created inside the oc.user container # this user MUST exist in the oc.user image # If you change this value, you have to rebuild your own oc.user file # The script oc.user in Dockerfile oc.user : # oc.user Dockerfile commands extract # ENV BUSER balloon # RUN groupadd --gid 4096 $BUSER # RUN useradd --create-home --shell /bin/bash --uid 4096 -g $BUSER --groups lpadmin,sudo $BUSER # 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' # 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.' # } ] } # front zoom value # set 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 } # # 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 # socket.SOCK_DGRAM # # 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' ] }}