--- tags: netgroup --- # Exam with Crownlabs and the Lockdown Browser After a few days of brainstorming, Gabriele and Marco thought of a solution to perform exams using the Crownlabs platform together with the Lockdown browser. This document is structured as follows: - [Proposed solution](#Proposed-solution) - [Motivation leading to the proposed solution](#Motivation-leading-to-the-proposed-solution) - [Minor removable gotchas](#Minor-removable-gotchas) - [TBD] Path to the curent solution and previous ideas - [VM isolation: Network Policies](#VM-isolation-Network-Policies) ## Approach adopted for vLAIBs * External authentication service, which provides one-time credentials unique per user and exam sessions (with a direct link, and password shared through the exam platform). * IP check between the one used to access the "Portale della Didattica" and the one used to access the VLAIB -> if they do not match, a report is generated for further analysis. ## Proposed solution ### Generic use case Before an exam: - setup the crownlabs cluster to expose a random URL (PROXY_URL) in the domain crownlabs.polito.it during the exam (e.g. 11gd8161gw9.crowlabs.polito.it). A new URL needs to be generated for each exam session and will be valid until the end of the exam - all students performing the exam need to have a functioning crownlabs account - for each student a corresponding VM for the exam needs to be started in crownlabs - the VM associated to the student for the exam need to be hidden from the crownlabs dashboards of a student. (i.e. running in a namespace dedicated for the exam) - for each VM associated to a student for the exam a 4 character access_code (e.g. hash(vm_url,backend_secret)) needs to be created. The code has validity only for a single exam session - the professor managing the exam will receive a CSV file with columns: studentId,vm_url,access_code - the exam should be conducted with a support virtual classroom The PROXY_URL hosts a simple webpage that functions as a proxy to let the user connect to his dedicated VM. In order to know the real identity of the user, the webpage forces the keycloak sign-in. Once a user is authenticated, there are 2 possible outcomes: - if it is the first time that the VM is accessed: - the webpage will show the access_code of the target VM and a *connect button* - the user needs to write the access_code in a answer of the lockdown browser - the user clicks the *connect button* and gets redirected to his VM - if it is NOT the first time that the VM is accessed - the webpage shows an input box and a *connect button* - the user needs to write the access code in the input box and click on the connect button - if the access_code is correct the page will redirect the user to his VM, else will show an error In case a student doesn't have the code to access the VM (for any kind of reason), he/she can contact the professor who will provide him/her with the corresponding code for the VM. ### Possible variant for exams with low number of students In case the number of students attending the exam is low, we thought of a variant that has the following: - *pros* - more secure - requires less hustle to setup - *cons* - less scalable - requires more effort from the professor part at the start of the exam The variant differs in the following ways from the generic use case: - When a student signs in the keycloak authentication on the PROXY_URL webpage, there is no distinction among the number of times the VM is accessed. The webpage will simply ask the student for the personal access_code (which has been given by the professor at the start of the exam). - At the start of the exam the professor needs to send to each student the access_code for the student's VM through the virtual classroom. ## Motivation leading to the proposed solution The proposed solution can seem a bit complex, this is done in order to go around some limitations imposed by the Lockdown Browser and to prevent some security problems that arise from theese limitations that could corrupt the validity of the exam. - The need for a proxy url is due to the fact that the Lockdown browser doesn't allow to show a url different for each student - The proxy url needs to be secret and random. - If the URL would be public, an external person A, who wants to help student B, could reach the proxy webpage and access as student B for the first time and obtain the access_code. At this point person A could freely act in the VM and the student B could ask to the professor the access_code saying there was a problem with the browser and closed the tab, so he needs to log back in. So now 2 people have access to the same VM. - if the URL would be to easy to find then someone could try to guess it using a brute-force approach. If he found it too quickly he could access the proxy webpage before the first access of the target user, falling in the issue exposed in the previous point. - The Lockdown Browser is not very stable and could behave strangely, also the student could accidentally close the VM tab. So it is not safe to assume that a user needs to access the VM only once. The presence of the access_code allows to access the VM multiple times and prevents the folowing scenario: - student A and B attend the exam in the same session. They have exchanged credentials before the start of the exam. If the access_code would not be present they could access each other's VM back and forth and gain information one from the other. - Given the strange behaviors of the Lockdown Browser it is not safe to assume that the student can write/mantain a copy of the access code of the VM. So the professor must know the codes of the students and can give them to the students through the virtual classroom, since students who use the virtual classroom are automatically authenticated/authorized by the virtual classroom system ## Minor removable gotchas - The following issue persists in every exam conducted using Crownlabs and is independent from the platform being used. VMs are `instance` custom resources inside a kubernetes cluster so anyone who has admin permissions (or permissions to get/list/watch said resources) can view the fields of the resource and know the URL and the student corresponding to the VM. At the moment also all users with `manager` access to one course are authorized to list all instances, regardless of the course they belong to. It means that person A who has admin or manager permissions and knows the credentials of student B could access the VM during the exam from anywhere. - the PROXY_URL should be long enough to prevent brute-force discovery (i.e. provide a guarantee similar to the one of the VM URLs). The exam will probably last around an hour, so if the URL is too short there could enough time to _break it_. To further prevent this after a certain amount of time, all VMs, regardless if it is the first time they are being access, could require the access_code. By adding this, professor should encourage students to access the VMs as soon as possible, just to get the access_code. If some students fail to do it, they can still get it from the professor. - Needs to be investigated how to deliver the exam (NFS attached to the VMs with the correct permissions?). ## Path to the curent solution and previous ideas TBD ## VM isolation: Network Policies Currently, CrownLabs environments have full access to the Internet, while some isolation is in place only to prevent VMs of one tenant from accessing the ones of a different tenant. Although this model is optimal for the laboratories use-case, it does not fit well the requirements of an exam. Clearly, a much more restricted configuration is required. The configuration proposed for the exam involves the following (notice that network policies in k8s operate in white-listing mode only): * All the egress traffic originating from the VMs is blocked * All the ingress traffic directing to the VMs is blocked but: * The one coming from the ingress controller, to allow connecting to the VM itself; * The one coming from the `instance-operator`, as it is required to *"declare"* a VM ready. An example of the network policies that may be used to implement this configuration is the following (still to be tested thoroughly, but a slightly more relaxed configuration seemed to work ok): ```yaml= # Block all ingress and egress traffic kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: name: crownlabs-exam-deny-all namespace: crownlabs-exam-... spec: policyTypes: - Ingress - Egress --- # Allow the egress traffic required by oauth2-proxy to work kind: NetworkPolicy apiVersion: networking.k8s.io/v1 metadata: name: crownlabs-exam-allow-oauth2-proxy namespace: crownlabs-exam-... spec: podSelector: matchLabels: app.kubernetes.io/component: oauth2-proxy policyTypes: - Egress egress: - to: - namespaceSelector: matchLabels: app.kubernetes.io/name: "ingress-nginx" - namespaceSelector: matchLabels: name: kube-system podSelector: matchLabels: k8s-app: kube-dns --- # Allow the ingress traffic from the ingress controller and the instance-operator apiVersion: networking.k8s.io/v1 kind: NetworkPolicy metadata: name: crownlabs-allow-trusted-ingress-traffic namespace: crownlabs-exam-... spec: policyTypes: - Ingress ingress: - from: - namespaceSelector: matchLabels: app.kubernetes.io/name: "ingress-nginx" - namespaceSelector: matchLabels: name: crownlabs-production podSelector: matchLabels: app.kubernetes.io/name: "instance-operator" ```