├── .circleci ├── Dockerfile-coord ├── Dockerfile-scionHost ├── actions │ └── update-keys.py ├── config.yml ├── django-ci-entrypoint.sh ├── docker-compose.test-prod-db.yaml ├── docker-compose.yml ├── scripts │ ├── await-beacons.sh │ ├── check-scion-status.sh │ ├── enable-fabrid-sciond.sh │ └── ping-all.sh ├── setup │ ├── build-containers.sh │ ├── check-scion-connectivity.sh │ ├── export-logs.sh │ ├── generate-host-envs.py │ ├── init-ases.sh │ ├── init-coord.sh │ └── package-upgrade.sh └── test-prod.env ├── .dockerignore ├── .flake8 ├── .gitattributes ├── .gitignore ├── LICENSE ├── README.md ├── deploy ├── Caddyfile ├── Dockerfile-django └── django-entrypoint.sh ├── dev-requirements.in ├── dev-requirements.txt ├── docker-compose.yml ├── manage.py ├── requirements.in ├── requirements.txt ├── run └── .gitkeep ├── scionlab ├── __init__.py ├── admin.py ├── config_tar.py ├── context_processors.py ├── defines.py ├── fixtures │ ├── dev_root_ca_cert.pem │ ├── dev_root_ca_key.pem │ ├── testdata.py │ ├── testdata.yaml │ ├── testtopo.py │ ├── testuser.py │ └── testuseras.py ├── forms │ ├── attachment_conf_form.py │ ├── fields.py │ ├── login_form.py │ ├── registration_form.py │ └── user_as_form.py ├── hostfiles │ ├── README_dedicated.md │ ├── README_vm.md │ ├── Vagrantfile.tmpl │ ├── client.conf.tmpl │ ├── scionlab-config │ ├── scionlab-config.service │ └── server.conf.tmpl ├── management │ └── commands │ │ ├── check_crypto.py │ │ ├── create_link.py │ │ ├── export_zonefile.py │ │ ├── initialize_root_ca.py │ │ └── mass_email.py ├── migrations │ ├── 0001_squashed_0011_pullap.py │ ├── 0002_colibri_service_schema.py │ ├── 0003_colibri_service_data.py │ ├── 0004_remove_link_bandwidth.py │ ├── 0005_trc_pem.py │ ├── 0006_bump_version_cs_hosts.py │ ├── 0007_remove_ssh_host.py │ ├── 0008_remove_colibri.py │ ├── 0009_allhosts_dirty.py │ ├── 0010_as_fabrid_enabled.py │ └── __init__.py ├── models │ ├── core.py │ ├── pki.py │ ├── trc.py │ ├── user.py │ ├── user_as.py │ └── vpn.py ├── openvpn_config.py ├── scion │ ├── as_ids.py │ ├── certs.py │ ├── config.py │ ├── keys.py │ ├── pkicommand.py │ ├── topology.py │ └── trcs.py ├── settings │ ├── __init__.py │ ├── common.py │ ├── development.py │ └── production.py ├── static │ └── scionlab │ │ ├── images │ │ └── favicon.ico │ │ └── style.css ├── templates │ ├── 503.html │ ├── django_registration │ │ ├── activation_complete.html │ │ ├── activation_email_body.txt │ │ ├── activation_email_subject.txt │ │ ├── activation_failed.html │ │ ├── registration_confirm.html │ │ ├── registration_form.html │ │ └── registration_resend.html │ ├── registration │ │ ├── logged_out.html │ │ ├── login.html │ │ ├── password_change_done.html │ │ ├── password_change_form.html │ │ ├── password_reset_complete.html │ │ ├── password_reset_confirm.html │ │ ├── password_reset_done.html │ │ ├── password_reset_email.html │ │ ├── password_reset_form.html │ │ └── password_reset_subject.txt │ └── scionlab │ │ ├── base.html │ │ ├── home.html │ │ ├── partials │ │ ├── installation_type_accordion.html │ │ ├── installation_type_accordion_script.html │ │ ├── installation_type_pkg.html │ │ ├── installation_type_src.html │ │ ├── installation_type_vm.html │ │ └── user_as_form_script.html │ │ ├── user.html │ │ ├── user_as_add.html │ │ ├── user_as_confirm_delete.html │ │ └── user_as_details.html ├── tests │ ├── __init__.py │ ├── data │ │ ├── test_config_tar │ │ │ ├── host_1.yml │ │ │ ├── host_16.yml │ │ │ ├── host_17.yml │ │ │ ├── host_18.yml │ │ │ ├── host_19.yml │ │ │ ├── host_4.yml │ │ │ ├── user_as_17.yml │ │ │ ├── user_as_18.yml │ │ │ ├── user_as_19.yml │ │ │ ├── user_as_20.yml │ │ │ └── user_as_21.yml │ │ ├── test_scion_trcs │ │ │ ├── README.txt │ │ │ ├── ca-ff00_0_110.crt │ │ │ ├── ca-ff00_0_110.key │ │ │ ├── ca-ff00_0_210.crt │ │ │ ├── ca-ff00_0_210.key │ │ │ ├── payload-1-config.toml │ │ │ ├── payload-1-signed-regular-ff00_0_110.der │ │ │ ├── payload-1-signed-sensitive-ff00_0_110.der │ │ │ ├── payload-1.der │ │ │ ├── payload-2-config.toml │ │ │ ├── payload-2-signed-regular-ff00_0_110.der │ │ │ ├── payload-2.der │ │ │ ├── payload-3-config.toml │ │ │ ├── payload-3-signed-regular-ff00_0_210.der │ │ │ ├── payload-3-signed-sensitive-ff00_0_110.der │ │ │ ├── payload-3-signed-sensitive-ff00_0_210.der │ │ │ ├── payload-3.der │ │ │ ├── regenerate.py │ │ │ ├── root-ff00_0_110.crt │ │ │ ├── root-ff00_0_110.key │ │ │ ├── root-ff00_0_210.crt │ │ │ ├── root-ff00_0_210.key │ │ │ ├── trc-1.trc │ │ │ ├── trc-2.trc │ │ │ ├── trc-3.trc │ │ │ ├── voting-regular-ff00_0_110.crt │ │ │ ├── voting-regular-ff00_0_110.key │ │ │ ├── voting-regular-ff00_0_210.crt │ │ │ ├── voting-regular-ff00_0_210.key │ │ │ ├── voting-sensitive-ff00_0_110.crt │ │ │ ├── voting-sensitive-ff00_0_110.key │ │ │ ├── voting-sensitive-ff00_0_210.crt │ │ │ └── voting-sensitive-ff00_0_210.key │ │ ├── test_scionlab-config │ │ │ ├── good.json │ │ │ ├── missing_id.json │ │ │ ├── missing_secret.json │ │ │ ├── no_url.json │ │ │ ├── no_version.json │ │ │ └── old_version.json │ │ └── test_user_forms │ │ │ ├── invalid_forms.yaml │ │ │ └── valid_forms.yaml │ ├── test_admin.py │ ├── test_api.py │ ├── test_archive.py │ ├── test_as_ids.py │ ├── test_certificates.py │ ├── test_config_tar.py │ ├── test_hashers.py │ ├── test_maintenance_mode.py │ ├── test_migration_trc_pem.py │ ├── test_models.py │ ├── test_pki.py │ ├── test_portmap.py │ ├── test_registration.py │ ├── test_scion_trcs.py │ ├── test_scionlab_config.py │ ├── test_trc.py │ ├── test_user.py │ ├── test_user_as_models.py │ ├── test_user_as_views.py │ ├── test_vpn_certs.py │ └── utils.py ├── urls.py ├── util │ ├── __init__.py │ ├── archive.py │ ├── django.py │ ├── hashers.py │ ├── http.py │ └── portmap.py ├── views │ ├── __init__.py │ ├── api.py │ ├── registration_view.py │ ├── topology.py │ └── user_as_views.py └── wsgi.py ├── scripts ├── create-fixture-testdata.sh ├── deactivate_dormant.py ├── fed4fire-scionlab.yml ├── import_topo.py ├── init-test-db.sh ├── merge_ap_routers.py ├── port_clash_fixup.py ├── recreate-test-results.sh └── vpn_cleanup.py └── static_bin ├── Dockerfile ├── README.md ├── build.sh ├── scion-pki ├── scion-pki-darwin-amd64 └── scion-pki-linux-amd64 /.circleci/Dockerfile-coord: -------------------------------------------------------------------------------- 1 | FROM python:3.11 2 | ENV PYTHONUNBUFFERED 1 3 | 4 | RUN pip3 install appdeps 5 | 6 | RUN mkdir /scionlab 7 | WORKDIR /scionlab 8 | COPY requirements.txt dev-requirements.txt /scionlab/ 9 | RUN pip3 install -r requirements.txt -r dev-requirements.txt 10 | COPY . /scionlab/ 11 | 12 | # Fixup django settings for the integration tests: 13 | RUN sed -i 's/^SCIONLAB_SITE = .*/SCIONLAB_SITE = "http:\/\/coord:8000"/' scionlab/settings/development.py 14 | 15 | # Add /scionlab/ to PYTHONPATH to simplify running the scripts in .circleci/actions/ 16 | ENV PYTHONPATH /scionlab/ 17 | 18 | ENV DJANGO_SETTINGS_MODULE=scionlab.settings.development 19 | # Use CI entrypoint, should be similar to deploy/django-entrypoint.sh 20 | CMD /scionlab/.circleci/django-ci-entrypoint.sh 21 | -------------------------------------------------------------------------------- /.circleci/Dockerfile-scionHost: -------------------------------------------------------------------------------- 1 | FROM ubuntu:jammy 2 | # New docker versions seem __NOT__ to create /.dockerenv during __image creation__. But we need it. 3 | RUN touch /.dockerenv 4 | 5 | ARG package_repo=packages.netsec.inf.ethz.ch 6 | 7 | # Force debconf (called by apt-get) to be noninteractive 8 | ENV DEBIAN_FRONTEND=noninteractive 9 | RUN echo 'debconf debconf/frontend select Noninteractive' | debconf-set-selections 10 | 11 | # Install base 12 | RUN apt-get update && apt-get install --assume-yes \ 13 | systemd \ 14 | sudo \ 15 | apt-transport-https \ 16 | jq \ 17 | curl \ 18 | moreutils 19 | 20 | # systemd configuration. Note that the cgroup volume is no longer needed, nor works like documented below. 21 | # Based on: https://developers.redhat.com/blog/2014/05/05/running-systemd-within-docker-container/ 22 | # - converted to ubuntu, i.e. fixed some paths and removed unnecessary cleanup 23 | # - keep systemd-user-sessions.service, to allow login through SSH (login disabled on startup until this is run) 24 | ENV container docker 25 | RUN (cd /lib/systemd/system/sysinit.target.wants/; for i in *; do [ $i = systemd-tmpfiles-setup.service ] || rm -f $i; done); \ 26 | (cd /lib/systemd/system/multi-user.target.wants/; for i in *; do [ $i = systemd-user-sessions.service ] || rm -f $i; done); \ 27 | rm -f /etc/systemd/system/*.wants/*; \ 28 | rm -f /lib/systemd/system/local-fs.target.wants/*; \ 29 | rm -f /lib/systemd/system/sockets.target.wants/*udev*; \ 30 | rm -f /lib/systemd/system/sockets.target.wants/*initctl*; \ 31 | rm -f /lib/systemd/system/basic.target.wants/*; 32 | 33 | # Install SCION 34 | # XXX(matzf): install testing/prod based on branch name??? 35 | RUN bash -c "echo \"deb [trusted=yes] https://${package_repo}/debian all main\" > /etc/apt/sources.list.d/scionlab.list" 36 | RUN apt-get update && apt-get install --assume-yes scionlab 37 | 38 | # Add 'scionlab' user with passwordless sudo 39 | RUN sed '/^%sudo/s/ALL$/NOPASSWD:ALL/' /etc/sudoers -i 40 | RUN useradd --create-home --shell /bin/bash --groups sudo scionlab 41 | # Add 'user' user without sudo group to run user commands 42 | RUN useradd --create-home --shell /bin/bash user 43 | 44 | # Overwrite scionlab-config script with version from repo 45 | COPY scionlab/hostfiles/scionlab-config /usr/bin/ 46 | 47 | # Add CI scripts 48 | COPY .circleci/scripts/* /usr/local/bin/ 49 | 50 | CMD ["/bin/systemd"] 51 | -------------------------------------------------------------------------------- /.circleci/actions/update-keys.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """ 16 | Update AS keys for AS with given ids 17 | 18 | Run this script on the coordinator (container coord). 19 | """ 20 | 21 | 22 | import argparse 23 | import os 24 | 25 | import django 26 | 27 | 28 | def main(): 29 | parser = argparse.ArgumentParser(description='Update AS keys or core-AS keys') 30 | parser.add_argument('as_ids', nargs='+') 31 | parser.add_argument('--core-keys', action='store_true', 32 | help='update core keys (default: update AS keys)') 33 | 34 | args = parser.parse_args() 35 | 36 | if args.core_keys: 37 | print("Update core keys for %s" % args.as_ids) 38 | update_core_keys(args.as_ids) 39 | else: 40 | print("Update keys for AS %s" % args.as_ids) 41 | update_keys(args.as_ids) 42 | 43 | 44 | def update_keys(as_ids): 45 | from scionlab.models.core import AS 46 | 47 | ases = AS.objects.filter(as_id__in=as_ids) 48 | AS.update_cp_as_keys(ases) 49 | 50 | 51 | def update_core_keys(as_ids): 52 | from scionlab.models.core import AS 53 | 54 | ases = AS.objects.filter(as_id__in=as_ids) 55 | AS.update_core_as_keys(ases) 56 | 57 | 58 | if __name__ == '__main__': 59 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'scionlab.settings.development') 60 | django.setup() 61 | main() 62 | -------------------------------------------------------------------------------- /.circleci/django-ci-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -e 17 | 18 | # Wait for DB 19 | appdeps.py --interval-secs 1 --wait-secs 60 --port-wait $POSTGRES_HOST:$POSTGRES_PORT 20 | 21 | # Initialise/migrate DB 22 | /scionlab/manage.py migrate 23 | 24 | /scionlab/manage.py runserver 0.0.0.0:8000 25 | -------------------------------------------------------------------------------- /.circleci/docker-compose.test-prod-db.yaml: -------------------------------------------------------------------------------- 1 | services: 2 | db: 3 | # Latest postgres docker image uses the new Debian bookworm and it is incompatible with some 4 | # existing docker implementations. CircleCI runs one of those older implementations. 5 | # Pinning the image to N-bullseye (prior Debian distribution) as a workaround. 6 | # https://github.com/docker-library/postgres/issues/1100 7 | # image: postgres:latest 8 | image: postgres:12-bullseye 9 | expose: 10 | - "5432" 11 | environment: 12 | POSTGRES_DB: scionlab_coordinator 13 | POSTGRES_USER: scionlab_rw 14 | POSTGRES_PASSWORD: scionlab_rw_passw0rd 15 | -------------------------------------------------------------------------------- /.circleci/docker-compose.yml: -------------------------------------------------------------------------------- 1 | networks: 2 | as_net: 3 | driver: bridge 4 | ipam: 5 | driver: default 6 | config: 7 | - subnet: 172.31.0.0/16 8 | coord_net: # internal for webserver / DB 9 | 10 | services: 11 | coord: 12 | build: 13 | context: .. 14 | dockerfile: .circleci/Dockerfile-coord 15 | networks: 16 | as_net: 17 | ipv4_address: 172.31.0.10 18 | coord_net: 19 | ports: 20 | - "8432:8000" 21 | volumes: 22 | - ../run/:/scionlab/run/:z 23 | depends_on: 24 | - coord-db 25 | environment: 26 | POSTGRES_HOST: coord-db 27 | POSTGRES_PORT: 5432 28 | POSTGRES_DB: scionlab_coordinator 29 | POSTGRES_USER: scionlab_rw 30 | POSTGRES_PASSWORD: scionlab_rw_passw0rd 31 | 32 | coord-db: 33 | # Latest postgres docker image uses the new Debian bookworm and it is incompatible with some 34 | # existing docker implementations. CircleCI runs one of those older implementations. 35 | # Pinning the image to N-bullseye (prior Debian distribution) as a workaround. 36 | # https://github.com/docker-library/postgres/issues/1100 37 | # image: postgres:latest 38 | image: postgres:14-bullseye 39 | networks: 40 | - coord_net 41 | expose: 42 | - "5432" 43 | volumes: 44 | - pgdata:/var/lib/postgresql/data 45 | environment: 46 | POSTGRES_DB: scionlab_coordinator 47 | POSTGRES_USER: scionlab_rw 48 | POSTGRES_PASSWORD: scionlab_rw_passw0rd 49 | 50 | as1301: 51 | build: 52 | context: .. 53 | dockerfile: .circleci/Dockerfile-scionHost 54 | networks: 55 | as_net: 56 | ipv4_address: 172.31.0.110 57 | env_file: /tmp/as1301.env 58 | privileged: true 59 | 60 | as1303: 61 | build: 62 | context: .. 63 | dockerfile: .circleci/Dockerfile-scionHost 64 | networks: 65 | as_net: 66 | ipv4_address: 172.31.0.111 67 | env_file: /tmp/as1303.env 68 | privileged: true 69 | 70 | as1305: 71 | build: 72 | context: .. 73 | dockerfile: .circleci/Dockerfile-scionHost 74 | networks: 75 | as_net: 76 | ipv4_address: 172.31.0.112 77 | env_file: /tmp/as1305.env 78 | privileged: true 79 | 80 | as1401: 81 | build: 82 | context: .. 83 | dockerfile: .circleci/Dockerfile-scionHost 84 | networks: 85 | as_net: 86 | ipv4_address: 172.31.0.113 87 | env_file: /tmp/as1401.env 88 | privileged: true 89 | 90 | as1405: 91 | build: 92 | context: .. 93 | dockerfile: .circleci/Dockerfile-scionHost 94 | networks: 95 | as_net: 96 | ipv4_address: 172.31.0.114 97 | env_file: /tmp/as1405.env 98 | privileged: true 99 | 100 | useras4: 101 | build: 102 | context: .. 103 | dockerfile: .circleci/Dockerfile-scionHost 104 | networks: 105 | as_net: 106 | ipv4_address: 172.31.0.2 107 | env_file: /tmp/as4.env 108 | privileged: true 109 | 110 | volumes: 111 | pgdata: 112 | -------------------------------------------------------------------------------- /.circleci/scripts/await-beacons.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | cs_config_file=/etc/scion/cs-1.toml 17 | 18 | # Get prometheus metrics address from config toml file : 19 | prom=$(sed -n "/prometheus/s/.*=\s*\"\(.*\)\"/\1/p" "$cs_config_file") 20 | while true; do 21 | # Fetch & parse metrics, possibly aggregate multiple results with different tags 22 | received_beacons=$(curl --silent "$prom/metrics" | 23 | grep control_beaconing_received_beacons_total | 24 | grep 'result="ok' | 25 | sed 's/.* //' | 26 | awk '{s+=$1}END{print s}') 27 | if [[ $received_beacons -gt 0 ]]; then 28 | break 29 | fi 30 | sleep 1 31 | done 32 | -------------------------------------------------------------------------------- /.circleci/scripts/check-scion-status.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -ex 17 | 18 | # Check that the services are up: 19 | ! systemctl is-failed scion-* || (systemctl status scion-* --lines 2000 --no-pager --full && false) 20 | -------------------------------------------------------------------------------- /.circleci/scripts/enable-fabrid-sciond.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2024 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | file_path="/etc/scion/sciond.toml" 18 | search="[drkey_level2_db]" 19 | new_entry=$'[drkey_level2_db]\nconnection = "/var/lib/scion/sd.drkey.db"' 20 | 21 | # Check if the file exists 22 | if [ -f "$file_path" ]; then 23 | # Check if the file contains the text 24 | if ! grep -Fxq "$search" "$file_path"; then 25 | # Add the text if it's not present 26 | echo "$new_entry" >> "$file_path" 27 | fi 28 | fi -------------------------------------------------------------------------------- /.circleci/scripts/ping-all.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -eo pipefail 17 | 18 | # Ping _a_ host in all destination ASes. 19 | # Note that pinging the local AS is fine. 20 | 21 | ias=( 22 | '19-ffaa:0:1301' 23 | '19-ffaa:0:1303' 24 | '19-ffaa:0:1305' 25 | '20-ffaa:0:1401' 26 | '20-ffaa:0:1405' 27 | '20-ffaa:1:4' 28 | ) 29 | 30 | set -x 31 | for dstIA in ${ias[@]}; do 32 | chronic scion ping -c 1 "$dstIA,127.0.0.1" 33 | done 34 | -------------------------------------------------------------------------------- /.circleci/setup/build-containers.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # Extract host configuration info from testdata.yaml fixture 17 | python $(dirname $0)/generate-host-envs.py 18 | 19 | # The .dockerignore is for production, we'll need this 20 | sed -i '/.circleci/d' $(dirname $0)/../../.dockerignore 21 | 22 | # docker-compose rm -f 23 | # docker-compose pull 24 | 25 | # Parameter specifies --build-arg package_repo=... (testing or not prod packages) 26 | docker-compose build --no-cache "$@" 27 | -------------------------------------------------------------------------------- /.circleci/setup/check-scion-connectivity.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | set -eo pipefail 17 | 18 | containers=$(docker-compose ps --services | egrep -x '(user)?as[0-9]+') 19 | 20 | set -x 21 | sleep 10 # Give the services enough time to start (or fail) 22 | for c in $containers; do 23 | docker-compose exec -T "$c" check-scion-status.sh 24 | done 25 | for c in $containers; do 26 | docker-compose exec -T --user user "$c" await-beacons.sh 27 | done 28 | for c in $containers; do 29 | docker-compose exec -T --user user "$c" ping-all.sh 30 | done 31 | 32 | docker-compose exec -T --user user useras4 chronic scion ping -c 1 "19-ffaa:0:1301,127.0.0.1" --fabridquery "0-0#0,0@0" 33 | docker-compose exec -T --user user as1301 chronic scion ping -c 1 "20-ffaa:1:4,127.0.0.1" --fabridquery "0-0#0,0@0" -------------------------------------------------------------------------------- /.circleci/setup/export-logs.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | mkdir -p logs 17 | for c in $(docker-compose ps --services | egrep -x '(user)?as[0-9]+'); do 18 | docker-compose exec -T "$c" journalctl -u scion-* > logs/$c.log 19 | done 20 | -------------------------------------------------------------------------------- /.circleci/setup/generate-host-envs.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Parse the testdata.yaml fixture to extract host-uid and secrets for the hosts in the test setup. 3 | # Write the information to /tmp/asXY.env files, so it can easily be consumed in the docker-compose 4 | # setup. 5 | 6 | import yaml 7 | 8 | import os 9 | BASE_DIR = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) 10 | 11 | TESTDATA_FILE = os.path.join(BASE_DIR, 'scionlab/fixtures/testdata.yaml') 12 | TEST_ASES = [ 13 | 'ffaa:0:1301', 14 | 'ffaa:0:1303', 15 | 'ffaa:0:1305', 16 | 'ffaa:0:1401', 17 | 'ffaa:0:1405', 18 | 'ffaa:1:4', 19 | ] 20 | 21 | with open(TESTDATA_FILE) as f: 22 | data = yaml.load(f, Loader=yaml.SafeLoader) 23 | 24 | ases = {} 25 | hosts = {} 26 | for entry in data: 27 | if entry['model'] == 'scionlab.as': 28 | ases[entry['pk']] = entry['fields'] 29 | elif entry['model'] == 'scionlab.host': 30 | hosts[entry['pk']] = entry['fields'] 31 | 32 | for host in hosts.values(): 33 | as_id = ases[host['AS']]['as_id'] 34 | env_filename = '/tmp/as%s.env' % (as_id.split(':')[-1]) 35 | if as_id in TEST_ASES: 36 | envs = { 37 | 'SCIONLAB_HOST_ID': host['uid'], 38 | 'SCIONLAB_HOST_SECRET': host['secret'], 39 | } 40 | env_str = ''.join('%s=%s\n' % (k, v) for k, v in envs.items()) 41 | with open(env_filename, 'w') as f: 42 | f.write(env_str) 43 | print(env_filename, ':') 44 | print(env_str) 45 | -------------------------------------------------------------------------------- /.circleci/setup/init-ases.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | docker-compose exec useras4 /bin/bash -c 'enable-fabrid-sciond.sh' 17 | docker-compose exec as1301 /bin/bash -c 'enable-fabrid-sciond.sh' 18 | 19 | for c in $(docker-compose ps --services | egrep -x '(user)?as[0-9]+'); do 20 | docker-compose exec -T "$c" /bin/bash -c 'scionlab-config --host-id ${SCIONLAB_HOST_ID} --host-secret ${SCIONLAB_HOST_SECRET} --url http://coord:8000' 21 | done 22 | -------------------------------------------------------------------------------- /.circleci/setup/init-coord.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | # wait a bit for the migrations in the django-entrypoint.sh are done: 17 | docker-compose exec -T coord appdeps.py --interval-secs 1 --wait-secs 60 --port-wait coord:8000 18 | docker-compose exec -T coord ./manage.py loaddata scionlab/fixtures/testdata.yaml 19 | -------------------------------------------------------------------------------- /.circleci/setup/package-upgrade.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | for c in $(docker-compose ps --services | egrep -x '(user)?as[0-9]+'); do 17 | # We have to hide the /.dockerenv file, since the scionlab packages assume that they cannot use systemd, run systemctl when run in docker 18 | docker-compose exec -T "$c" /bin/bash -c \ 19 | 'echo "deb [trusted=yes] https://packages-test.netsec.inf.ethz.ch/debian all main" > /etc/apt/sources.list.d/scionlab.list; \ 20 | apt-get update; \ 21 | sudo mv /.dockerenv /.dockerenv.bk; \ 22 | DEBIAN_FRONTEND=noninteractive apt-get install -y --only-upgrade scionlab; \ 23 | sudo mv /.dockerenv.bk /.dockerenv;' 24 | done 25 | -------------------------------------------------------------------------------- /.circleci/test-prod.env: -------------------------------------------------------------------------------- 1 | SCIONLAB_SITE=http://localhost 2 | POSTGRES_HOST=db 3 | POSTGRES_PORT=5432 4 | POSTGRES_DB=scionlab_coordinator 5 | POSTGRES_USER=scionlab_rw 6 | POSTGRES_PASSWORD=scionlab_rw_passw0rd 7 | EMAIL_HOST_USER=username 8 | EMAIL_HOST_PASSWORD=passw0rd 9 | RECAPTCHA_PUBLIC_KEY=invalidpub 10 | RECAPTCHA_PRIVATE_KEY=invalidpriv 11 | VPN_CA_KEY_PASSWORD=passw00rd 12 | -------------------------------------------------------------------------------- /.dockerignore: -------------------------------------------------------------------------------- 1 | # run/ mounted as volume 2 | run 3 | .circleci 4 | .git 5 | .gitignore 6 | 7 | # Environments 8 | env.sh 9 | .env 10 | .venv 11 | env/ 12 | venv/ 13 | ENV/ 14 | env.bak/ 15 | venv.bak/ 16 | -------------------------------------------------------------------------------- /.flake8: -------------------------------------------------------------------------------- 1 | [flake8] 2 | exclude = 3 | .venv/ 4 | .env/ 5 | .venv/ 6 | env/ 7 | venv/ 8 | ENV/ 9 | env.bak/ 10 | venv.bak/ 11 | migrations 12 | scionlab/settings/development.py 13 | scionlab/settings/production.py 14 | filename = 15 | *.py 16 | ./scionlab/hostfiles/scionlab-config 17 | max-line-length = 100 18 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | static_bin/scion-pki-darwin-amd64 filter=lfs diff=lfs merge=lfs -text 2 | static_bin/scion-pki-linux-amd64 filter=lfs diff=lfs merge=lfs -text 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | run/* 2 | !run/.gitkeep 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | wheels/ 26 | *.egg-info/ 27 | .installed.cfg 28 | *.egg 29 | MANIFEST 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | .pytest_cache/ 52 | test-reports/ 53 | 54 | # Translations 55 | *.mo 56 | *.pot 57 | 58 | # Django stuff: 59 | *.log 60 | local_settings.py 61 | db.sqlite3 62 | 63 | # Flask stuff: 64 | instance/ 65 | .webassets-cache 66 | 67 | # Scrapy stuff: 68 | .scrapy 69 | 70 | # Sphinx documentation 71 | docs/_build/ 72 | 73 | # PyBuilder 74 | target/ 75 | 76 | # Jupyter Notebook 77 | .ipynb_checkpoints 78 | 79 | # pyenv 80 | .python-version 81 | 82 | # celery beat schedule file 83 | celerybeat-schedule 84 | 85 | # SageMath parsed files 86 | *.sage.py 87 | 88 | # Environments 89 | env.sh 90 | .env 91 | .venv 92 | env/ 93 | venv/ 94 | ENV/ 95 | env.bak/ 96 | venv.bak/ 97 | 98 | # Spyder project settings 99 | .spyderproject 100 | .spyproject 101 | 102 | # Rope project settings 103 | .ropeproject 104 | 105 | # mkdocs documentation 106 | /site 107 | 108 | # mypy 109 | .mypy_cache/ 110 | 111 | # Vim 112 | **.swp 113 | # Ctags 114 | tags 115 | # Idea 116 | .idea/ 117 | # UML 118 | uml.pdf 119 | 120 | # VS Code 121 | .vscode 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SCIONLab 2 | SCIONLab is a global research network to test the SCION next-generation 3 | internet architecture. Users can join the SCION network with their own 4 | computation resources and can set up and run their own autonomous systems 5 | (ASes). 6 | 7 | This repository contains the central website that orchestrates the 8 | configuration of ASes in the SCIONLab network. 9 | The website creates a configuration tar-ball for all the SCION services, 10 | including keys, certificates and all other options and settings, for each host 11 | in the SCIONLab network. 12 | 13 | The website has two parts: 14 | - public: 15 | - registration, password management etc. 16 | - simple interface to create/edit "user ASes" with a single link to a 17 | predefined "attachment point" ASes. 18 | The important point here is that the configuration of the corresponding 19 | attachment point AS(es) is automatically updated after any change to a user 20 | AS. 21 | - admin: 22 | - general website administration (user management etc.) 23 | - configuration of SCIONLab _infrastructure_ ASes 24 | - deployment of configuration to managed infrastructure nodes 25 | 26 | 27 | ## Development 28 | 29 | ### Installation 30 | Steps to start up the django webserver for development and local testing. 31 | 32 | ```bash 33 | # Make a venv for scionlab 34 | python3 -m venv venv 35 | source venv/bin/activate 36 | 37 | # NOTE: on debian/ubuntu python venv requires: 38 | # apt install python3-venv 39 | # and because it's apparently botched, update pip etc. in the venv: 40 | # pip install --upgrade pip setuptools wheel 41 | 42 | # Install Python requirements (Django, libraries, etc.) 43 | pip install --require-hashes -r requirements.txt -r dev-requirements.txt 44 | 45 | # NOTE: Some packages may fail to build. To ensure a complete dev setup is ready, in ubuntu you would run: 46 | # apt install gcc libpq-dev libpython3-dev libffi-dev 47 | # on your machine; install and try again. 48 | ``` 49 | 50 | To render the topology graph, `graphviz` needs to be installed additionally to the python dependencies. On ubuntu: 51 | ``` 52 | apt install graphviz 53 | ``` 54 | If this is missing, the topology graph will fail to render (with a 500 error code). 55 | 56 | ### Running 57 | 58 | The commands below assume an environment as installed above. 59 | 60 | Initialise development sqlite-DB with some an admin, a testuser and some ASes. 61 | The usernames and passwords for the test users can be found in [scionlab/fixtures/testuser.py](scionlab/fixtures/testuser.py). 62 | ```bash 63 | scripts/init-test-db.sh 64 | ``` 65 | 66 | Start the django development server: 67 | ```bash 68 | python manage.py runserver 69 | ``` 70 | 71 | ### Managing Dependencies 72 | There are two requirements-files for pip; `requirements.txt` contains the 73 | requirements for the production environment and `dev-requirements.txt` contains 74 | the _additional_ requirements for a development environment (style-checker, 75 | testing infrastructure etc). 76 | 77 | We use [pip-tools](https://pypi.org/project/pip-tools/) to manage the requirements. 78 | The `*.txt` files are generated from the corresponding `*.in` file. To generate/update requirements files, run 79 | ```bash 80 | pip-compile --generate-hashes --output-file=requirements.txt requirements.in 81 | pip-compile --generate-hashes --output-file=dev-requirements.txt dev-requirements.in 82 | ``` 83 | These commands are also recorded in the preamble of the `*.txt` files. 84 | 85 | 86 | ### Testing 87 | 88 | ##### Unit tests: 89 | 90 | ```bash 91 | ./manage.py test --parallel 92 | ``` 93 | 94 | ##### Style checker: 95 | 96 | ```bash 97 | flake8 98 | ``` 99 | 100 | ##### Integration tests: 101 | 102 | A set of more complex integration tests that will actually run the generated 103 | configuration for multiple SCION ASes uses the CircleCI infrastructure. 104 | 105 | ## Deployment 106 | 107 | There exists an internal [GitLab repository](https://gitlab.inf.ethz.ch/PRV-PERRIG/scionlab-deploy) containing the configuration for the pipeline deploying SCIONLab coordinator into running environment. GitHub webhook against this repo will triger deployment every time when there are changes in the `master` branch. 108 | 109 | In order to have a fully functional installation [the internal configuration](https://gitlab.inf.ethz.ch/PRV-PERRIG/scionlab-config) is needed. It contains some internal secret keys and configurations for accessing the SCIONLab infrastructure machines. More detailed informations can be found directly in its repository. 110 | -------------------------------------------------------------------------------- /deploy/Caddyfile: -------------------------------------------------------------------------------- 1 | {$SCIONLAB_SITE} { 2 | rewrite /favicon.ico /static/scionlab/images/favicon.ico 3 | handle /static/* { 4 | root * /srv/scionlab 5 | file_server 6 | } 7 | handle { 8 | reverse_proxy django:8000 9 | } 10 | 11 | log 12 | tls scionlab-admins@sympa.ethz.ch 13 | } 14 | 15 | # Redirect to www. subdomain 16 | scionlab.org { 17 | log 18 | redir https://www.scionlab.org{uri} permanent 19 | } 20 | -------------------------------------------------------------------------------- /deploy/Dockerfile-django: -------------------------------------------------------------------------------- 1 | FROM python:3.11 2 | ENV PYTHONUNBUFFERED 1 3 | 4 | # Graphviz is used to render the topology (see scionlab.views.topology); the python 5 | # library only wraps calls to the binaries which need to be installed on the system. 6 | RUN apt-get update && apt-get install -y graphviz 7 | 8 | # Appdeps is used by the entrypoint script to wait for the DB services. 9 | # Not included in requirements because it's a requirement of the 10 | # docker-compose plumbing, not the application. 11 | RUN pip3 install appdeps 12 | 13 | RUN mkdir /scionlab 14 | WORKDIR /scionlab 15 | COPY requirements.txt /scionlab/ 16 | RUN pip3 install -r requirements.txt 17 | COPY . /scionlab/ 18 | 19 | 20 | ENV DJANGO_SETTINGS_MODULE=scionlab.settings.production 21 | CMD /scionlab/deploy/django-entrypoint.sh 22 | -------------------------------------------------------------------------------- /deploy/django-entrypoint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -e 4 | 5 | # Collect/update static files. These will be consumed by caddy, reading from the same volume. 6 | /scionlab/manage.py collectstatic --noinput 7 | 8 | # Wait for DB 9 | appdeps.py --interval-secs 1 --wait-secs 60 --port-wait $POSTGRES_HOST:$POSTGRES_PORT 10 | 11 | # Initialise/migrate DB 12 | /scionlab/manage.py migrate 13 | 14 | gunicorn --log-level info --capture-output -b django:8000 scionlab.wsgi 15 | -------------------------------------------------------------------------------- /dev-requirements.in: -------------------------------------------------------------------------------- 1 | # Testing: 2 | django-webtest>=1.9.6 3 | flake8 4 | flake8-formatter-junit-xml 5 | parameterized 6 | unittest-xml-reporting 7 | tblib 8 | # fix to same version as requirements: 9 | typing-extensions==4.0.1 10 | # require min version for lxml dependency via unittest-xml-reporting: 11 | lxml>=4.9.1 12 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3' 2 | 3 | services: 4 | django: 5 | build: 6 | context: . 7 | dockerfile: deploy/Dockerfile-django 8 | env_file: run/scionlab.env 9 | expose: 10 | - "8000" 11 | volumes: 12 | - ./run/:/scionlab/run/:z 13 | - web-static:/scionlab/static/:z 14 | 15 | caddy: 16 | image: caddy:2.4.6-alpine 17 | env_file: run/scionlab.env 18 | depends_on: 19 | - django 20 | ports: 21 | - "80:80" 22 | - "443:443" 23 | volumes: 24 | - ./deploy/Caddyfile:/etc/caddy/Caddyfile:z 25 | - web-static:/srv/scionlab/static:z 26 | - caddydata:/data 27 | 28 | volumes: 29 | web-static: 30 | caddydata: 31 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == '__main__': 6 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'scionlab.settings.development') 7 | try: 8 | from django.core.management import execute_from_command_line 9 | except ImportError as exc: 10 | raise ImportError( 11 | "Couldn't import Django. Are you sure it's installed and " 12 | "available on your PYTHONPATH environment variable? Did you " 13 | "forget to activate a virtual environment?" 14 | ) from exc 15 | execute_from_command_line(sys.argv) 16 | -------------------------------------------------------------------------------- /requirements.in: -------------------------------------------------------------------------------- 1 | Django>=3.2.20,<4.0 2 | cryptography>=41.0.2 3 | django-crispy-forms>=1.9.0 4 | django-extensions 5 | django-maintenance-mode>=0.14.0 6 | django-recaptcha2 7 | django-registration>=3.1 8 | graphviz 9 | gunicorn 10 | jinja2 11 | psycopg2-binary # PostgreSQL backend 12 | pynacl 13 | pyyaml>=6.0.1 # for fixtures and config generation; high severity vulnerability before 4.1 (CVE-2017-18342), Cython>=3.0.0 (released in 2023) incompatible before 6.0.1 14 | scrypt 15 | toml 16 | 17 | 18 | # Sub-dependencies of above packages which have security implications, and thus require specific prerequisites: 19 | requests>=2.31.0 20 | -------------------------------------------------------------------------------- /run/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/run/.gitkeep -------------------------------------------------------------------------------- /scionlab/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/__init__.py -------------------------------------------------------------------------------- /scionlab/context_processors.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | def instance_indicator(request): 17 | """ 18 | Inject a `instance_indicator` string to indicate dev/testing instances. 19 | If set, this is rendered into a ribbon in the scionlab/base.html template 20 | """ 21 | from django.conf import settings 22 | mode = '' 23 | if hasattr(settings, 'INSTANCE_NAME'): 24 | mode = settings.INSTANCE_NAME 25 | if settings.DEBUG: 26 | mode += '[debug]' 27 | return {'instance_indicator': mode} 28 | -------------------------------------------------------------------------------- /scionlab/defines.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import datetime 16 | 17 | 18 | MAX_PORT = 2**16-1 19 | """ Max value for ports """ 20 | 21 | MAX_INTERFACE_ID = 2**12-1 22 | 23 | USER_AS_ID_BEGIN = 0xffaa00010001 # 'ffaa:1:1' 24 | USER_AS_ID_END = 0xffaa0001ffff # 'ffaa:1:ffff' 25 | 26 | DEFAULT_PUBLIC_PORT = 50000 # UDP/IP, public_ip:port (bound to bind_ip:port if bind_ip is set) 27 | # Fixed port assignment scheme for routers: the internal/control/metrics ports 28 | # are defined as base_port + instance_id. 29 | # Note that this does not strictly adhere to the suggestions by scion/wiki/default-port-ranges. 30 | # Notably, this scheme starts at lower numbers to allow up to 40 routers before collision with the 31 | # range used for the dispatcher (30041). 32 | BR_INTERNAL_PORT_BASE = 30000 # UDP/IP, internal_ip:port 33 | BR_METRICS_PORT_BASE = 30400 # TCP/IP, 127.0.0.1:port 34 | 35 | CS_PORT = 30254 # UDP/SCION (inter-AS) *and* TCP/IP (intra-AS) 36 | CS_QUIC_PORT = 30354 # QUIC/UDP/SCION port for inter-AS 37 | CS_METRICS_PORT = 30454 # TCP/IP, 127.0.0.1:port 38 | 39 | SD_TCP_PORT = 30255 # TCP/IP, 127.0.0.1:port, NOTE: also in scion-daemon.deb 40 | SD_METRICS_PORT = 30455 # " ditto 41 | 42 | DISPATCHER_PORT = 30041 # UDP/IP, 127.0.0.1:port, [::]:port, NOTE: fixed! 43 | DISPATCHER_METRICS_PORT = 30441 # TCP/IP, 127.0.0.1:port, NOTE: also in scion-daemon.deb 44 | 45 | SIG_CTRL_PORT = 30256 46 | SIG_DATA_PORT = 30056 47 | 48 | BW_PORT = 40001 49 | PP_PORT = 40002 50 | 51 | OPENVPN_SERVER_PORT = 1194 52 | 53 | DEFAULT_HOST_INTERNAL_IP = "127.0.0.1" 54 | DEFAULT_LINK_MTU = 1500 - 20 - 8 55 | 56 | PROPAGATE_TIME_CORE = 60 # lower beaconing frequency in cores to save resources 57 | PROPAGATE_TIME_NONCORE = 5 # higher frequency in non-cores ASes to have quicker startup 58 | 59 | SCION_CONFIG_DIR = "/etc/scion" 60 | SCION_VAR_DIR = "/var/lib/scion" 61 | OPENVPN_CONFIG_DIR = "/etc/openvpn" 62 | 63 | # Default expiration time for keys; we currently only distinguish between core/non-core keys. 64 | # Note: the expiration of the core keys also defines the expiration of the TRCs. 65 | DEFAULT_EXPIRATION_AS_KEYS = datetime.timedelta(days=365) 66 | DEFAULT_EXPIRATION_CORE_KEYS = datetime.timedelta(days=2*365) 67 | DEFAULT_TRC_GRACE_PERIOD = datetime.timedelta(hours=6) 68 | -------------------------------------------------------------------------------- /scionlab/fixtures/dev_root_ca_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDDTCCAnagAwIBAgIUSI5qCQF0Jboz7dhLCTQWYsf3C4wwDQYJKoZIhvcNAQEL 3 | BQAwgZYxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEPMA0GA1UEBwwGWnVyaWNo 4 | MQwwCgYDVQQKDANFVEgxDzANBgNVBAsMBk5ldFNlYzEPMA0GA1UEAwwGRVRIIENB 5 | MREwDwYDVQQpDAhTQ0lPTlZQTjEmMCQGCSqGSIb3DQEJARYXc2Npb25AbGlzdHMu 6 | aW5mLmV0aHouY2gwHhcNMjIwOTE5MDgxNTE5WhcNMzIwOTE2MDgxNTE5WjCBljEL 7 | MAkGA1UEBhMCQ0gxCzAJBgNVBAgMAlpIMQ8wDQYDVQQHDAZadXJpY2gxDDAKBgNV 8 | BAoMA0VUSDEPMA0GA1UECwwGTmV0U2VjMQ8wDQYDVQQDDAZFVEggQ0ExETAPBgNV 9 | BCkMCFNDSU9OVlBOMSYwJAYJKoZIhvcNAQkBFhdzY2lvbkBsaXN0cy5pbmYuZXRo 10 | ei5jaDCBnzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAwhWgjqkNVXtTscUvYdZN 11 | voi1FqVvbKYu6j7XMXa/9OU/pkYd2Di77mPbxr4jmEDN6m1yXj00LZhai7xmPMad 12 | qIXPVY8ALIfq/SQA2Apoc6+68pjwxcHiiARkSE++jAVIkrSubJn63Bzyr6b7K8li 13 | WRsEd0/q7pzSDLiZNwzdB2MCAwEAAaNWMFQwHwYDVR0jBBgwFoAUkW7Ai7WR7aL+ 14 | 2PWeKvciwJ4fBE8wHQYDVR0OBBYEFJFuwIu1ke2i/tj1nir3IsCeHwRPMBIGA1Ud 15 | EwEB/wQIMAYBAf8CAQAwDQYJKoZIhvcNAQELBQADgYEAh4qmP39la0NJe9yYDblK 16 | KJsc1T/MEOXTWycoE8aF9mD/M3KjIzlN7V4qVwlU5iKRqS7nfys5nTIPIYcGO3yE 17 | h8d0rUbWHQUzKz9FIuy32dz2b9DelrjOnwTlMdWBuHlqXpJnzg+fGwsCJtPgeDu4 18 | WFW2TFJloTuFRb4Z1YqGYks= 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /scionlab/fixtures/dev_root_ca_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: AES-256-CBC,9E32EDB6904BFA858061CFB99DF2FEE2 4 | 5 | iQH3Skrm1yyKPxnIBwNKOooLfmtGyS4RsvaQMC1mXjUMIQy79hRkTc6kc33MV08F 6 | L4eM9eG+HBIUNAby4VCIJFfBtlrC21A8OqNPKqhkTH5R8dHb93kQg5b1h/lStvCg 7 | FJv48224s1oATSrxP2plD7pZzNWJEwggYRwywBZ80Ifob3zxo7v/ChOFOb3HtnBt 8 | 3JAp5L4khTgin6wr1aLLntafc+Ygfk6KAm6aecphF5c2YuSGEwTDDtSxw7JgoeG/ 9 | QaVcIFu66dwljLMD2becayshZ4AVyTGCDBUaHKtJ9OHkDp6oBuZ9QUKP5mNkGZKU 10 | F0RGPwCg/K9x8iUdFVncblAEtxuqbZixKTCzMjOzgNOy6xHbE9R9Ge7brwTarnHa 11 | Lf9VuRG5TxGUDyR6hpS6sAaEPqmzsjC5pQit95da2Ehvkz5Dlq6/q69lg/PDjfSJ 12 | sR6dM4iWtIbRH55RFXOBrDaTXVihcFtExh1a9PbKJtpc6pirRdylcCMWiaGA38Ym 13 | eY0qcrenYYykqMta+ZyaHFm2eymFFib2+V6jqTyTs2+Dm/IEqFpLp4sWFaQwq+sw 14 | 1P9OoEXmvBNCgd8p9mu/eGaRX/im52PywgVIHa/rz1xtwDXw+FdF9eFthKga91Yo 15 | tLV+TAhcDSaTHJAi7EZ+IUeP64x0Gpkf3b9rbnP+mE70WsTh1wbpNbIZP/3TAUxk 16 | wsDEZqUEP3Ikdod5znO52IUBzbAcFusFiUvI0aDqO2mmSGLXldsgMnlhAhtRz957 17 | t/m38K+aYZHSvccVNK9aaBBAk58Ot789BCt+3IsjXve3MvSyElgpCDSlAdOFwKCK 18 | -----END RSA PRIVATE KEY----- 19 | -------------------------------------------------------------------------------- /scionlab/fixtures/testdata.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from scionlab.fixtures import testtopo, testuser, testuseras 16 | 17 | 18 | def create_testdata(): 19 | testuser.create_testusers() 20 | testtopo.create_testtopo() 21 | testuseras.create_testuserases() 22 | -------------------------------------------------------------------------------- /scionlab/fixtures/testuser.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from scionlab.models.user import User 16 | 17 | TESTUSER_EMAIL = 'scion@scionlab.org' 18 | TESTUSER_PWD = 'scion' 19 | # Exbert starts with some UserASes 20 | TESTUSER_EXBERT_EMAIL = 'exbert@scionlab.org' 21 | TESTUSER_EXBERT_PWD = 'exbert' 22 | TESTUSER_ADMIN_EMAIL = 'admin@scionlab.org' 23 | TESTUSER_ADMIN_PWD = 'admin' 24 | 25 | 26 | def get_testuser(): 27 | """ Return the User object for testuser """ 28 | return User.objects.get(email=TESTUSER_EMAIL) 29 | 30 | 31 | def get_testuser_exbert(): 32 | """ Return the User object for Exbert """ 33 | return User.objects.get(email=TESTUSER_EXBERT_EMAIL) 34 | 35 | 36 | # Explicitly make `get_testuser` and `get_testuser_admin` not a test; 37 | # nose thinks this looks like a test... 38 | get_testuser.__test__ = False 39 | get_testuser_exbert.__test__ = False 40 | 41 | 42 | def create_testusers(): 43 | User.objects.create_superuser( 44 | email=TESTUSER_ADMIN_EMAIL, 45 | password=TESTUSER_ADMIN_PWD, 46 | ) 47 | User.objects.create_user( 48 | email=TESTUSER_EMAIL, 49 | password=TESTUSER_PWD 50 | ) 51 | User.objects.create_user( 52 | email=TESTUSER_EXBERT_EMAIL, 53 | password=TESTUSER_EXBERT_PWD 54 | ) 55 | -------------------------------------------------------------------------------- /scionlab/fixtures/testuseras.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from scionlab.fixtures.testuser import get_testuser_exbert 16 | from scionlab.models.user_as import AttachmentPoint, AttachmentConf, UserAS 17 | 18 | 19 | def create_testuserases(): 20 | exbert = get_testuser_exbert() 21 | aps = AttachmentPoint.objects.all() 22 | _create_user_as(exbert, aps[0], UserAS.VM, True, False) 23 | _create_user_as(exbert, aps[1], UserAS.VM, False, False) 24 | _create_user_as(exbert, aps[2], UserAS.PKG, False, False) 25 | _create_user_as(exbert, aps[3], UserAS.PKG, True, True) 26 | _create_user_as(exbert, aps[0], UserAS.SRC, False, True) 27 | 28 | 29 | def _create_user_as(owner, attachment_point, installation_type, use_vpn, enable_fabrid): 30 | user_as = UserAS.objects.create( 31 | owner=owner, 32 | installation_type=installation_type, 33 | label="", 34 | isd=attachment_point.AS.isd, 35 | fabrid_enabled=enable_fabrid 36 | ) 37 | user_as.update_attachments([ 38 | AttachmentConf( 39 | attachment_point=attachment_point, 40 | use_vpn=use_vpn, 41 | public_ip='172.31.0.200', 42 | public_port=54321, 43 | ), 44 | ]) 45 | -------------------------------------------------------------------------------- /scionlab/forms/fields.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import ipaddress 16 | from django import forms 17 | from django.core.exceptions import ValidationError 18 | 19 | 20 | class GenericIPNetworkField(forms.Field): 21 | def __init__(self, *args, **kwargs): 22 | kwargs.pop('max_length', 0) # parent is not length aware 23 | super().__init__(*args, **kwargs) 24 | 25 | def to_python(self, value): 26 | if not value: 27 | return None 28 | try: 29 | subnet = ipaddress.ip_network(value) 30 | except ValueError: 31 | raise ValidationError('Invalid network address') 32 | return subnet 33 | -------------------------------------------------------------------------------- /scionlab/forms/login_form.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.contrib.auth.forms import AuthenticationForm 16 | from snowpenguin.django.recaptcha2.fields import ReCaptchaField 17 | from snowpenguin.django.recaptcha2.widgets import ReCaptchaWidget 18 | 19 | 20 | class AuthenticationFormWithCaptcha(AuthenticationForm): 21 | captcha = ReCaptchaField(widget=ReCaptchaWidget(explicit=True, container_id='recaptcha-id')) 22 | -------------------------------------------------------------------------------- /scionlab/forms/registration_form.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.forms import Form, EmailField 16 | from django_registration.forms import RegistrationForm 17 | from crispy_forms.helper import FormHelper 18 | from crispy_forms.layout import Layout, Submit 19 | from crispy_forms.bootstrap import AppendedText 20 | from snowpenguin.django.recaptcha2.fields import ReCaptchaField 21 | from snowpenguin.django.recaptcha2.widgets import ReCaptchaWidget 22 | 23 | from scionlab.models.user import User 24 | 25 | 26 | class RegistrationFormWithCaptcha(RegistrationForm): 27 | class Meta(RegistrationForm.Meta): 28 | model = User 29 | fields = ('first_name', 'last_name', 'organisation', 'email', ) 30 | 31 | captcha = ReCaptchaField(widget=ReCaptchaWidget(explicit=True, container_id='recaptcha-id')) 32 | 33 | def __init__(self, *args, **kwargs): 34 | self.helper = self._crispy_helper() 35 | super().__init__(*args, **kwargs) 36 | for f in ['first_name', 'last_name']: 37 | self.fields[f].required = True 38 | 39 | def _crispy_helper(self): 40 | """ 41 | Create the crispy-forms FormHelper. The form will then be rendered 42 | using {% crispy form %} in the template. 43 | """ 44 | helper = FormHelper() 45 | helper.attrs['id'] = 'id_user_as_form' 46 | helper.layout = Layout( 47 | AppendedText('email', ''), 48 | AppendedText('first_name', ''), 49 | AppendedText('last_name', ''), 50 | AppendedText('organisation', ''), 51 | AppendedText('password1', ''), 52 | AppendedText('password2', ''), 53 | 'captcha', 54 | Submit('register', 'Register', css_class='btn-block'), 55 | ) 56 | return helper 57 | 58 | 59 | class RegistrationResendForm(Form): 60 | # quirk: to access the `send_activation_email` functionality, we need a RegistrationView (design 61 | # bug) in which we use this form. The RegistrationView does some magic checking to ensure 62 | # that the User model is configured properly, for which it accesses the Meta. 63 | # So this Meta just serves to make this check happy and is otherwise useless. 64 | class Meta: 65 | model = User 66 | _meta = Meta() 67 | 68 | email = EmailField() 69 | -------------------------------------------------------------------------------- /scionlab/hostfiles/README_dedicated.md: -------------------------------------------------------------------------------- 1 | # SCIONLab Dedicated 2 | SCIONLab Dedicated System configuration 3 | 4 | This is the SCION configuration for a dedicated system. 5 | 6 | ## Setup of the system 7 | 8 | You need to install SCION to the system, and/or update it to the version you need, manually. 9 | Please refer to the documentation on: 10 | https://docs.scionlab.org 11 | 12 | ## Further information 13 | 14 | You can find more information about the SCION architecture in: 15 | https://www.scion-architecture.net/ 16 | As well as tutorials, videos, publications, source code and much more! 17 | -------------------------------------------------------------------------------- /scionlab/hostfiles/README_vm.md: -------------------------------------------------------------------------------- 1 | # SCIONLab VM 2 | 3 | ## Install Vagrant and VirtualBox 4 | 5 | Running your AS in a VM as suggested below requires Vagrant and VirtualBox. 6 | 7 | Please install Vagrant and VirtualBox: 8 | - https://www.virtualbox.org/wiki/Downloads 9 | - https://www.vagrantup.com/docs/installation/ 10 | 11 | On recent Ubuntu or Debian systems, it may be enough to run 12 | 13 | $ sudo apt-get install vagrant virtualbox 14 | 15 | 16 | ## Using Vagrant to run the VM 17 | 18 | Navigate your shell to the directory containing the Vagrantfile (and this README). 19 | Note that all vagrant commands always need to be run in this directory! 20 | To start your VM, run 21 | 22 | $ vagrant up 23 | 24 | When running you're VM for the first time, this will download the Ubuntu base box 25 | and then install all the SCION packages and their dependencies. 26 | 27 | This will already start the services for your SCIONLab AS. 28 | 29 | Once the `vagrant up` command returns the prompt, you can connect to your VM to 30 | start exploring: 31 | 32 | $ vagrant ssh 33 | 34 | The directory containing the Vagrant file is synced with the VM where the files 35 | will appear in the `/vagrant/` directory. 36 | This is convenient way to share files between your your host machine and your 37 | VM, and allows to move data both ways. 38 | 39 | To shutdown the VM, run 40 | 41 | $ vagrant halt 42 | 43 | To start it back up, just type `vagrant up` again. Finally, if you want to wipe 44 | your VM, e.g. to start fresh, run `vagrant destroy`. 45 | 46 | More information for `vagrant` commands can be found at: 47 | https://www.vagrantup.com/docs/cli 48 | 49 | 50 | ## Running SCION 51 | 52 | The SCION infrastructure is automatically started when the VM boots up. 53 | 54 | Please refer to the online tutorials for more information: 55 | https://docs.scionlab.org 56 | -------------------------------------------------------------------------------- /scionlab/hostfiles/client.conf.tmpl: -------------------------------------------------------------------------------- 1 | # Specify that we are a client 2 | client 3 | 4 | # We use a TUN device, a virtual point-to-point IP link 5 | dev tun 6 | 7 | # Connecting to a UDP server 8 | proto udp 9 | 10 | # Enable server authentication 11 | remote-cert-tls server 12 | 13 | # Select prefered cipher available in ovpn 2.3 14 | cipher AES-256-CBC 15 | 16 | # IP and port of the server 17 | remote ${ServerIP} ${ServerPort} 18 | 19 | # Keep trying indefinitely to resolve the 20 | # host name of the OpenVPN server. 21 | resolv-retry infinite 22 | 23 | # Do not bind to a specific local port number 24 | nobind 25 | 26 | # Preserve state accross restarts 27 | persist-key 28 | persist-tun 29 | 30 | # Enable compression on the VPN link. 31 | comp-lzo 32 | 33 | # Log verbosity 34 | verb 3 35 | 36 | # Silence repeating messages 37 | mute 10 38 | 39 | ${CACert} 40 | 41 | ${ClientCert} 42 | 43 | ${ClientKey} 44 | -------------------------------------------------------------------------------- /scionlab/hostfiles/scionlab-config.service: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=scionlab-config daemon mode 3 | After=network-online.target 4 | Wants=network-online.target 5 | 6 | [Service] 7 | Type=simple 8 | ExecStart=/usr/bin/scionlab-config --daemon 9 | Restart=on-failure 10 | 11 | [Install] 12 | WantedBy=multi-user.target 13 | -------------------------------------------------------------------------------- /scionlab/hostfiles/server.conf.tmpl: -------------------------------------------------------------------------------- 1 | mode server 2 | tls-server 3 | 4 | dev tun 5 | local ${ServerPublicIP} 6 | port ${ServerPort} 7 | proto udp 8 | 9 | # 1. choice: recommended, long term 10 | # 2. choice: compatible with clients with openvpn 2.3 and updated client.conf file 11 | # 3. choice: compatible with older clients 12 | ncp-ciphers AES-256-GCM:AES-256-CBC:BF-CBC 13 | 14 | topology subnet 15 | push "topology subnet" 16 | ifconfig ${ServerVPNIP} ${Netmask} 17 | route ${Subnet} ${Netmask} 18 | push "route ${ServerVPNIP}" 19 | 20 | client-config-dir ccd 21 | ccd-exclusive 22 | 23 | keepalive 10 120 24 | comp-lzo no 25 | push "comp-lzo no" 26 | 27 | txqueuelen 100 28 | 29 | user nobody 30 | group nogroup 31 | persist-key 32 | persist-tun 33 | status openvpn-status.log 34 | verb 3 35 | mute 20 36 | 37 | # certificates and keys 38 | dh dh.pem # Note: generated by scionlab-config using `openssl dhparam -out dh.pem 2048` 39 | 40 | ${CACert} 41 | 42 | ${ServerCert} 43 | 44 | 45 | ${ServerKey} 46 | 47 | -------------------------------------------------------------------------------- /scionlab/management/commands/check_crypto.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.core.management.base import BaseCommand 16 | 17 | from scionlab.models.core import ISD 18 | from scionlab.scion.pkicommand import ScionPkiError 19 | 20 | 21 | class Command(BaseCommand): 22 | help = 'Checks all certificates of all infrastructure ASes for their validity' 23 | 24 | def handle(self, *args, **kwargs): 25 | failed_isds = [] 26 | failed_ases = [] 27 | isds = ISD.objects.all() 28 | for isd in isds: 29 | try: 30 | num = isd.validate_crypto() 31 | print(f'ISD {isd.isd_id}: {isd.label} ; {num} TRCs.') 32 | except ScionPkiError as ex: 33 | failed_isds.append(isd) 34 | print(f'---------------------------\n' 35 | f'ERROR: failed to verify ISD {isd.isd_id}: {ex}' 36 | f'\n-----------------------------') 37 | continue 38 | ases = isd.ases.filter(owner=None) 39 | for as_ in ases: 40 | try: 41 | num = as_.validate_crypto() 42 | print(f'AS {as_.as_id}, core: {as_.is_core} ; {num} certs') 43 | except ScionPkiError as ex: 44 | failed_ases.append(as_) 45 | print(f'failed AS: {as_.as_id}: {ex}') 46 | 47 | print(f'summary; {len(failed_isds)} failed ISDs: ' 48 | f'{[isd.isd_id for isd in failed_isds]}') 49 | print(f'summary; {len(failed_ases)} failed ASes: ' 50 | f'{[as_.as_id for as_ in failed_ases]}') 51 | -------------------------------------------------------------------------------- /scionlab/management/commands/create_link.py: -------------------------------------------------------------------------------- 1 | # Copyright 2024 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import sys 16 | 17 | from django.core.management.base import BaseCommand, CommandParser 18 | 19 | from scionlab.models.core import Host, Link 20 | 21 | 22 | class Command(BaseCommand): 23 | help = 'Create Link between two hosts' 24 | 25 | def add_arguments(self, parser: CommandParser) -> None: 26 | parser.add_argument('-t', '--type', type=str, required=True, 27 | choices=['core', 'provider', 'peer'], 28 | help='Type of the link. ' + 29 | 'If provider, host_a is parent, host_b is child.') 30 | parser.add_argument('host_a', type=str, 31 | help='First host. If link is of type provider, this is the parent.') 32 | parser.add_argument('host_b', type=str, 33 | help='Second host. If link is of type provider, this is the child.') 34 | 35 | def handle(self, *args, **kwargs): 36 | # Check the type is correct. 37 | type = None 38 | for t in Link.LINK_TYPES: 39 | if kwargs['type'].lower() == t[0].lower(): 40 | type = t[0] 41 | if type is None: 42 | raise RuntimeError('logic error: link types have changed') 43 | 44 | # Find the hosts. 45 | host_a = self.find_host(kwargs['host_a']) 46 | host_b = self.find_host(kwargs['host_b']) 47 | 48 | # Create the link. 49 | self.create_link(type, host_a, host_b) 50 | 51 | def find_host(self, hostname): 52 | hosts = Host.objects.filter(label=hostname) 53 | if hosts.count() == 1: 54 | return hosts.get() 55 | if hosts.count() > 1: 56 | exit(f'more than one host with the same label "{hostname}"') 57 | 58 | # No host with that exact label found, try to find substrings. 59 | hosts = Host.objects.filter(label__contains=hostname) 60 | if hosts.count() == 0: 61 | exit(f'no host found with or containing label "{hostname}"') 62 | 63 | labels = [h.label for h in hosts] 64 | print(f'no host with exact label "{hostname}". Found similar:\n' + '\n'.join(labels)) 65 | if len(labels) > 1: 66 | # More than one host found, bail and ask the user to be specific. 67 | exit('be more specific') 68 | 69 | # If only one matches the substring, use it. 70 | print(f'replacing "{hostname}" -> "{labels[0]}"') 71 | return hosts.get() 72 | 73 | def create_link(self, type, host_a, host_b): 74 | # The create_from_hosts method checks the validity of the type for those ASes. 75 | Link.objects.create_from_hosts(type, host_a, host_b) 76 | 77 | 78 | def exit(msg: str): 79 | print(msg) 80 | sys.exit(1) 81 | -------------------------------------------------------------------------------- /scionlab/management/commands/export_zonefile.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import argparse 16 | 17 | from django.core.management.base import BaseCommand 18 | 19 | from scionlab.models.core import Host 20 | from scionlab.models.user_as import UserAS 21 | 22 | 23 | class Command(BaseCommand): 24 | help = 'Creates a RAINS compatible zonefile containing all hosts' 25 | 26 | TYPE_SCION_IP4 = ':scionip4:' 27 | USER_PREFIX = 'user-' 28 | 29 | def add_arguments(self, parser): 30 | parser.add_argument('-z', '--zone', type=str, required=True, 31 | help='The name of the zone under which assertions \ 32 | are added (e.g. \'node.snet.\')') 33 | parser.add_argument('-c', '--context', type=str, default='.', 34 | help='Namespace in which the zone is created (default: \'.\')') 35 | parser.add_argument('-o', '--out', type=argparse.FileType('w'), default='-', 36 | help='Output file (default: Stdout)') 37 | 38 | def handle(self, *args, **options): 39 | 40 | record_dict = {} 41 | 42 | # user ASes 43 | for ua in UserAS.objects.all(): 44 | host = ua.hosts.first() 45 | parts = ua.as_id.split(':') 46 | record_dict[self.USER_PREFIX + parts[2]] = host.scion_address() 47 | 48 | # Infrastructure 49 | # all hosts belonging to an AS without an owner 50 | for infra in Host.objects.filter(AS__owner=None).exclude(label=None): 51 | record_dict[infra.label] = infra.scion_address() 52 | 53 | # write zonefile 54 | zone_file = ':Z: %s %s [\n' % (options['zone'], options['context']) 55 | for k in sorted(record_dict.keys(), key=str.lower): 56 | zone_file += self.encodeAssertion(k, record_dict[k]) + '\n' 57 | zone_file += ']' 58 | 59 | options['out'].write(zone_file) 60 | 61 | self.stdout.write(self.style.SUCCESS( 62 | 'Zonefile successfully written to file')) 63 | 64 | def encodeAssertion(self, subjectName, value): 65 | assertion = ' :A: %s [ %s%s ]' % ( 66 | subjectName, self.TYPE_SCION_IP4.ljust(12), value) 67 | return assertion 68 | -------------------------------------------------------------------------------- /scionlab/management/commands/initialize_root_ca.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | import os 15 | 16 | from django.conf import settings 17 | from django.core.management.base import BaseCommand, CommandError 18 | 19 | from scionlab.openvpn_config import write_vpn_ca_config 20 | 21 | 22 | class Command(BaseCommand): 23 | help = 'Generate root CA configuration' 24 | 25 | def handle(self, *args, **options): 26 | if os.path.exists(settings.VPN_CA_KEY_PATH) and os.path.exists(settings.VPN_CA_CERT_PATH): 27 | raise CommandError('Root CA files already generated. ' 28 | 'To generate a new root CA configuration, remove the ' 29 | 'key (%s) and certificate (%s) files first.' % ( 30 | settings.VPN_CA_KEY_PATH, settings.VPN_CA_CERT_PATH)) 31 | write_vpn_ca_config() 32 | -------------------------------------------------------------------------------- /scionlab/management/commands/mass_email.py: -------------------------------------------------------------------------------- 1 | # Copyright 2023 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import argparse 16 | import sys 17 | 18 | from typing import List 19 | from django.conf import settings 20 | from django.core.management.base import BaseCommand, CommandParser 21 | from django.core.mail import EmailMessage 22 | 23 | from scionlab.models.user import User 24 | 25 | 26 | class Command(BaseCommand): 27 | help = 'List or send a message to the unique emails of the users in alphabetical order' 28 | 29 | def add_arguments(self, parser: CommandParser) -> None: 30 | parser.add_argument('-a', '--action', type=str, required=True, 31 | choices=['list', 'send'], 32 | help='Action of the command: list or send') 33 | parser.add_argument('--subject', type=str, required=False, 34 | help='Subject of the email to send') 35 | parser.add_argument('--body', type=argparse.FileType('r'), default='-', 36 | help='Input file to read the body of the email from') 37 | parser.add_argument('--skip_blocks', type=int, default=0, 38 | help='skip N initial blocks of recipients. Useful to continue ' + 39 | 'sending after a crash') 40 | 41 | def handle(self, *args, **kwargs): 42 | action = kwargs['action'] 43 | if action == 'list': 44 | self.list() 45 | elif action == 'send': 46 | self.send(**kwargs) 47 | 48 | def list(self): 49 | # print active users emails: 50 | for email in self._obtain_active_emails(): 51 | print(email) 52 | 53 | # report user stats: 54 | inactive = [] 55 | active = [] 56 | for u in User.objects.all(): 57 | if not u.is_active: 58 | inactive.append(u) 59 | else: 60 | active.append(u) 61 | print(f'--------------------------- inactive: {len(inactive)}') 62 | print(f'--------------------------- active: {len(active)}') 63 | 64 | def send(self, **kwargs): 65 | # retrieve the email parameters, or complain 66 | if 'subject' not in kwargs or kwargs['subject'] is None: 67 | exit('Need a subject') 68 | subject = kwargs['subject'] 69 | if kwargs['body'] == sys.stdin: 70 | print('Type the body of the email, end with ^D') 71 | with kwargs['body'] as f: 72 | body = f.read() 73 | skip_blocks = kwargs['skip_blocks'] 74 | 75 | self._send( 76 | subject=subject, 77 | body=body, 78 | skip_blocks=skip_blocks, 79 | ) 80 | 81 | def _send(self, subject: str, body: str, skip_blocks: int): 82 | recipients = self._obtain_active_emails() 83 | block_count = (len(recipients) - 1) // settings.MAX_EMAIL_RECIPIENTS + 1 84 | for b in range(skip_blocks, block_count): 85 | # the recipients are a subset of the total 86 | dest = recipients[ 87 | b*settings.MAX_EMAIL_RECIPIENTS: 88 | (b + 1) * settings.MAX_EMAIL_RECIPIENTS] 89 | # prepare the email and send it 90 | msg = EmailMessage( 91 | subject, 92 | body, 93 | settings.DEFAULT_FROM_EMAIL, # From 94 | [], # To 95 | bcc=dest, 96 | ) 97 | msg.send() 98 | print(f'sent {b+1}/{block_count} -> {dest}') 99 | print('done') 100 | 101 | def _obtain_active_emails(self) -> List[str]: 102 | emails = User.objects.filter( 103 | is_active=True, 104 | ).values_list('email', flat=True).order_by('email').distinct() 105 | return emails 106 | 107 | 108 | def exit(msg: str): 109 | print(msg) 110 | sys.exit(1) 111 | -------------------------------------------------------------------------------- /scionlab/migrations/0002_colibri_service_schema.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.6 on 2021-10-06 03:33 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('scionlab', '0001_squashed_0011_pullap'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='service', 15 | name='type', 16 | field=models.CharField(choices=[('CS', 'Control Service'), ('SIG', 'SCION IP Gateway'), ('CO', 'Colibri Service'), ('BW', 'Bandwidth tester server'), ('PP', 'Pingpong server')], max_length=16), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /scionlab/migrations/0003_colibri_service_data.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.6 on 2021-10-06 03:38 2 | 3 | from django.db import migrations 4 | 5 | from scionlab.models.core import Service as ServiceNew 6 | 7 | 8 | def create_colibri_services(apps, schema_editor): 9 | """ 10 | will create a new colibri service located where the cs is 11 | Note that since the breaking changes update of January 2024, Service does not have 12 | a field called CO, so the literal value had to be written instead of ServiceNew.CO 13 | """ 14 | Service = apps.get_model('scionlab', 'Service') 15 | assert Service.objects.filter(type='CO').count() == 0 # should be empty 16 | for cs in Service.objects.filter(type=ServiceNew.CS): 17 | Service.objects.create(type='CO', AS=cs.AS, host=cs.host) 18 | Host = apps.get_model('scionlab', 'Host') 19 | Host.objects.bump_config() 20 | 21 | 22 | def delete_colibri_services(apps, schema_editor): 23 | """ deletes all colibri services """ 24 | Service = apps.get_model('scionlab', 'Service') 25 | Service.objects.filter(type='CO').delete() 26 | 27 | 28 | class Migration(migrations.Migration): 29 | 30 | dependencies = [ 31 | ('scionlab', '0002_colibri_service_schema'), 32 | ] 33 | 34 | operations = [ 35 | migrations.RunPython(create_colibri_services, # forward 36 | delete_colibri_services), # backward 37 | ] 38 | -------------------------------------------------------------------------------- /scionlab/migrations/0004_remove_link_bandwidth.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2 on 2021-11-25 02:46 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('scionlab', '0003_colibri_service_data'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='link', 15 | name='bandwidth', 16 | ), 17 | ] 18 | -------------------------------------------------------------------------------- /scionlab/migrations/0005_trc_pem.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import textwrap 16 | 17 | from django.db import migrations 18 | 19 | PEM_WIDTH = 64 # see e.g. RFC 7468 20 | TRC_PEM_HEADER = "-----BEGIN TRC-----\n" 21 | TRC_PEM_FOOTER = "-----END TRC-----\n" 22 | 23 | 24 | def convert_trcs_to_pem(apps, schema_editor): 25 | """ 26 | Convert TRC data from DER to PEM. 27 | As the DER blob is already stored as base64-encoded text in the DB, 28 | we only need to wrap lines and add the header and footer to turn it into a PEM. 29 | """ 30 | TRC = apps.get_model('scionlab', 'TRC') 31 | for trc in TRC.objects.all(): 32 | trc.trc = trc_base64der_to_pem(trc.trc) 33 | trc.save() 34 | 35 | 36 | def trc_base64der_to_pem(der: str) -> str: 37 | assert not der.startswith(TRC_PEM_HEADER) 38 | assert not der.endswith(TRC_PEM_HEADER) 39 | body = textwrap.fill(der, width=PEM_WIDTH) 40 | return TRC_PEM_HEADER + body + "\n" + TRC_PEM_FOOTER 41 | 42 | 43 | def revert_trcs_to_der(apps, schema_editor): 44 | """ 45 | Convert TRC from PEM back to base64-encoded DER. 46 | """ 47 | TRC = apps.get_model('scionlab', 'TRC') 48 | for trc in TRC.objects.all(): 49 | trc.trc = trc_pem_to_base64der(trc.trc) 50 | trc.save() 51 | 52 | 53 | def trc_pem_to_base64der(pem: str) -> str: 54 | assert pem.startswith(TRC_PEM_HEADER) 55 | assert pem.endswith(TRC_PEM_FOOTER) 56 | stripped = pem[len(TRC_PEM_HEADER):-len(TRC_PEM_FOOTER)] 57 | return stripped.replace("\n", "") 58 | 59 | 60 | class Migration(migrations.Migration): 61 | 62 | dependencies = [ 63 | ('scionlab', '0004_remove_link_bandwidth'), 64 | ] 65 | 66 | operations = [ 67 | migrations.RunPython(convert_trcs_to_pem, # forward 68 | revert_trcs_to_der), # backward 69 | ] 70 | -------------------------------------------------------------------------------- /scionlab/migrations/0006_bump_version_cs_hosts.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.1.6 on 2022-01-27 14:35 2 | 3 | from django.db import migrations 4 | 5 | from scionlab.models.core import Host, Service 6 | 7 | 8 | def bump_version(apps, schema_editor): 9 | """ 10 | Data migration: bump the version of those hosts that run 11 | the Control Service of Core ASes. 12 | They need to get new configuration from the Coordinator, due to a change in how the 13 | CA type is specified. 14 | """ 15 | Host.objects.bump_config(services__type=Service.CS, AS__is_core=True) 16 | 17 | 18 | class Migration(migrations.Migration): 19 | 20 | dependencies = [ 21 | ('scionlab', '0005_trc_pem'), 22 | ] 23 | 24 | operations = [ 25 | migrations.RunPython(bump_version, # forward 26 | lambda a, b: None), # backward 27 | ] 28 | -------------------------------------------------------------------------------- /scionlab/migrations/0007_remove_ssh_host.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.5 on 2022-02-09 05:18 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | def copy_ssh_host_to_label(apps, schema_editor): 7 | Host = apps.get_model("scionlab", "Host") 8 | Host.objects.filter(label=None).exclude(ssh_host=None).update(label=models.F('ssh_host')) 9 | 10 | 11 | class Migration(migrations.Migration): 12 | 13 | dependencies = [ 14 | ('scionlab', '0006_bump_version_cs_hosts'), 15 | ] 16 | 17 | operations = [ 18 | migrations.RunPython(copy_ssh_host_to_label, # forward 19 | lambda a, b: None), # backward 20 | migrations.RemoveField( 21 | model_name='host', 22 | name='ssh_host', 23 | ), 24 | migrations.AlterField( 25 | model_name='host', 26 | name='label', 27 | field=models.CharField(blank=True, help_text='Hostname. For managed hosts, this should correspond to the hostname used in the ansible inventory.', max_length=255, null=True), 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /scionlab/migrations/0008_remove_colibri.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2024-01-26 08:21 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('scionlab', '0007_remove_ssh_host'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterField( 14 | model_name='service', 15 | name='type', 16 | field=models.CharField(choices=[ 17 | ('CS', 'Control Service'), 18 | ('SIG', 'SCION IP Gateway'), 19 | ('BW', 'Bandwidth tester server'), 20 | ('PP', 'Pingpong server'), 21 | ], max_length=16), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /scionlab/migrations/0009_allhosts_dirty.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2024-06-12 02:31 2 | 3 | from django.db import migrations 4 | 5 | from scionlab.models.core import Host 6 | 7 | 8 | def bump_version(apps, schema_editor): 9 | """ 10 | Data migration: bump the version of all hosts. 11 | """ 12 | Host.objects.bump_config() 13 | 14 | class Migration(migrations.Migration): 15 | 16 | dependencies = [ 17 | ('scionlab', '0008_remove_colibri'), 18 | ] 19 | 20 | operations = [ 21 | migrations.RunPython(bump_version, # forward 22 | lambda a, b: None), # backward 23 | ] 24 | -------------------------------------------------------------------------------- /scionlab/migrations/0010_as_fabrid_enabled.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.20 on 2024-09-25 10:10 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('scionlab', '0009_allhosts_dirty'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='as', 15 | name='fabrid_enabled', 16 | field=models.BooleanField(default=False), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /scionlab/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/migrations/__init__.py -------------------------------------------------------------------------------- /scionlab/models/user.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """ 16 | :mod:`scionlab.models.user` --- Django models for SCIONLab users 17 | ================================================================ 18 | """ 19 | 20 | from django.contrib.auth.models import AbstractUser, BaseUserManager 21 | from django.conf import settings 22 | from django.core.exceptions import ValidationError 23 | from django.db import models 24 | from django.utils.translation import gettext_lazy as _ 25 | 26 | 27 | class UserManager(BaseUserManager): 28 | """Define a model manager for User model with no username field.""" 29 | 30 | use_in_migrations = True 31 | 32 | def _create_user(self, email, password, **extra_fields): 33 | """Create and save a User with the given email and password.""" 34 | if not email: 35 | raise ValueError('email must be set') 36 | email = self.normalize_email(email) 37 | user = self.model(email=email, **extra_fields) 38 | user.set_password(password) 39 | user.save() 40 | return user 41 | 42 | def create_user(self, email, password=None, **extra_fields): 43 | """Create and save a regular User with the given email and password.""" 44 | extra_fields.setdefault('is_staff', False) 45 | extra_fields.setdefault('is_superuser', False) 46 | return self._create_user(email, password, **extra_fields) 47 | 48 | def create_superuser(self, email, password, **extra_fields): 49 | """Create and save a SuperUser with the given email and password.""" 50 | extra_fields.setdefault('is_staff', True) 51 | extra_fields.setdefault('is_superuser', True) 52 | 53 | if extra_fields.get('is_staff') is not True: 54 | raise ValueError('Superuser must have is_staff=True.') 55 | if extra_fields.get('is_superuser') is not True: 56 | raise ValueError('Superuser must have is_superuser=True.') 57 | 58 | return self._create_user(email, password, **extra_fields) 59 | 60 | 61 | class User(AbstractUser): 62 | """ User model """ 63 | 64 | username = None 65 | # Use longer password field; salts from old coordinator are very long, too long to fit into the 66 | # default max_length 128 (as specified in django.contrib.auth.AbstractBaseUser). 67 | password = models.CharField(_('password'), max_length=255) 68 | email = models.EmailField(_('email address'), unique=True) 69 | organisation = models.CharField(max_length=255, null=False, blank=True) 70 | 71 | USERNAME_FIELD = 'email' 72 | REQUIRED_FIELDS = [] 73 | 74 | objects = UserManager() 75 | 76 | def max_num_ases(self): 77 | if self.is_staff: 78 | return settings.MAX_ASES_ADMIN 79 | return settings.MAX_ASES_USER 80 | 81 | def num_ases(self): 82 | return self.ases.count() 83 | 84 | def check_as_quota(self): 85 | """ 86 | Check if the user is allowed to create another AS. 87 | :raises: Validation error if quota is exceeded 88 | """ 89 | if self.num_ases() >= self.max_num_ases(): 90 | raise ValidationError("UserAS quota exceeded", 91 | code='user_as_quota_exceeded') 92 | -------------------------------------------------------------------------------- /scionlab/scion/as_ids.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """ 16 | :mod:`scionlab.scion.as_ids` --- Parsing/string formatting for SCION AS IDs 17 | =========================================================================== 18 | """ 19 | 20 | import re 21 | 22 | REGEX = re.compile(r"^([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,4}):([0-9a-fA-F]{1,4})$") 23 | 24 | 25 | def format(as_id_int): 26 | """ 27 | Format an AS-identifier given as a (48-bit) integer as a string 28 | of the form [0-ffff]:[0-ffff]:[0-ffff] 29 | :param str as_id_int: AS-identifier to format 30 | :returns: AS-identifier as string 31 | :raises: ValueError if `as_id_int` not in the valid range for an AS-identifier 32 | """ 33 | if as_id_int < 0 or as_id_int >= 2**48: 34 | raise ValueError('Invalid AS-ID') 35 | low = as_id_int & 0xffff 36 | mid = (as_id_int >> 16) & 0xffff 37 | hig = (as_id_int >> 32) & 0xffff 38 | return "%x:%x:%x" % (hig, mid, low) 39 | 40 | 41 | def parse(as_id_str): 42 | """ 43 | Parse an AS-identifier of the form [0-ffff]:[0-ffff]:[0-ffff] 44 | and return the corresponding (48-bit) integer representation. 45 | :param str as_id_str: AS-identifier to parse 46 | :returns: AS-identifier as integer 47 | """ 48 | match = REGEX.match(as_id_str) 49 | if not match: 50 | raise ValueError('Invalid AS-ID') 51 | low = int(match.group(3), 16) 52 | mid = int(match.group(2), 16) 53 | hig = int(match.group(1), 16) 54 | return hig << 32 | mid << 16 | low 55 | -------------------------------------------------------------------------------- /scionlab/scion/keys.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | """ 17 | :mod:`scionlab.scion.keys` --- SCION key creation and handling functions 18 | ======================================================================== 19 | """ 20 | 21 | import contextlib 22 | 23 | from cryptography.hazmat.primitives.asymmetric import ec 24 | from cryptography.hazmat.primitives import serialization 25 | from tempfile import NamedTemporaryFile 26 | from typing import cast 27 | 28 | from scionlab.scion.pkicommand import run_scion_pki 29 | 30 | 31 | def generate_key() -> ec.EllipticCurvePrivateKeyWithSerialization: 32 | """ Generate an elliptic curve private key """ 33 | # valid curves are: SECP256R1, SECP384R1, and secp521r1 34 | key = ec.generate_private_key(curve=ec.SECP256R1()) 35 | key = cast(ec.EllipticCurvePrivateKeyWithSerialization, key) # only for type hints 36 | return key 37 | 38 | 39 | def encode_key(key: ec.EllipticCurvePrivateKeyWithSerialization) -> str: 40 | """ Returns the key as a PEM formatted string """ 41 | return key.private_bytes( 42 | encoding=serialization.Encoding.PEM, 43 | format=serialization.PrivateFormat.PKCS8, 44 | encryption_algorithm=serialization.NoEncryption() 45 | ).decode("ascii") 46 | 47 | 48 | def decode_key(pem: str) -> ec.EllipticCurvePrivateKey: 49 | """ Returns an EllipticCurve key from its PEM encoding """ 50 | return serialization.load_pem_private_key(pem.encode("ascii"), password=None) 51 | 52 | 53 | def verify_key(key: str, cert: str): 54 | """ 55 | Verify that the certificate is valid, using the last TRC as anchor. 56 | The key is passed as a PEM string. 57 | The certificate is passed as a PEM string. 58 | Raises ScionPkiError if the certificate is not valid. 59 | """ 60 | with contextlib.ExitStack() as stack: 61 | key_file = stack.enter_context(NamedTemporaryFile(mode='wt', suffix=".key")) 62 | cert_file = stack.enter_context(NamedTemporaryFile(mode='wt', suffix=".crt")) 63 | files = [key_file, cert_file] 64 | for f, value in zip(files, [key, cert]): 65 | f.write(value) 66 | f.flush() 67 | _run_scion_pki_key('match', 'certificate', key_file.name, cert_file.name) 68 | 69 | 70 | def _run_scion_pki_key(*args, cwd=None, check=True): 71 | return run_scion_pki('key', *args, cwd=cwd, check=check) 72 | -------------------------------------------------------------------------------- /scionlab/scion/pkicommand.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """ 16 | :mod:`scionlab.scion.util` --- scion-pki execution 17 | ================================================== 18 | """ 19 | 20 | import subprocess 21 | 22 | from django.conf import settings 23 | 24 | 25 | def run_scion_pki(*args, cwd=None, check=True): 26 | try: 27 | return subprocess.run([settings.SCION_PKI_COMMAND, *args], 28 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT, 29 | encoding='utf-8', 30 | check=check, 31 | cwd=cwd) 32 | except subprocess.CalledProcessError as e: 33 | raise ScionPkiError(e) from None 34 | 35 | 36 | class ScionPkiError(subprocess.CalledProcessError): 37 | """ 38 | Wrapper for CalledProcessError (raised by subprocess.run on returncode != 0 if check=True), 39 | that includes the process output (stdout) in the __str__. 40 | """ 41 | def __init__(self, e): 42 | if isinstance(e, subprocess.CalledProcessError): 43 | super().__init__(e.returncode, e.cmd, e.output, e.stderr) 44 | else: 45 | super().__init__(returncode=0, cmd='<>', output=str(e)) 46 | 47 | def __str__(self): 48 | s = super().__str__() 49 | if self.output: 50 | s += "\nOutput:\n" 51 | s += self.output 52 | return s 53 | -------------------------------------------------------------------------------- /scionlab/settings/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/settings/__init__.py -------------------------------------------------------------------------------- /scionlab/settings/development.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | from .common import * 17 | 18 | SCIONLAB_SITE = 'http://localhost:8000' 19 | 20 | # ##### DEBUG CONFIGURATION ############################### 21 | DEBUG = True 22 | TEMPLATES[0]['OPTIONS']['debug'] = True 23 | CRISPY_FAIL_SILENTLY = not DEBUG 24 | 25 | # allow all hosts during development 26 | ALLOWED_HOSTS = ['*'] 27 | 28 | # ##### DATABASE CONFIGURATION ############################ 29 | if os.environ.get("POSTGRES_HOST"): 30 | db = { 31 | 'ENGINE': 'django.db.backends.postgresql_psycopg2', 32 | 'HOST': os.environ['POSTGRES_HOST'], 33 | 'PORT': os.environ['POSTGRES_PORT'], 34 | 'NAME': os.environ['POSTGRES_DB'], 35 | 'USER': os.environ['POSTGRES_USER'], 36 | 'PASSWORD': os.environ['POSTGRES_PASSWORD'], 37 | 'ATOMIC_REQUESTS': True, 38 | } 39 | else: 40 | db = { 41 | 'ENGINE': 'django.db.backends.sqlite3', 42 | 'NAME': os.path.join(BASE_DIR, 'run', 'dev.sqlite3'), 43 | 'ATOMIC_REQUESTS': True, 44 | } 45 | 46 | DATABASES = {'default': db} 47 | 48 | # ##### TESTING ########################################### 49 | 50 | # Output file for JUnit XML test report, for xmlrunner. 51 | # Enabled in CI by 52 | # --testrunner 'xmlrunner.extra.djangotestrunner.XMLTestRunner' 53 | TEST_OUTPUT_FILE_NAME = 'test-reports/django/results.xml' 54 | 55 | # ##### EXTENSIONS CONFIGURATION ########################## 56 | 57 | # django-recaptcha2 test keys 58 | RECAPTCHA_PUBLIC_KEY = '6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI' 59 | RECAPTCHA_PRIVATE_KEY = '6LeIxAcTAAAAAGG-vFI1TnRWxMZNFuojJ4WifJWe' 60 | 61 | # ##### MAILER CONFIGURATION ############################## 62 | EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' 63 | 64 | 65 | # ##### VPN CONFIG OVERRIDES ############################## 66 | VPN_CA_KEY_PASSWORD = 'sci0nl4b' 67 | VPN_CA_KEY_PATH = os.path.join(BASE_DIR, 'scionlab/fixtures/dev_root_ca_key.pem') 68 | VPN_CA_CERT_PATH = os.path.join(BASE_DIR, 'scionlab/fixtures/dev_root_ca_cert.pem') 69 | 70 | 71 | class VPNKeygenConfDev(VPNKeygenConf): 72 | KEY_SIZE = 1024 # Shortest workable keys for faster tests. 73 | # Note: 512-bit can be generated, but cannot initiate openvpn connection. 74 | 75 | 76 | VPN_KEYGEN_CONFIG = VPNKeygenConfDev 77 | -------------------------------------------------------------------------------- /scionlab/static/scionlab/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/static/scionlab/images/favicon.ico -------------------------------------------------------------------------------- /scionlab/static/scionlab/style.css: -------------------------------------------------------------------------------- 1 | /* Sticky footer styles 2 | -------------------------------------------------- */ 3 | html { 4 | position: relative; 5 | min-height: 100%; 6 | } 7 | body { 8 | margin-bottom: 7rem; 9 | } 10 | main { 11 | padding-top: 3rem; 12 | padding-bottom: 3rem; 13 | } 14 | footer { 15 | position: absolute; 16 | bottom: 0; 17 | width: 100%; 18 | height: 7rem; 19 | overflow: hidden; 20 | background-color: #f0f0f0; 21 | font-size: 85%; 22 | } 23 | 24 | 25 | /* Layout helpers for forms 26 | -------------------------------------------------- */ 27 | /* add sleek border for forms, give them a fixed max size so they don't span 28 | * the entire main container */ 29 | .form-box { 30 | border: 1px solid #d8dee2; 31 | border-radius: 5px; 32 | padding: 15px 20px; 33 | max-width: 700px; 34 | padding: 16px; 35 | } 36 | 37 | /* login form */ 38 | #login-form-panel { 39 | max-width: 360px; 40 | padding: 16px; 41 | margin: auto; 42 | } 43 | 44 | #login-form { 45 | background-color: #fff; 46 | } 47 | 48 | .text-lighter { 49 | font-weight: 300; 50 | } 51 | 52 | .nowrap { 53 | white-space: nowrap; 54 | } 55 | 56 | pre { 57 | background: #f4f4f4; 58 | padding: 8px 12px; 59 | border-radius: 6px; 60 | } 61 | 62 | /* Tweaks for forms (crispy-forms with bootstrap4 template pack) 63 | -------------------------------------------------- */ 64 | /* more compact error list */ 65 | .alert.alert-block ul { 66 | list-style-type: none; 67 | margin: 0; 68 | padding: 0; 69 | } 70 | 71 | /* red asterisk for required fields */ 72 | .asteriskField { 73 | color: #cb2431; 74 | padding-left: 3px; 75 | } 76 | 77 | /* hack to unhide feedback for invalid form fields in crispy/bootstrap-4. 78 | * Issue caused by weird output by crispy and bug/missing feature in bootstrap-4. 79 | * (see https://github.com/twbs/bootstrap/issues/23454). */ 80 | .invalid-feedback { 81 | display: block; 82 | } 83 | 84 | /* UserAS add/detail 85 | ------------------------------------*/ 86 | /* Since there is no :first-of-class in CSS, this is a trick to say "all but the first .attachment" */ 87 | .attachment ~ .attachment { 88 | margin-top: 16px; 89 | } 90 | /* Uncollapsed state */ 91 | #id_user_as_form #new-ap-collapser .fa-minus-circle, 92 | #id_user_as_form .bind-row-collapser .fa-minus-circle { 93 | display: inline; 94 | } 95 | #id_user_as_form #new-ap-collapser .fa-plus-circle, 96 | #id_user_as_form .bind-row-collapser .fa-plus-circle { 97 | display: none; 98 | } 99 | /* Collapsed state */ 100 | #id_user_as_form #new-ap-collapser.collapsed .fa-minus-circle, 101 | #id_user_as_form .bind-row-collapser.collapsed .fa-minus-circle { 102 | display: none; 103 | } 104 | #id_user_as_form #new-ap-collapser.collapsed .fa-plus-circle, 105 | #id_user_as_form .bind-row-collapser.collapsed .fa-plus-circle { 106 | display: inline; 107 | } 108 | 109 | /* "TESTING" ribbon over nav bar 110 | ------------------------------------*/ 111 | .ribbon { 112 | position: absolute; 113 | left: 0px; 114 | top: 0px; 115 | z-index: 1; 116 | width: 150px; 117 | height: 150px; 118 | pointer-events: none; 119 | } 120 | .ribbon span { 121 | font-size: 20px; 122 | color: #FFF; 123 | text-transform: uppercase; 124 | text-align: center; 125 | line-height: 30px; 126 | width: 200%; 127 | display: block; 128 | transform: rotate(-45deg); 129 | position: absolute; 130 | left: -50%; 131 | top: 50%; 132 | box-shadow: 0 3px 10px -5px rgba(0, 0, 0, 1); 133 | } 134 | 135 | .ribbon-yellow { 136 | background: linear-gradient(#F79E05 0%, #8F5408 100%); 137 | } 138 | .ribbon-red { 139 | background: linear-gradient(#F70505 0%, #8F0808 100%); 140 | } 141 | .ribbon-blue { 142 | background: linear-gradient(#2989d8 0%, #1e5799 100%); 143 | } 144 | -------------------------------------------------------------------------------- /scionlab/templates/503.html: -------------------------------------------------------------------------------- 1 | 2 | SCIONLab - Site Maintenance 3 | 9 | 10 |
11 |

We’ll be back soon!

12 |
13 |

14 | Sorry for the inconvenience, we’re performing some maintenance on SCIONLab at the moment. 15 | If you need to, you can always contact us via email. 16 |

17 |
18 |
19 | -------------------------------------------------------------------------------- /scionlab/templates/django_registration/activation_complete.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | 3 | {% block meta %}{% endblock meta %} 4 | 5 | {% block content %} 6 | Registration complete. 7 |
8 | You will be redirected to the login page... 9 | {% endblock content %} 10 | -------------------------------------------------------------------------------- /scionlab/templates/django_registration/activation_email_body.txt: -------------------------------------------------------------------------------- 1 | Hello {{ user.first_name }} {{ user.last_name }} 2 | 3 | Thank you for signing up with SCIONLab. To use the service you have to confirm your email address by clicking the link below. 4 | If you did not expect this email, please ignore it. 5 | 6 | {{ scheme }}://{{ site }}{% url 'django_registration_activate' activation_key=activation_key %} 7 | 8 | 9 | Kind regards, 10 | 11 | Your SCIONLab Team 12 | 13 | -- 14 | 15 | Learn more about SCIONLab at {{ scheme }}://{{ site }} 16 | -------------------------------------------------------------------------------- /scionlab/templates/django_registration/activation_email_subject.txt: -------------------------------------------------------------------------------- 1 | [SCIONLab] Verify your email address -------------------------------------------------------------------------------- /scionlab/templates/django_registration/activation_failed.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | 3 | {% block content %} 4 | Registration failed. 5 | This link is invalid, expired or was already used. 6 | {% endblock %} 7 | -------------------------------------------------------------------------------- /scionlab/templates/django_registration/registration_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | 3 | {% block title %}{{block.super}} Registration{% endblock %} 4 | {% block content_title %}SCIONLab Registration{% endblock %} 5 | 6 | {% block content %} 7 |

Your registration was successful!

8 |

9 | Before you can login, you must activate your account with the activation link sent to your 10 | email address. If you did not receive this email, please check your junk/spam 11 | folder. If you entered an incorrect email address, you will need to re-register 12 | with the correct email address. 13 |

14 | {% endblock content %} 15 | -------------------------------------------------------------------------------- /scionlab/templates/django_registration/registration_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load crispy_forms_tags %} 3 | {% load recaptcha2 %} 4 | 5 | {% block meta %}{% recaptcha_explicit_support %}{% endblock %} 6 | {% block title %}{{block.super}} Registration{% endblock %} 7 | {% block content_title %}SCIONLab Registration{% endblock %} 8 | 9 | {% block content %} 10 | 13 |
14 | {% crispy form %} 15 |
16 | 17 | 18 | {% recaptcha_explicit_init 'en' %} 19 | 20 | {% endblock content %} 21 | -------------------------------------------------------------------------------- /scionlab/templates/django_registration/registration_resend.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block title %}{{ block.super }} - Resend activation link{% endblock title%} 5 | {% block content_title %}Resend activation link{% endblock content_title%} 6 | 7 | {% block content %} 8 | 9 |

Before you can login, you must activate your account with the activation link sent to your 10 | email address. If you did not receive this email, please check your junk/spam 11 | folder. If you entered an incorrect email address, you will need to re-register 12 | with the correct email address.

13 |

Didn't receive the account activation link? Enter your email address below, and we'll email it again.

14 |
{% csrf_token %} 15 | {{ form|crispy }} 16 | 17 |
18 | {% endblock content %} 19 | -------------------------------------------------------------------------------- /scionlab/templates/registration/logged_out.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | 3 | {% block content %} 4 |

Thank you and goodbye!

5 |

Go back to login

6 | {% endblock content %} 7 | -------------------------------------------------------------------------------- /scionlab/templates/registration/login.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block title %}{{ block.super }} - Login{% endblock title%} 5 | 6 | {% block content %} 7 |
8 |
{% csrf_token %} 9 | {{ form|crispy }} 10 | 11 | Forgot password? 12 |
13 |
14 | New to SCIONLab? Create an account. 15 |
16 |
17 | {% endblock content %} 18 | -------------------------------------------------------------------------------- /scionlab/templates/registration/password_change_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | 3 | {% block title %}{{ block.super }} - Password change{% endblock title%} 4 | {% block content_title %}Password change{% endblock content_title%} 5 | 6 | {% block content %} 7 |

Your password was changed.

8 | {% endblock content %} 9 | -------------------------------------------------------------------------------- /scionlab/templates/registration/password_change_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block title %}{{ block.super }} - Password change{% endblock title%} 5 | {% block content_title %}Password change{% endblock content_title%} 6 | 7 | {% block content %} 8 |

Please enter your old password, for security's sake, and then enter your new password twice so we can verify you typed it in correctly.

9 |
{% csrf_token %} 10 | {{ form|crispy }} 11 | 12 |
13 | {% endblock content %} 14 | -------------------------------------------------------------------------------- /scionlab/templates/registration/password_reset_complete.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | 3 | {% block title %}{{ block.super }} - Password reset{% endblock title%} 4 | {% block content_title %}Password reset{% endblock content_title%} 5 | 6 | {% block content %} 7 |

Your password has been set. You may go ahead and log in now.

8 | 9 |

Log in

10 | {% endblock content %} 11 | -------------------------------------------------------------------------------- /scionlab/templates/registration/password_reset_confirm.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block title %}{{ block.super }} - Password reset confirmation{% endblock title%} 5 | {% block content_title %}Password reset confirmation{% endblock content_title%} 6 | 7 | {% block content %} 8 | {% if validlink %} 9 |

Please enter your new password twice so we can verify you typed it in correctly.

10 |
{% csrf_token %} 11 | {{ form|crispy }} 12 | 13 |
14 | {% else %} 15 |

The password reset link was invalid, possibly because it has already been used. Please request a new password reset.

16 | {% endif %} 17 | {% endblock content %} 18 | -------------------------------------------------------------------------------- /scionlab/templates/registration/password_reset_done.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | 3 | {% block title %}{{ block.super }} - Password reset{% endblock title%} 4 | {% block content_title %}Password reset{% endblock content_title%} 5 | 6 | {% block content %} 7 |

We've emailed you instructions for setting your password, if an account exists with the email you entered. You should receive them shortly.

8 | 9 |

If you don't receive an email, please make sure you've entered the address you registered with, and check your spam folder.

10 | {% endblock content %} 11 | -------------------------------------------------------------------------------- /scionlab/templates/registration/password_reset_email.html: -------------------------------------------------------------------------------- 1 | Hello {{ user.first_name }} {{ user.last_name }} 2 | 3 | You're receiving this email because you requested a password reset for your SCIONLab user account. 4 | Please choose a new password using the link below. 5 | 6 | {{ protocol }}://{{ domain }}{% url 'password_reset_confirm' uidb64=uid token=token %} 7 | 8 | 9 | Kind regards, 10 | 11 | Your SCIONLab Team 12 | 13 | -- 14 | 15 | Learn more about SCIONLab at {{ protocol }}://{{ domain }} 16 | -------------------------------------------------------------------------------- /scionlab/templates/registration/password_reset_form.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load crispy_forms_tags %} 3 | 4 | {% block title %}{{ block.super }} - Password reset{% endblock title%} 5 | {% block content_title %}Password reset{% endblock content_title%} 6 | 7 | {% block content %} 8 |

Forgotten your password? Enter your email address below, and we'll email you instructions for setting a new one.

9 |
{% csrf_token %} 10 | {{ form|crispy }} 11 | 12 |
13 | {% endblock content %} 14 | -------------------------------------------------------------------------------- /scionlab/templates/registration/password_reset_subject.txt: -------------------------------------------------------------------------------- 1 | [SCIONLab] Password reset -------------------------------------------------------------------------------- /scionlab/templates/scionlab/partials/installation_type_accordion.html: -------------------------------------------------------------------------------- 1 | {# Based on django-crispy-forms/crispy_forms/templates/bootstrap4/layout/radioselect.html #} 2 | {# Simplified, removed many of the unused checks and attributes no errors for installation type #} 3 | 4 | {# Expects `accordion_templates` in the context, as a list of one template-name for each field choice #} 5 | 6 | {# Builds a field with the following structure: #} 7 | {# - field div #} 8 | {# - label #} 9 | {# - div accordion, responsible for opening _one_ of the collapse-items below (bootstrap) #} 10 | {# - div card #} 11 | {# for each option: #} 12 | {# - card header #} 13 | {# - radio input and label #} 14 | {# - div collapse, show for option initially selected #} 15 | {# - card body #} 16 | {# - include the template for this choice, specified in `accordion_templates` #} 17 | 18 | {% load crispy_forms_filters %} 19 | {% load crispy_forms_field %} 20 | {% load l10n %} 21 | 22 | {# from crispy field.html, stripped down #} 23 |
24 | {# the label for the field #} 25 | {% if field.label and form_show_labels %} 26 | 29 | {% endif %} 30 | 31 | {# the radioselect part #} 32 |
33 | {% for choice in field.field.choices %} 34 |
35 |
36 |
37 | 38 | 41 |
42 |
43 |
44 |
45 | {% include accordion_templates|slice:forloop.counter|last %} {# WHAT THE F... is this the only way to access a list by index??? #} 46 |
47 |
48 |
49 | {% endfor %} 50 |
51 |
52 | 53 | {% include "scionlab/partials/installation_type_accordion_script.html" %} 54 | -------------------------------------------------------------------------------- /scionlab/templates/scionlab/partials/installation_type_accordion_script.html: -------------------------------------------------------------------------------- 1 | 14 | -------------------------------------------------------------------------------- /scionlab/templates/scionlab/partials/installation_type_pkg.html: -------------------------------------------------------------------------------- 1 |

2 | To install SCION from packages, please follow the steps in the tutorials. 3 | As a short summary, the commands to install the packages on Ubuntu are: 4 |

5 |
 6 | sudo apt-get install apt-transport-https ca-certificates
 7 | echo "deb [trusted=yes] https://packages.netsec.inf.ethz.ch/debian all main" | sudo tee /etc/apt/sources.list.d/scionlab.list
 8 | sudo apt-get update
 9 | sudo apt-get install scionlab
10 | 
11 | 12 |

13 | Once you've saved your setup, you can deploy the configuration to your machine. The easiest way is to run the scionlab-config script: 14 |

15 |
16 | {% if host_id and host_secret %}
17 | sudo scionlab-config --host-id={{host_id}} --host-secret={{host_secret}}
18 | {% else %}
19 | # host-id/secret will be displayed here after saving your configuration
20 | sudo scionlab-config --host-id=<...> --host-secret=<...>
21 | {% endif %}
22 | 
23 | 24 |

25 | Alternatively, you can download the configuration tarfile with the link below and manually unpack it following the instructions in the tutorials. 26 |

27 | -------------------------------------------------------------------------------- /scionlab/templates/scionlab/partials/installation_type_src.html: -------------------------------------------------------------------------------- 1 |

2 | Build SCION from sources following the instructions in the README and the tutorials. 3 |

4 | 5 |

6 | Once you've saved your setup, you can download the configuration tarfile with the link below. After unpacking the gen/ to the SCION source directory, you can start your AS using the development scripts (scion.sh and supervisor/supervisor.sh), as described in the tutorial. 7 |

8 | -------------------------------------------------------------------------------- /scionlab/templates/scionlab/partials/installation_type_vm.html: -------------------------------------------------------------------------------- 1 | Run your SCIONLab AS in a Vagrant VM. 2 | Once you've saved your setup, you can download a tarfile with the link below. The Vagrantfile in this archive is all you need to start your AS: 3 | 4 |
 5 | cd [..directory with unpacked Vagrantfile..]
 6 | vagrant up
 7 | 
8 | 9 | 13 | -------------------------------------------------------------------------------- /scionlab/templates/scionlab/user.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load static %} 3 | 4 | {% block title %}{{block.super}} - My ASes{% endblock %} 5 | {% block content_title %}My SCIONLab ASes{% endblock %} 6 | 7 | {% block content %} 8 |
9 |

10 | {% if object_list.count == 0 %} 11 | You currently have no registered SCIONLab ASes. 12 | {% else %} 13 | You currently have {{ object_list.count }} SCIONLab AS{{ object_list.count|pluralize:"es" }}. 14 | {% endif %} 15 |

16 |

17 | {% if object_list.count < request.user.max_num_ases %} 18 | You can create up to {{ request.user.max_num_ases }} ASes with your account. 19 | Please use the button below to create a new AS. 20 | {% else %} 21 | You have reached the maximum number of ASes and cannot create further ones. 22 | {% endif %} 23 |

24 |
25 | 26 | 27 | {% if object_list %} 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | {% for user_as in object_list %} 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 57 | 60 | 74 | 75 | {% endfor %} 76 | 77 |
#AS IDLabelISDParent ASesIP/PortActiveConfiguration
{{ forloop.counter }} {{ user_as.isd_as_str }}{{ user_as.label|default:"-" }}{{ user_as.useras.isd.label }}{{ user_as.attachment_points_labels|join:', ' }}{{ user_as.ip_port_labels|join:', ' }} 51 | {% if user_as.is_active %} 52 | Active 53 | {% else %} 54 | Inactive 55 | {% endif %} 56 | 58 | 59 |
78 | {% endif %} 79 | 80 | 81 | {% if object_list.count < request.user.max_num_ases %} 82 | Create a new SCIONLab AS 83 | {% endif %} 84 | {% endblock content %} 85 | -------------------------------------------------------------------------------- /scionlab/templates/scionlab/user_as_add.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load static %} 3 | {% load crispy_forms_tags %} 4 | 5 | {% block title %}Create User AS{% endblock %} 6 | 7 | {% block content %} 8 |
9 |
10 | {% csrf_token %} 11 | {% crispy form %} 12 | 13 |

Provider link

14 | {{ form.attachment_conf_form_set.management_form|crispy }} 15 | {{ form.attachment_conf_form_set|as_crispy_errors }} 16 | {% for form in form.attachment_conf_form_set %} 17 | {% crispy form %} 18 | {% endfor %} 19 | 20 |
21 |
22 | 23 | {% include "scionlab/partials/user_as_form_script.html" with attachment_points=attachment_points %} 24 | 25 | {% endblock content %} 26 | -------------------------------------------------------------------------------- /scionlab/templates/scionlab/user_as_confirm_delete.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | 3 | {% block title %}Delete User AS{% endblock %} 4 | 5 | {% block content %} 6 |

Are you sure?

7 | 8 |
{% csrf_token %} 9 |

Are you sure you want to delete AS {{ object }}?

10 | 11 | 12 | 13 | No, take me back 14 | 15 |
16 | {% endblock content %} 17 | -------------------------------------------------------------------------------- /scionlab/templates/scionlab/user_as_details.html: -------------------------------------------------------------------------------- 1 | {% extends 'scionlab/base.html' %} 2 | {% load static %} 3 | {% load crispy_forms_tags %} 4 | 5 | {% block title %}User AS{% endblock %} 6 | {% block content_title %}AS {{object.as_id }}{% endblock %} 7 | 8 | {% block content %} 9 |
10 | {% csrf_token %} 11 | {% crispy form %} 12 | 13 |

Provider links

14 | {{ form.attachment_conf_form_set.management_form|crispy }} 15 | {{ form.attachment_conf_form_set|as_crispy_errors }} 16 | {% for form in form.attachment_conf_form_set %} 17 | {% crispy form %} 18 | {% endfor %} 19 |
20 | 21 | {% if object.fixed_links %} 22 |

Fixed links

23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | {% for l in object.fixed_links %} 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | {% endfor %} 44 | 45 |
TypeAS AIP/Port AAS BIP/Port B
{{ l.type }}{{ l.interfaceA.AS }}{{ l.interfaceA.get_public_ip }}:{{l.interfaceA.public_port}}{{ l.interfaceB.AS }}{{ l.interfaceB.get_public_ip }}:{{l.interfaceB.public_port}}
46 | {% endif %} 47 | 48 |
49 | 50 | 51 | {% if object.is_active %} 52 | 53 | 54 | Download configuration 55 | 56 | 57 |
{% csrf_token %} 58 |
59 | {% else %} 60 | 64 |
{% csrf_token %} 65 |
66 | {% endif %} 67 | 68 | {% include "scionlab/partials/user_as_form_script.html" with attachment_points=attachment_points %} 69 | 70 | {% endblock content %} 71 | -------------------------------------------------------------------------------- /scionlab/tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/__init__.py -------------------------------------------------------------------------------- /scionlab/tests/data/test_config_tar/user_as_17.yml: -------------------------------------------------------------------------------- 1 | README.md: |- 2 | content_not_checked 3 | Vagrantfile: |- 4 | # -*- mode: ruby -*- 5 | # vi: set ft=ruby : 6 | 7 | ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox' 8 | 9 | Vagrant.require_version ">= 1.8.5" 10 | 11 | Vagrant.configure(2) do |config| 12 | $setup_scion = <<-'SCRIPT' 13 | set -e 14 | 15 | echo 'install and update system packages' 16 | export DEBIAN_FRONTEND=noninteractive; export LC_ALL=C 17 | apt-get update > /dev/null 18 | apt-get upgrade -y -qq 19 | apt-get install -y -qq apt-transport-https ca-certificates unattended-upgrades 20 | 21 | echo 'install SCIONLab' 22 | echo "deb [trusted=yes] https://packages.netsec.inf.ethz.ch/debian all main" > /etc/apt/sources.list.d/scionlab.list 23 | apt-get update > /dev/null 24 | apt-get install -y -qq scionlab 25 | 26 | echo 'configure time sync' 27 | printf '%s\n' \ 28 | '[Time]' \ 29 | 'NTP=0.ubuntu.pool.ntp.org 1.ubuntu.pool.ntp.org 2.ubuntu.pool.ntp.org 3.ubuntu.pool.ntp.org' \ 30 | 'FallbackNTP=ntp.ubuntu.com' \ 31 | > /etc/systemd/timesyncd.conf 32 | systemctl restart systemd-timesyncd.service 33 | 34 | echo 'configure unattended upgrades for all system and SCION package upgrades' 35 | printf '%s\n' \ 36 | 'Unattended-Upgrade::Origins-Pattern { "origin=*"; };' \ 37 | 'Unattended-Upgrade::Automatic-Reboot "true";' \ 38 | 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' \ 39 | 'APT::Periodic::Update-Package-Lists "always";' \ 40 | 'APT::Periodic::Unattended-Upgrade "always";' \ 41 | > /etc/apt/apt.conf.d/51unattended-upgrades-scionlab-tweaks 42 | mkdir /etc/systemd/system/apt-daily.timer.d/ || true 43 | printf '%s\n' \ 44 | '[Timer]' \ 45 | 'OnCalendar=' \ 46 | 'OnCalendar=07,19:00' \ 47 | 'RandomizedDelaySec=0' \ 48 | > /etc/systemd/system/apt-daily.timer.d/override.conf 49 | mkdir /etc/systemd/system/apt-daily-upgrade.timer.d/ || true 50 | printf '%s\n' \ 51 | '[Timer]' \ 52 | 'OnCalendar=' \ 53 | 'OnCalendar=07,19:15' \ 54 | 'RandomizedDelaySec=0' \ 55 | > /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf 56 | systemctl daemon-reload 57 | systemctl restart apt-daily.timer apt-daily-upgrade.timer 58 | 59 | # Fetch configuration from coordinator and start SCION 60 | scionlab-config --host-id=61eb4405c7d94c6a97c6be80deec058b --host-secret=db68ab32553f45d8a8aa1f6909f6d6a6 --url=http://localhost:8000 61 | SCRIPT 62 | 63 | config.vm.box = "ubuntu/bionic64" 64 | # forward "webapp" port: 65 | config.vm.network "forwarded_port", guest: 8000, host: 8000, protocol: "tcp" 66 | config.vm.provider "virtualbox" do |vb| 67 | vb.customize [ "setextradata", :id, "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled", 1 ] 68 | vb.customize [ "modifyvm", :id, "--uartmode1", "file", File::NULL ] 69 | vb.memory = "2048" 70 | vb.name = "SCIONLabVM-ffaa_1_1" 71 | end 72 | config.vm.hostname = "scionlab-ffaa-1-1" 73 | config.vm.provision "shell", privileged: true, inline: $setup_scion 74 | end 75 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_config_tar/user_as_18.yml: -------------------------------------------------------------------------------- 1 | README.md: |- 2 | content_not_checked 3 | Vagrantfile: |- 4 | # -*- mode: ruby -*- 5 | # vi: set ft=ruby : 6 | 7 | ENV['VAGRANT_DEFAULT_PROVIDER'] = 'virtualbox' 8 | 9 | Vagrant.require_version ">= 1.8.5" 10 | 11 | Vagrant.configure(2) do |config| 12 | $setup_scion = <<-'SCRIPT' 13 | set -e 14 | 15 | echo 'install and update system packages' 16 | export DEBIAN_FRONTEND=noninteractive; export LC_ALL=C 17 | apt-get update > /dev/null 18 | apt-get upgrade -y -qq 19 | apt-get install -y -qq apt-transport-https ca-certificates unattended-upgrades 20 | 21 | echo 'install SCIONLab' 22 | echo "deb [trusted=yes] https://packages.netsec.inf.ethz.ch/debian all main" > /etc/apt/sources.list.d/scionlab.list 23 | apt-get update > /dev/null 24 | apt-get install -y -qq scionlab 25 | 26 | echo 'configure time sync' 27 | printf '%s\n' \ 28 | '[Time]' \ 29 | 'NTP=0.ubuntu.pool.ntp.org 1.ubuntu.pool.ntp.org 2.ubuntu.pool.ntp.org 3.ubuntu.pool.ntp.org' \ 30 | 'FallbackNTP=ntp.ubuntu.com' \ 31 | > /etc/systemd/timesyncd.conf 32 | systemctl restart systemd-timesyncd.service 33 | 34 | echo 'configure unattended upgrades for all system and SCION package upgrades' 35 | printf '%s\n' \ 36 | 'Unattended-Upgrade::Origins-Pattern { "origin=*"; };' \ 37 | 'Unattended-Upgrade::Automatic-Reboot "true";' \ 38 | 'Unattended-Upgrade::Automatic-Reboot-Time "02:00";' \ 39 | 'APT::Periodic::Update-Package-Lists "always";' \ 40 | 'APT::Periodic::Unattended-Upgrade "always";' \ 41 | > /etc/apt/apt.conf.d/51unattended-upgrades-scionlab-tweaks 42 | mkdir /etc/systemd/system/apt-daily.timer.d/ || true 43 | printf '%s\n' \ 44 | '[Timer]' \ 45 | 'OnCalendar=' \ 46 | 'OnCalendar=07,19:00' \ 47 | 'RandomizedDelaySec=0' \ 48 | > /etc/systemd/system/apt-daily.timer.d/override.conf 49 | mkdir /etc/systemd/system/apt-daily-upgrade.timer.d/ || true 50 | printf '%s\n' \ 51 | '[Timer]' \ 52 | 'OnCalendar=' \ 53 | 'OnCalendar=07,19:15' \ 54 | 'RandomizedDelaySec=0' \ 55 | > /etc/systemd/system/apt-daily-upgrade.timer.d/override.conf 56 | systemctl daemon-reload 57 | systemctl restart apt-daily.timer apt-daily-upgrade.timer 58 | 59 | # Fetch configuration from coordinator and start SCION 60 | scionlab-config --host-id=7b0c0004f6254d29b3f7c77d31010599 --host-secret=36c82ae4d42045ad942c0959924fffea --url=http://localhost:8000 61 | SCRIPT 62 | 63 | config.vm.box = "ubuntu/bionic64" 64 | # forward border router ports: 65 | config.vm.network "forwarded_port", guest: 54321, host: 54321, protocol: "udp" 66 | # forward "webapp" port: 67 | config.vm.network "forwarded_port", guest: 8000, host: 8000, protocol: "tcp" 68 | config.vm.provider "virtualbox" do |vb| 69 | vb.customize [ "setextradata", :id, "VBoxInternal/Devices/VMMDev/0/Config/GetHostTimeDisabled", 1 ] 70 | vb.customize [ "modifyvm", :id, "--uartmode1", "file", File::NULL ] 71 | vb.memory = "2048" 72 | vb.name = "SCIONLabVM-ffaa_1_2" 73 | end 74 | config.vm.hostname = "scionlab-ffaa-1-2" 75 | config.vm.provision "shell", privileged: true, inline: $setup_scion 76 | end 77 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/README.txt: -------------------------------------------------------------------------------- 1 | Data generated following the specs at 2 | https://scion.docs.anapaya.net/en/latest/cryptography/trc-signing-ceremony-phases.html 3 | 4 | TRC verified with: scion-pki trcs verify --anchor trc-1.trc trc-1.trc 5 | 6 | To recreate: 7 | python manage.py shell -c 'from scionlab.tests.data.test_scion_trcs.regenerate import regenerate; regenerate()' 8 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/ca-ff00_0_110.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICqDCCAk2gAwIBAgIUVseW+w8DkU0YrHvrvoVkY8z8rIEwCgYIKoZIzj0EAwQw 3 | gaMxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 4 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxNDAyBgNVBAMMKzEtZmYw 5 | MDowOjExMCBIaWdoIFNlY3VyaXR5IFJvb3QgQ2VydGlmaWNhdGUxHTAbBgsrBgEE 6 | AYOwHAECAQwMMS1mZjAwOjA6MTEwMB4XDTIwMTExMjA4MDAwMFoXDTIwMTExMzA4 7 | MDAwMFowgZoxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8 8 | cmljaDEPMA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxKzApBgNVBAMM 9 | IjEtZmYwMDowOjExMCBTZWN1cmUgQ0EgQ2VydGlmaWNhdGUxHTAbBgsrBgEEAYOw 10 | HAECAQwMMS1mZjAwOjA6MTEwMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEaDfg 11 | 6DmAHJDOGKdjJ7lX4sqBvnncJCF9XY6SfKvakVTbnHYs1JaqHm2NWGBZChH799WM 12 | 3tERcc0Ec+qEMyvXd6NmMGQwEgYDVR0TAQH/BAgwBgEB/wIBADAOBgNVHQ8BAf8E 13 | BAMCAQYwHQYDVR0OBBYEFGxVON0WtbNzLd/wDc1PQ2mL5pojMB8GA1UdIwQYMBaA 14 | FGYzr6kNFlgrcykrFbiL7D+MH9ZhMAoGCCqGSM49BAMEA0kAMEYCIQDGqpAfIXbp 15 | 0cNFt0TGp43YWDd6XvEDHpJnqmrrGESyhwIhAMTAGAO3slJ36ybqTflm4fWhnvaJ 16 | 1iMcX/kAcwNEEZtO 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/ca-ff00_0_110.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIOXNVqJxhmnyFW03ES3RSDJvw9FfHUDe08H4D+gtiuKMoAoGCCqGSM49 3 | AwEHoUQDQgAEaDfg6DmAHJDOGKdjJ7lX4sqBvnncJCF9XY6SfKvakVTbnHYs1Jaq 4 | Hm2NWGBZChH799WM3tERcc0Ec+qEMyvXdw== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/ca-ff00_0_210.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICjTCCAjOgAwIBAgIUd7Kw7JTM8Hr5v1+fBIKE5AluDJMwCgYIKoZIzj0EAwQw 3 | gZYxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 4 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxJzAlBgNVBAMMHkhpZ2gg 5 | U2VjdXJpdHkgUm9vdCBDZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7AcAQIBDAwxLWZm 6 | MDA6MDoyMTAwHhcNMjAxMTEyMDgwMDAwWhcNMjAxMTEzMDgwMDAwWjCBjTELMAkG 7 | A1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xyaWNoMQ8wDQYDVQQK 8 | DAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzEeMBwGA1UEAwwVU2VjdXJlIENBIENl 9 | cnRpZmljYXRlMR0wGwYLKwYBBAGDsBwBAgEMDDEtZmYwMDowOjIxMDBZMBMGByqG 10 | SM49AgEGCCqGSM49AwEHA0IABPzYWpK90TXSZwULizLvjUTwx7o7HCWg5UAlLWD+ 11 | kmEzr85YrhVuDmihglp5aL5lnpj3dzCZ0/DIfiG6ec+cInajZjBkMBIGA1UdEwEB 12 | /wQIMAYBAf8CAQAwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBSFdoeGMwoWNG6I 13 | o+fgU+f/NrLFPTAfBgNVHSMEGDAWgBQSk9s2w29fP/M0JWIv3nFODE2gWzAKBggq 14 | hkjOPQQDBANIADBFAiBn4MHoXgkG4mowtv1nlL0TV5bcC6UX9bMugadojoWYhwIh 15 | ALkMOKTeHWhGsGJ4cx1BytMnB3RJH7aWX/FJgZCFd2GN 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/ca-ff00_0_210.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIJ2zzQ9z2V1Xcgm8Lu//O6E9CwZD1X3QoLn2/V4flCZ3oAoGCCqGSM49 3 | AwEHoUQDQgAE/Nhakr3RNdJnBQuLMu+NRPDHujscJaDlQCUtYP6SYTOvzliuFW4O 4 | aKGCWnlovmWemPd3MJnT8Mh+Ibp5z5widg== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-1-config.toml: -------------------------------------------------------------------------------- 1 | isd = 1 2 | description = "SCIONLab TRC for ISD 1" 3 | base_version = 1 4 | serial_version = 1 5 | voting_quorum = 1 6 | grace_period = "0s" 7 | authoritative_ases = [ "ff00:0:110",] 8 | core_ases = [ "ff00:0:110",] 9 | cert_files = [ "voting-sensitive-ff00_0_110.crt", "voting-regular-ff00_0_110.crt", "root-ff00_0_110.crt",] 10 | no_trust_reset = false 11 | 12 | [validity] 13 | not_before = 1605168000 # 12.11.2020 8:00am UTC 14 | validity = "1800s" # could go up to 3600s as the certificates have 24h validity 15 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-1-signed-regular-ff00_0_110.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-1-signed-regular-ff00_0_110.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-1-signed-sensitive-ff00_0_110.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-1-signed-sensitive-ff00_0_110.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-1.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-1.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-2-config.toml: -------------------------------------------------------------------------------- 1 | isd = 1 2 | description = "SCIONLab TRC for ISD 1" 3 | base_version = 1 4 | serial_version = 2 5 | voting_quorum = 1 6 | grace_period = "0s" 7 | authoritative_ases = [ "ff00:0:110",] 8 | core_ases = [ "ff00:0:110",] 9 | cert_files = [ "voting-sensitive-ff00_0_110.crt", "voting-regular-ff00_0_110.crt", "root-ff00_0_110.crt",] 10 | no_trust_reset = false 11 | votes = [1] 12 | 13 | [validity] 14 | not_before = 1605168000 # 12.11.2020 8:00am UTC 15 | validity = "1800s" # could go up to 3600s as the certificates have 24h validity 16 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-2-signed-regular-ff00_0_110.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-2-signed-regular-ff00_0_110.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-2.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-2.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-3-config.toml: -------------------------------------------------------------------------------- 1 | isd = 1 2 | description = "SCIONLab TRC for ISD 1" 3 | base_version = 1 4 | serial_version = 3 5 | voting_quorum = 1 6 | grace_period = "3600s" 7 | authoritative_ases = [ "ff00:0:110", "ff00:0:210"] 8 | core_ases = [ "ff00:0:110", "ff00:0:210" ] 9 | cert_files = [ "voting-sensitive-ff00_0_110.crt", "voting-regular-ff00_0_110.crt", "root-ff00_0_110.crt", "voting-sensitive-ff00_0_210.crt", "voting-regular-ff00_0_210.crt", "root-ff00_0_210.crt"] 10 | no_trust_reset = false 11 | votes = [0] 12 | 13 | [validity] 14 | not_before = 1605168000 # 12.11.2020 8:00am UTC 15 | validity = "1800s" # could go up to 3600s as the certificates have 24h validity 16 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-3-signed-regular-ff00_0_210.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-3-signed-regular-ff00_0_210.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-3-signed-sensitive-ff00_0_110.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-3-signed-sensitive-ff00_0_110.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-3-signed-sensitive-ff00_0_210.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-3-signed-sensitive-ff00_0_210.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/payload-3.der: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/tests/data/test_scion_trcs/payload-3.der -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/root-ff00_0_110.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICsjCCAlegAwIBAgIUDGlEikuY+C5YRiuVdxrvCYuH42UwCgYIKoZIzj0EAwQw 3 | gaMxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 4 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxNDAyBgNVBAMMKzEtZmYw 5 | MDowOjExMCBIaWdoIFNlY3VyaXR5IFJvb3QgQ2VydGlmaWNhdGUxHTAbBgsrBgEE 6 | AYOwHAECAQwMMS1mZjAwOjA6MTEwMB4XDTIwMTExMjA4MDAwMFoXDTIwMTExMzA4 7 | MDAwMFowgaMxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8 8 | cmljaDEPMA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxNDAyBgNVBAMM 9 | KzEtZmYwMDowOjExMCBIaWdoIFNlY3VyaXR5IFJvb3QgQ2VydGlmaWNhdGUxHTAb 10 | BgsrBgEEAYOwHAECAQwMMS1mZjAwOjA6MTEwMFkwEwYHKoZIzj0CAQYIKoZIzj0D 11 | AQcDQgAE8DlBkDjj3UHX+hksr29ifEDwmu0MhdA4SDpuLqoG//kEFPLgF7sbVMNv 12 | 23EQmISAyoP5Djblm4+ZeIa+rVsrSqNnMGUwEgYDVR0TAQH/BAgwBgEB/wIBATAO 13 | BgNVHQ8BAf8EBAMCAQYwHQYDVR0OBBYEFGYzr6kNFlgrcykrFbiL7D+MH9ZhMCAG 14 | A1UdJQQZMBcGCysGAQQBg7AcAQMDBggrBgEFBQcDCDAKBggqhkjOPQQDBANJADBG 15 | AiEA9/rKKlAV44FjQqHhC2AKioKowoPG3rqSxeVlRQJU3fcCIQD8vwQkj5knaIlA 16 | M78B0iGHqLcXT9WStI4UsHAM1DGweg== 17 | -----END CERTIFICATE----- 18 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/root-ff00_0_110.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIPwZPLlUZWO1rIBj59jybs6P1dx9rcXF7pk6OsLRrcpGoAoGCCqGSM49 3 | AwEHoUQDQgAE8DlBkDjj3UHX+hksr29ifEDwmu0MhdA4SDpuLqoG//kEFPLgF7sb 4 | VMNv23EQmISAyoP5Djblm4+ZeIa+rVsrSg== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/root-ff00_0_210.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIClzCCAj2gAwIBAgIUQlcoL5ve6otereiPVnLaa9V+0YYwCgYIKoZIzj0EAwQw 3 | gZYxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 4 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxJzAlBgNVBAMMHkhpZ2gg 5 | U2VjdXJpdHkgUm9vdCBDZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7AcAQIBDAwxLWZm 6 | MDA6MDoyMTAwHhcNMjAxMTEyMDgwMDAwWhcNMjAxMTEzMDgwMDAwWjCBljELMAkG 7 | A1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xyaWNoMQ8wDQYDVQQK 8 | DAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzEnMCUGA1UEAwweSGlnaCBTZWN1cml0 9 | eSBSb290IENlcnRpZmljYXRlMR0wGwYLKwYBBAGDsBwBAgEMDDEtZmYwMDowOjIx 10 | MDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABGtKP8USPnfh7+ObyAQ/QUo10kig 11 | nykjVh8PCcyMCr+UdK5wWG7WFI2mEnC0wJfP2ThGfWG8JeaVOE3pe2kVEy6jZzBl 12 | MBIGA1UdEwEB/wQIMAYBAf8CAQEwDgYDVR0PAQH/BAQDAgEGMB0GA1UdDgQWBBQS 13 | k9s2w29fP/M0JWIv3nFODE2gWzAgBgNVHSUEGTAXBgsrBgEEAYOwHAEDAwYIKwYB 14 | BQUHAwgwCgYIKoZIzj0EAwQDSAAwRQIhAK/qYvYWaBRRf9SGoS+8c2LMSvFOgX1k 15 | gsYnSu1W5LGrAiA5q6ck/llwE5APJrJBbEVZ9sxduPz3jbbFI13jLPqVvA== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/root-ff00_0_210.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIG03VkJpfaklzgtA6V53bvGxCLsd8IduRWJ7hzyX1nbSoAoGCCqGSM49 3 | AwEHoUQDQgAEa0o/xRI+d+Hv45vIBD9BSjXSSKCfKSNWHw8JzIwKv5R0rnBYbtYU 4 | jaYScLTAl8/ZOEZ9Ybwl5pU4Tel7aRUTLg== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/trc-1.trc: -------------------------------------------------------------------------------- 1 | -----BEGIN TRC----- 2 | MIIL6QYJKoZIhvcNAQcCoIIL2jCCC9YCAQExDTALBglghkgBZQMEAgMwgghYBgkq 3 | hkiG9w0BBwGggghJBIIIRTCCCEECAQAwCQIBAQIBAQIBATAiGA8yMDIwMTExMjA4 4 | MDAwMFoYDzIwMjAxMTEyMDgzMDAwWgIBAAEBADAAAgEBMAwTCmZmMDA6MDoxMTAw 5 | DBMKZmYwMDowOjExMAwWU0NJT05MYWIgVFJDIGZvciBJU0QgMTCCB8wwggKJMIIC 6 | L6ADAgECAhR1uxAMcV4D46ZKYo+f7eU1pSXHxjAKBggqhkjOPQQDBDCBoTELMAkG 7 | A1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xyaWNoMQ8wDQYDVQQK 8 | DAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzEyMDAGA1UEAwwpMS1mZjAwOjA6MTEw 9 | IFNlbnNpdGl2ZSBWb3RpbmcgQ2VydGlmaWNhdGUxHTAbBgsrBgEEAYOwHAECAQwM 10 | MS1mZjAwOjA6MTEwMB4XDTIwMTExMjA4MDAwMFoXDTIwMTExMzA4MDAwMFowgaEx 11 | CzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEPMA0G 12 | A1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxMjAwBgNVBAMMKTEtZmYwMDow 13 | OjExMCBTZW5zaXRpdmUgVm90aW5nIENlcnRpZmljYXRlMR0wGwYLKwYBBAGDsBwB 14 | AgEMDDEtZmYwMDowOjExMDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABK7Vqd2K 15 | 8jvCdb+U5bjbWNR44X+SrfJbEApYlPPrg5Wv+xc3NkYfiFLzmgm/rjtRoq/VXJSe 16 | 0yuTiSEc0CPQnrKjQzBBMB0GA1UdDgQWBBTTzRlNbr5hMZ/FH1pHqno1QhKcDjAg 17 | BgNVHSUEGTAXBgsrBgEEAYOwHAEDAQYIKwYBBQUHAwgwCgYIKoZIzj0EAwQDSAAw 18 | RQIgWehitMJDKkGNfTc5AIdFDnUWHjUi3XKLmdLsOFWGYw8CIQDf9PkPzMI8VoP6 19 | slMfVEAR0kuS2pGDgdrYj6ppWqnT0zCCAoUwggIroAMCAQICFAxFMU0lyKahNiYC 20 | JIQsI3urqi/qMAoGCCqGSM49BAMEMIGfMQswCQYDVQQGEwJDSDELMAkGA1UECAwC 21 | WkgxEDAOBgNVBAcMB1rDvHJpY2gxDzANBgNVBAoMBk5ldHNlYzEPMA0GA1UECwwG 22 | TmV0c2VjMTAwLgYDVQQDDCcxLWZmMDA6MDoxMTAgUmVndWxhciBWb3RpbmcgQ2Vy 23 | dGlmaWNhdGUxHTAbBgsrBgEEAYOwHAECAQwMMS1mZjAwOjA6MTEwMB4XDTIwMTEx 24 | MjA4MDAwMFoXDTIwMTExMzA4MDAwMFowgZ8xCzAJBgNVBAYTAkNIMQswCQYDVQQI 25 | DAJaSDEQMA4GA1UEBwwHWsO8cmljaDEPMA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQL 26 | DAZOZXRzZWMxMDAuBgNVBAMMJzEtZmYwMDowOjExMCBSZWd1bGFyIFZvdGluZyBD 27 | ZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7AcAQIBDAwxLWZmMDA6MDoxMTAwWTATBgcq 28 | hkjOPQIBBggqhkjOPQMBBwNCAAQ03y2TgJ8/gdMXSyi2dZgqmUxBcG2S8Qxoo60F 29 | qSQSv5fEPM98nElovnULSjaR8WXoLL0oisBDESxwL9x54X9Oo0MwQTAdBgNVHQ4E 30 | FgQUilsS1oZ5PpTj6emzyjAL/zi0XTswIAYDVR0lBBkwFwYLKwYBBAGDsBwBAwIG 31 | CCsGAQUFBwMIMAoGCCqGSM49BAMEA0gAMEUCIQD+yOnXOzYXnKxzWu+ZyirjJU9F 32 | nPoTw13wKclPWljTjwIgZlymqG+UXnpwmSeg99tcZYjbaN/tsnXaqasonQWoHfQw 33 | ggKyMIICV6ADAgECAhQMaUSKS5j4LlhGK5V3Gu8Ji4fjZTAKBggqhkjOPQQDBDCB 34 | ozELMAkGA1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xyaWNoMQ8w 35 | DQYDVQQKDAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzE0MDIGA1UEAwwrMS1mZjAw 36 | OjA6MTEwIEhpZ2ggU2VjdXJpdHkgUm9vdCBDZXJ0aWZpY2F0ZTEdMBsGCysGAQQB 37 | g7AcAQIBDAwxLWZmMDA6MDoxMTAwHhcNMjAxMTEyMDgwMDAwWhcNMjAxMTEzMDgw 38 | MDAwWjCBozELMAkGA1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xy 39 | aWNoMQ8wDQYDVQQKDAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzE0MDIGA1UEAwwr 40 | MS1mZjAwOjA6MTEwIEhpZ2ggU2VjdXJpdHkgUm9vdCBDZXJ0aWZpY2F0ZTEdMBsG 41 | CysGAQQBg7AcAQIBDAwxLWZmMDA6MDoxMTAwWTATBgcqhkjOPQIBBggqhkjOPQMB 42 | BwNCAATwOUGQOOPdQdf6GSyvb2J8QPCa7QyF0DhIOm4uqgb/+QQU8uAXuxtUw2/b 43 | cRCYhIDKg/kONuWbj5l4hr6tWytKo2cwZTASBgNVHRMBAf8ECDAGAQH/AgEBMA4G 44 | A1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUZjOvqQ0WWCtzKSsVuIvsP4wf1mEwIAYD 45 | VR0lBBkwFwYLKwYBBAGDsBwBAwMGCCsGAQUFBwMIMAoGCCqGSM49BAMEA0kAMEYC 46 | IQD3+soqUBXjgWNCoeELYAqKgqjCg8beupLF5WVFAlTd9wIhAPy/BCSPmSdoiUAz 47 | vwHSIYeotxdP1ZK0jhSwcAzUMbB6MYIDZDCCAa0CAQEwgbgwgZ8xCzAJBgNVBAYT 48 | AkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEPMA0GA1UECgwGTmV0 49 | c2VjMQ8wDQYDVQQLDAZOZXRzZWMxMDAuBgNVBAMMJzEtZmYwMDowOjExMCBSZWd1 50 | bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7AcAQIBDAwxLWZmMDA6 51 | MDoxMTACFAxFMU0lyKahNiYCJIQsI3urqi/qMAsGCWCGSAFlAwQCA6CBiTAYBgkq 52 | hkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDExMDUyMTM0 53 | MjVaME8GCSqGSIb3DQEJBDFCBEA+yx9cnKOFkSGdvGRm7d8kUseE8NBIwpTI17Dv 54 | hMr0f7MvGLKhulci9AxA9+3HIy9ylalwQxib+jOx6ATtSMzVMAoGCCqGSM49BAME 55 | BEgwRgIhANtQLScwidl+0s4O2K4o4ZDSulKFwIuN+cat6taEu2VFAiEAmiFGINWT 56 | xxgB2YM0PjIJf8FkMXO5p1trMZ6/ZlZczPgwggGvAgEBMIG6MIGhMQswCQYDVQQG 57 | EwJDSDELMAkGA1UECAwCWkgxEDAOBgNVBAcMB1rDvHJpY2gxDzANBgNVBAoMBk5l 58 | dHNlYzEPMA0GA1UECwwGTmV0c2VjMTIwMAYDVQQDDCkxLWZmMDA6MDoxMTAgU2Vu 59 | c2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7AcAQIBDAwxLWZm 60 | MDA6MDoxMTACFHW7EAxxXgPjpkpij5/t5TWlJcfGMAsGCWCGSAFlAwQCA6CBiTAY 61 | BgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDExMDUy 62 | MTM0MjVaME8GCSqGSIb3DQEJBDFCBEA+yx9cnKOFkSGdvGRm7d8kUseE8NBIwpTI 63 | 17DvhMr0f7MvGLKhulci9AxA9+3HIy9ylalwQxib+jOx6ATtSMzVMAoGCCqGSM49 64 | BAMEBEgwRgIhAPA1e4QXD717WCat03Zj5StBXNP8Bvy6VdyxCTu5pCZUAiEAimnN 65 | B5Tj+siAGSiy5fZJ/di1p/P7pL8i+b2H5yUGtXE= 66 | -----END TRC----- 67 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/trc-2.trc: -------------------------------------------------------------------------------- 1 | -----BEGIN TRC----- 2 | MIIKOQYJKoZIhvcNAQcCoIIKKjCCCiYCAQExDTALBglghkgBZQMEAgMwgghbBgkq 3 | hkiG9w0BBwGggghMBIIISDCCCEQCAQAwCQIBAQIBAgIBATAiGA8yMDIwMTExMjA4 4 | MDAwMFoYDzIwMjAxMTEyMDgzMDAwWgIBAAEBADADAgEBAgEBMAwTCmZmMDA6MDox 5 | MTAwDBMKZmYwMDowOjExMAwWU0NJT05MYWIgVFJDIGZvciBJU0QgMTCCB8wwggKJ 6 | MIICL6ADAgECAhR1uxAMcV4D46ZKYo+f7eU1pSXHxjAKBggqhkjOPQQDBDCBoTEL 7 | MAkGA1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xyaWNoMQ8wDQYD 8 | VQQKDAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzEyMDAGA1UEAwwpMS1mZjAwOjA6 9 | MTEwIFNlbnNpdGl2ZSBWb3RpbmcgQ2VydGlmaWNhdGUxHTAbBgsrBgEEAYOwHAEC 10 | AQwMMS1mZjAwOjA6MTEwMB4XDTIwMTExMjA4MDAwMFoXDTIwMTExMzA4MDAwMFow 11 | gaExCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 12 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxMjAwBgNVBAMMKTEtZmYw 13 | MDowOjExMCBTZW5zaXRpdmUgVm90aW5nIENlcnRpZmljYXRlMR0wGwYLKwYBBAGD 14 | sBwBAgEMDDEtZmYwMDowOjExMDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABK7V 15 | qd2K8jvCdb+U5bjbWNR44X+SrfJbEApYlPPrg5Wv+xc3NkYfiFLzmgm/rjtRoq/V 16 | XJSe0yuTiSEc0CPQnrKjQzBBMB0GA1UdDgQWBBTTzRlNbr5hMZ/FH1pHqno1QhKc 17 | DjAgBgNVHSUEGTAXBgsrBgEEAYOwHAEDAQYIKwYBBQUHAwgwCgYIKoZIzj0EAwQD 18 | SAAwRQIgWehitMJDKkGNfTc5AIdFDnUWHjUi3XKLmdLsOFWGYw8CIQDf9PkPzMI8 19 | VoP6slMfVEAR0kuS2pGDgdrYj6ppWqnT0zCCAoUwggIroAMCAQICFAxFMU0lyKah 20 | NiYCJIQsI3urqi/qMAoGCCqGSM49BAMEMIGfMQswCQYDVQQGEwJDSDELMAkGA1UE 21 | CAwCWkgxEDAOBgNVBAcMB1rDvHJpY2gxDzANBgNVBAoMBk5ldHNlYzEPMA0GA1UE 22 | CwwGTmV0c2VjMTAwLgYDVQQDDCcxLWZmMDA6MDoxMTAgUmVndWxhciBWb3Rpbmcg 23 | Q2VydGlmaWNhdGUxHTAbBgsrBgEEAYOwHAECAQwMMS1mZjAwOjA6MTEwMB4XDTIw 24 | MTExMjA4MDAwMFoXDTIwMTExMzA4MDAwMFowgZ8xCzAJBgNVBAYTAkNIMQswCQYD 25 | VQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEPMA0GA1UECgwGTmV0c2VjMQ8wDQYD 26 | VQQLDAZOZXRzZWMxMDAuBgNVBAMMJzEtZmYwMDowOjExMCBSZWd1bGFyIFZvdGlu 27 | ZyBDZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7AcAQIBDAwxLWZmMDA6MDoxMTAwWTAT 28 | BgcqhkjOPQIBBggqhkjOPQMBBwNCAAQ03y2TgJ8/gdMXSyi2dZgqmUxBcG2S8Qxo 29 | o60FqSQSv5fEPM98nElovnULSjaR8WXoLL0oisBDESxwL9x54X9Oo0MwQTAdBgNV 30 | HQ4EFgQUilsS1oZ5PpTj6emzyjAL/zi0XTswIAYDVR0lBBkwFwYLKwYBBAGDsBwB 31 | AwIGCCsGAQUFBwMIMAoGCCqGSM49BAMEA0gAMEUCIQD+yOnXOzYXnKxzWu+Zyirj 32 | JU9FnPoTw13wKclPWljTjwIgZlymqG+UXnpwmSeg99tcZYjbaN/tsnXaqasonQWo 33 | HfQwggKyMIICV6ADAgECAhQMaUSKS5j4LlhGK5V3Gu8Ji4fjZTAKBggqhkjOPQQD 34 | BDCBozELMAkGA1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xyaWNo 35 | MQ8wDQYDVQQKDAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzE0MDIGA1UEAwwrMS1m 36 | ZjAwOjA6MTEwIEhpZ2ggU2VjdXJpdHkgUm9vdCBDZXJ0aWZpY2F0ZTEdMBsGCysG 37 | AQQBg7AcAQIBDAwxLWZmMDA6MDoxMTAwHhcNMjAxMTEyMDgwMDAwWhcNMjAxMTEz 38 | MDgwMDAwWjCBozELMAkGA1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAda 39 | w7xyaWNoMQ8wDQYDVQQKDAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzE0MDIGA1UE 40 | AwwrMS1mZjAwOjA6MTEwIEhpZ2ggU2VjdXJpdHkgUm9vdCBDZXJ0aWZpY2F0ZTEd 41 | MBsGCysGAQQBg7AcAQIBDAwxLWZmMDA6MDoxMTAwWTATBgcqhkjOPQIBBggqhkjO 42 | PQMBBwNCAATwOUGQOOPdQdf6GSyvb2J8QPCa7QyF0DhIOm4uqgb/+QQU8uAXuxtU 43 | w2/bcRCYhIDKg/kONuWbj5l4hr6tWytKo2cwZTASBgNVHRMBAf8ECDAGAQH/AgEB 44 | MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUZjOvqQ0WWCtzKSsVuIvsP4wf1mEw 45 | IAYDVR0lBBkwFwYLKwYBBAGDsBwBAwMGCCsGAQUFBwMIMAoGCCqGSM49BAMEA0kA 46 | MEYCIQD3+soqUBXjgWNCoeELYAqKgqjCg8beupLF5WVFAlTd9wIhAPy/BCSPmSdo 47 | iUAzvwHSIYeotxdP1ZK0jhSwcAzUMbB6MYIBsTCCAa0CAQEwgbgwgZ8xCzAJBgNV 48 | BAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEPMA0GA1UECgwG 49 | TmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxMDAuBgNVBAMMJzEtZmYwMDowOjExMCBS 50 | ZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7AcAQIBDAwxLWZm 51 | MDA6MDoxMTACFAxFMU0lyKahNiYCJIQsI3urqi/qMAsGCWCGSAFlAwQCA6CBiTAY 52 | BgkqhkiG9w0BCQMxCwYJKoZIhvcNAQcBMBwGCSqGSIb3DQEJBTEPFw0yMDExMDUy 53 | MTM0MjVaME8GCSqGSIb3DQEJBDFCBECkUHa+Z+sISyTfsKOd7qi2V6u3TKeHJlOb 54 | w8Co0N0pvf4F5a2o6ZXyvzbJp2oWkAiXi+CFWAbt7PJq+WnYiVKnMAoGCCqGSM49 55 | BAMEBEgwRgIhAMiEJkSgVQoc0Po40f6Bmeh7FBObgC0o/e6kgjVo0SJ7AiEAt3Lv 56 | 09S79ErO8DsLZrxtGn4b8ZkQVkenKb+c5GGwjD0= 57 | -----END TRC----- 58 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/voting-regular-ff00_0_110.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIChTCCAiugAwIBAgIUDEUxTSXIpqE2JgIkhCwje6uqL+owCgYIKoZIzj0EAwQw 3 | gZ8xCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 4 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxMDAuBgNVBAMMJzEtZmYw 5 | MDowOjExMCBSZWd1bGFyIFZvdGluZyBDZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7Ac 6 | AQIBDAwxLWZmMDA6MDoxMTAwHhcNMjAxMTEyMDgwMDAwWhcNMjAxMTEzMDgwMDAw 7 | WjCBnzELMAkGA1UEBhMCQ0gxCzAJBgNVBAgMAlpIMRAwDgYDVQQHDAdaw7xyaWNo 8 | MQ8wDQYDVQQKDAZOZXRzZWMxDzANBgNVBAsMBk5ldHNlYzEwMC4GA1UEAwwnMS1m 9 | ZjAwOjA6MTEwIFJlZ3VsYXIgVm90aW5nIENlcnRpZmljYXRlMR0wGwYLKwYBBAGD 10 | sBwBAgEMDDEtZmYwMDowOjExMDBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABDTf 11 | LZOAnz+B0xdLKLZ1mCqZTEFwbZLxDGijrQWpJBK/l8Q8z3ycSWi+dQtKNpHxZegs 12 | vSiKwEMRLHAv3Hnhf06jQzBBMB0GA1UdDgQWBBSKWxLWhnk+lOPp6bPKMAv/OLRd 13 | OzAgBgNVHSUEGTAXBgsrBgEEAYOwHAEDAgYIKwYBBQUHAwgwCgYIKoZIzj0EAwQD 14 | SAAwRQIhAP7I6dc7NhecrHNa75nKKuMlT0Wc+hPDXfApyU9aWNOPAiBmXKaob5Re 15 | enCZJ6D321xliNto3+2yddqpqyidBagd9A== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/voting-regular-ff00_0_110.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIBbFliy2uky4SHoeD1FXb8RS2eA6y9J0rYqfwRtI6Y8ioAoGCCqGSM49 3 | AwEHoUQDQgAENN8tk4CfP4HTF0sotnWYKplMQXBtkvEMaKOtBakkEr+XxDzPfJxJ 4 | aL51C0o2kfFl6Cy9KIrAQxEscC/ceeF/Tg== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/voting-regular-ff00_0_210.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICajCCAhGgAwIBAgIUKUh/NHJg629T4K/veYxBeXeszwEwCgYIKoZIzj0EAwQw 3 | gZIxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 4 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxIzAhBgNVBAMMGlJlZ3Vs 5 | YXIgVm90aW5nIENlcnRpZmljYXRlMR0wGwYLKwYBBAGDsBwBAgEMDDEtZmYwMDow 6 | OjIxMDAeFw0yMDExMTIwODAwMDBaFw0yMDExMTMwODAwMDBaMIGSMQswCQYDVQQG 7 | EwJDSDELMAkGA1UECAwCWkgxEDAOBgNVBAcMB1rDvHJpY2gxDzANBgNVBAoMBk5l 8 | dHNlYzEPMA0GA1UECwwGTmV0c2VjMSMwIQYDVQQDDBpSZWd1bGFyIFZvdGluZyBD 9 | ZXJ0aWZpY2F0ZTEdMBsGCysGAQQBg7AcAQIBDAwxLWZmMDA6MDoyMTAwWTATBgcq 10 | hkjOPQIBBggqhkjOPQMBBwNCAAQONMpvYWZJlAbJq4MGeyEc3kdA1bgId6w3ZojR 11 | +e1Uvf14MReRnbjA1WqdSURA/S8tri4w32siMHAfRO0YLAaAo0MwQTAdBgNVHQ4E 12 | FgQUhhZ4lYYFWt5DQd3M/ZmUysgHeqwwIAYDVR0lBBkwFwYLKwYBBAGDsBwBAwIG 13 | CCsGAQUFBwMIMAoGCCqGSM49BAMEA0cAMEQCIG9s75j8hVo0xGEWBOO+Wqv/lApq 14 | g9UcOi77/9ysD1haAiB9el6xlOoIyfFHtcW3Jn1FTFP9kuoLwFgzD16+LL4QYA== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/voting-regular-ff00_0_210.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEILomi9Efp+A0axVsbe0TS1lDeEtbc1ZxH6N2s+2/CiXZoAoGCCqGSM49 3 | AwEHoUQDQgAEDjTKb2FmSZQGyauDBnshHN5HQNW4CHesN2aI0fntVL39eDEXkZ24 4 | wNVqnUlEQP0vLa4uMN9rIjBwH0TtGCwGgA== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/voting-sensitive-ff00_0_110.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICiTCCAi+gAwIBAgIUdbsQDHFeA+OmSmKPn+3lNaUlx8YwCgYIKoZIzj0EAwQw 3 | gaExCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 4 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxMjAwBgNVBAMMKTEtZmYw 5 | MDowOjExMCBTZW5zaXRpdmUgVm90aW5nIENlcnRpZmljYXRlMR0wGwYLKwYBBAGD 6 | sBwBAgEMDDEtZmYwMDowOjExMDAeFw0yMDExMTIwODAwMDBaFw0yMDExMTMwODAw 7 | MDBaMIGhMQswCQYDVQQGEwJDSDELMAkGA1UECAwCWkgxEDAOBgNVBAcMB1rDvHJp 8 | Y2gxDzANBgNVBAoMBk5ldHNlYzEPMA0GA1UECwwGTmV0c2VjMTIwMAYDVQQDDCkx 9 | LWZmMDA6MDoxMTAgU2Vuc2l0aXZlIFZvdGluZyBDZXJ0aWZpY2F0ZTEdMBsGCysG 10 | AQQBg7AcAQIBDAwxLWZmMDA6MDoxMTAwWTATBgcqhkjOPQIBBggqhkjOPQMBBwNC 11 | AASu1andivI7wnW/lOW421jUeOF/kq3yWxAKWJTz64OVr/sXNzZGH4hS85oJv647 12 | UaKv1VyUntMrk4khHNAj0J6yo0MwQTAdBgNVHQ4EFgQU080ZTW6+YTGfxR9aR6p6 13 | NUISnA4wIAYDVR0lBBkwFwYLKwYBBAGDsBwBAwEGCCsGAQUFBwMIMAoGCCqGSM49 14 | BAMEA0gAMEUCIFnoYrTCQypBjX03OQCHRQ51Fh41It1yi5nS7DhVhmMPAiEA3/T5 15 | D8zCPFaD+rJTH1RAEdJLktqRg4Ha2I+qaVqp09M= 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/voting-sensitive-ff00_0_110.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIKU8/lPZ+2rAYXZlyuJaBDBFP0cuAL6c977qO+1FbUeToAoGCCqGSM49 3 | AwEHoUQDQgAErtWp3YryO8J1v5TluNtY1Hjhf5Kt8lsQCliU8+uDla/7Fzc2Rh+I 4 | UvOaCb+uO1Gir9VclJ7TK5OJIRzQI9Cesg== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/voting-sensitive-ff00_0_210.crt: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICcDCCAhWgAwIBAgIUZo4EX/QsII1My9Q7h+NlBABQ2PcwCgYIKoZIzj0EAwQw 3 | gZQxCzAJBgNVBAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEP 4 | MA0GA1UECgwGTmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxJTAjBgNVBAMMHFNlbnNp 5 | dGl2ZSBWb3RpbmcgQ2VydGlmaWNhdGUxHTAbBgsrBgEEAYOwHAECAQwMMS1mZjAw 6 | OjA6MjEwMB4XDTIwMTExMjA4MDAwMFoXDTIwMTExMzA4MDAwMFowgZQxCzAJBgNV 7 | BAYTAkNIMQswCQYDVQQIDAJaSDEQMA4GA1UEBwwHWsO8cmljaDEPMA0GA1UECgwG 8 | TmV0c2VjMQ8wDQYDVQQLDAZOZXRzZWMxJTAjBgNVBAMMHFNlbnNpdGl2ZSBWb3Rp 9 | bmcgQ2VydGlmaWNhdGUxHTAbBgsrBgEEAYOwHAECAQwMMS1mZjAwOjA6MjEwMFkw 10 | EwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEF1rrN/Pm1EDGKi4UgB3FqN3OUHz+sc+V 11 | C80KtplyDz5pvzbX+Twx1FSnKjaKwVt8b9JrZmV3HD6pa3aE2dEne6NDMEEwHQYD 12 | VR0OBBYEFIsGdnQn1yBPK4UI6ZHngR83m1qcMCAGA1UdJQQZMBcGCysGAQQBg7Ac 13 | AQMBBggrBgEFBQcDCDAKBggqhkjOPQQDBANJADBGAiEAjBmEnfoi11x39WLByoBF 14 | aU2+H8YeD1qm1MB0AR6jKXICIQDUlYmshrwT4rkr4Xepe1eKmwyN1P3EFmt4JZ82 15 | Sz5tDA== 16 | -----END CERTIFICATE----- 17 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scion_trcs/voting-sensitive-ff00_0_210.key: -------------------------------------------------------------------------------- 1 | -----BEGIN EC PRIVATE KEY----- 2 | MHcCAQEEIGmZk3XY7gBZhaGkVRWPwTkfa2oBfDCWH11943PGaQo7oAoGCCqGSM49 3 | AwEHoUQDQgAEF1rrN/Pm1EDGKi4UgB3FqN3OUHz+sc+VC80KtplyDz5pvzbX+Twx 4 | 1FSnKjaKwVt8b9JrZmV3HD6pa3aE2dEnew== 5 | -----END EC PRIVATE KEY----- 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scionlab-config/good.json: -------------------------------------------------------------------------------- 1 | { 2 | "host_id": "h1", 3 | "host_secret": "s1", 4 | "url": "https://test.scionlab.org", 5 | "version": "12.345" 6 | } 7 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scionlab-config/missing_id.json: -------------------------------------------------------------------------------- 1 | { 2 | "host_secret": "s1", 3 | "url": "https://test.scionlab.org", 4 | "version": "12.345" 5 | } 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scionlab-config/missing_secret.json: -------------------------------------------------------------------------------- 1 | { 2 | "host_id": "h1", 3 | "url": "https://test.scionlab.org", 4 | "version": "12.345" 5 | } 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scionlab-config/no_url.json: -------------------------------------------------------------------------------- 1 | { 2 | "host_id": "h1", 3 | "host_secret": "s1", 4 | "version": "12.345" 5 | } 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scionlab-config/no_version.json: -------------------------------------------------------------------------------- 1 | { 2 | "host_id": "h1", 3 | "host_secret": "s1", 4 | "url": "https://test.scionlab.org" 5 | } 6 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_scionlab-config/old_version.json: -------------------------------------------------------------------------------- 1 | { 2 | "host_id": "h1", 3 | "host_secret": "s1", 4 | "url": "https://test.scionlab.org", 5 | "version": 345 6 | } 7 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_user_forms/invalid_forms.yaml: -------------------------------------------------------------------------------- 1 | # Example 2 | # - desc: "Testcase description" 3 | # user-as-label: "UserAS label" 4 | # user-as-installation_type: "VM" 5 | # attachments: 6 | # - attachment_point: "3" 7 | # public_ip: "1.1.1.1" 8 | # public_port: "50000" 9 | # - attachment_point: "4" 10 | # use_vpn: "on" 11 | # public_port: "50000" 12 | - desc: "Needs either VPN or public IP" 13 | error: "Please provide a value for public IP, or enable \"Use VPN\"." 14 | attachments: 15 | - attachment_point: "1" 16 | 17 | - desc: "AP2 does not have VPN" 18 | error: "Selected attachment point does not support VPN" 19 | attachments: 20 | - attachment_point: "2" 21 | use_vpn: "on" 22 | 23 | - desc: "Invalid IP" 24 | error: "Enter a valid IPv4 or IPv6 address." 25 | attachments: 26 | - attachment_point: "1" 27 | public_ip: "aa" 28 | 29 | - desc: "Invalid bind IP" 30 | error: "Enter a valid IPv4 or IPv6 address." 31 | attachments: 32 | - attachment_point: "1" 33 | public_ip: "192.1.2.111" 34 | bind_ip: "aaa" 35 | 36 | - desc: "Localhost is not allowed" 37 | error: >- 38 | Public IP address must be a publicly routable address. 39 | It cannot be a multicast, loopback or otherwise reserved address. 40 | attachments: 41 | - attachment_point: "1" 42 | public_ip: "127.0.0.1" 43 | 44 | - desc: "Multicast is not allowed" 45 | error: >- 46 | Public IP address must be a publicly routable address. 47 | It cannot be a multicast, loopback or otherwise reserved address. 48 | attachments: 49 | - attachment_point: "1" 50 | public_ip: "10.0.0.1" 51 | 52 | - desc: "Private address is not allowed" 53 | error: >- 54 | Public IP address must be a publicly routable address. 55 | It cannot be a multicast, loopback or otherwise reserved address. 56 | attachments: 57 | - attachment_point: "1" 58 | public_ip: "224.0.0.1" 59 | 60 | - desc: "The attachment point doesn't support IPv6" 61 | error: "IP version 6 not supported by the selected attachment point" 62 | attachments: 63 | - attachment_point: "1" 64 | public_ip: "2a00:1450:400a:801::2004" 65 | 66 | - desc: "Inconsistent ISD" 67 | error: "All attachment points must belong to the same ISD" 68 | attachments: 69 | - attachment_point: "1" 70 | use_vpn: "on" 71 | - attachment_point: "3" 72 | use_vpn: "on" 73 | 74 | - desc: "Conflicting ports in public setup" 75 | error: "This port is already in use" 76 | installation_type: "PKG" 77 | attachments: 78 | - attachment_point: "3" 79 | public_ip: "192.1.2.111" 80 | public_port: "50000" 81 | - attachment_point: "4" 82 | public_ip: "192.1.2.111" 83 | public_port: "50000" 84 | 85 | - desc: "Conflicting local ports in public setup" 86 | error: "This port is already in use for the specified bind IP address" 87 | installation_type: "PKG" 88 | attachments: 89 | - attachment_point: "3" 90 | public_ip: "192.1.2.111" 91 | public_port: "50000" 92 | bind_ip: "192.0.2.100" 93 | - attachment_point: "4" 94 | public_ip: "192.1.2.112" 95 | public_port: "50000" 96 | bind_ip: "192.0.2.100" 97 | 98 | - desc: "Conflicting forwarding port in VM setup" 99 | error: "This port clashes in the VM setup" 100 | attachments: 101 | - attachment_point: "1" 102 | public_ip: "192.1.2.111" 103 | public_port: "50000" 104 | - attachment_point: "1" 105 | public_ip: "192.1.2.112" 106 | public_port: "50000" 107 | 108 | - desc: "Missing public IP for User AP" 109 | error: "Please enter a public IP address to become User AP" 110 | become_user_ap: "on" 111 | attachments: 112 | - attachment_point: "1" 113 | use_vpn: "on" 114 | -------------------------------------------------------------------------------- /scionlab/tests/data/test_user_forms/valid_forms.yaml: -------------------------------------------------------------------------------- 1 | # Example 2 | # - desc: "Testcase description" 3 | # label: "UserAS label" 4 | # installation_type: "VM" 5 | # attachments: 6 | # - attachment_point: "3" 7 | # public_ip: "1.1.1.1" 8 | # public_port: "50000" 9 | # - attachment_point: "4" 10 | # use_vpn: "on" 11 | # public_port: "50000" 12 | - desc: "VM installation type" 13 | attachments: 14 | - attachment_point: "1" 15 | use_vpn: "on" 16 | - desc: "PKG installation type" 17 | installation_type: "PKG" 18 | attachments: 19 | - attachment_point: "1" 20 | use_vpn: "on" 21 | - desc: "SRC installation type" 22 | installation_type: "SRC" 23 | attachments: 24 | - attachment_point: "1" 25 | use_vpn: "on" 26 | - desc: "Custom label" 27 | user-as-label: "This is a label" 28 | attachments: 29 | - attachment_point: "1" 30 | use_vpn: "on" 31 | - desc: "Custom public port" 32 | attachments: 33 | - attachment_point: "1" 34 | use_vpn: "on" 35 | public_port: "54321" 36 | - desc: "Custom public ip" 37 | attachments: 38 | - attachment_point: "1" 39 | public_ip: "192.1.2.111" 40 | - desc: "Custom public IP and public port" 41 | attachments: 42 | - attachment_point: "1" 43 | public_port: "54321" 44 | public_ip: "192.1.2.111" 45 | - desc: "Redundant public IP when VPN enabled" 46 | attachments: 47 | - attachment_point: "1" 48 | use_vpn: "on" 49 | public_ip: "192.1.2.111" 50 | - desc: "Custom public IP with attachment 2" 51 | attachments: 52 | - attachment_point: "2" 53 | public_ip: "192.1.2.111" 54 | - desc: "Redundant IPv6 with VPN" 55 | attachments: 56 | - attachment_point: "1" 57 | use_vpn: "on" 58 | public_ip: " 2a00:1450:400a:801::2004" 59 | 60 | - desc: "Multiple attachment through VPN and public to same attachment" 61 | attachments: 62 | - attachment_point: "1" 63 | use_vpn: "on" 64 | - attachment_point: "1" 65 | public_ip: "192.1.2.111" 66 | public_port: "50000" 67 | - desc: "Multiple attachment through public with same IP to same attachment" 68 | attachments: 69 | - attachment_point: "1" 70 | public_ip: "192.1.2.111" 71 | public_port: "50000" 72 | - attachment_point: "1" 73 | public_ip: "192.1.2.111" 74 | public_port: "50001" 75 | - desc: "Multiple attachment with same public_ip and bind_ip" 76 | installation_type: "PKG" 77 | attachments: 78 | - attachment_point: "3" 79 | public_ip: "192.1.2.111" 80 | public_port: "50000" 81 | bind_ip: "192.168.1.1" 82 | - attachment_point: "4" 83 | public_ip: "192.1.2.111" 84 | public_port: "50001" 85 | bind_ip: "192.168.1.1" 86 | - desc: "Multiple attachment through public and VPN to different attachments" 87 | attachments: 88 | - attachment_point: "3" 89 | public_ip: "192.1.2.111" 90 | public_port: "50000" 91 | - attachment_point: "4" 92 | public_ip: "192.1.2.111" 93 | public_port: "50001" 94 | - desc: "Same ports with different IPs with PKG" 95 | installation_type: "PKG" 96 | attachments: 97 | - attachment_point: "3" 98 | public_ip: "192.1.2.111" 99 | public_port: "50000" 100 | - attachment_point: "4" 101 | public_ip: "192.1.2.112" 102 | public_port: "50000" 103 | - desc: "User AS as an AP with public IP" 104 | become_user_ap: "on" 105 | public_ip: "192.1.2.111" 106 | attachments: 107 | - attachment_point: "1" 108 | use_vpn: "on" 109 | - desc: "User AS as an AP with public IP that provides VPN" 110 | become_user_ap: "on" 111 | public_ip: "192.1.2.111" 112 | provide_vpn: "on" 113 | attachments: 114 | - attachment_point: "1" 115 | use_vpn: "on" 116 | - desc: "Custom public IP with attachment 2 with FABRID" 117 | provide_fabrid: "on" 118 | attachments: 119 | - attachment_point: "2" 120 | public_ip: "192.1.2.111" 121 | - desc: "User AS as an AP with public IP that provides VPN with FABRID" 122 | become_user_ap: "on" 123 | public_ip: "192.1.2.111" 124 | provide_vpn: "on" 125 | provide_fabrid: "on" 126 | attachments: 127 | - attachment_point: "1" 128 | use_vpn: "on" 129 | -------------------------------------------------------------------------------- /scionlab/tests/test_archive.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import configparser 16 | import hashlib 17 | import pathlib 18 | import tempfile 19 | 20 | from django.test import TestCase 21 | 22 | from scionlab.util.archive import DictWriter, HashedArchiveWriter, FileArchiveWriter 23 | 24 | _TEST_DICT = {'foo': 'foo'} 25 | 26 | _TEST_CONFIG = configparser.ConfigParser() 27 | _TEST_CONFIG['foosection'] = {'foo': 'foo'} 28 | 29 | 30 | class HashedArchiveWriterTests(TestCase): 31 | def test_undistorted(self): 32 | """ Check that base writer receives same content """ 33 | base = DictWriter() 34 | self._add_stuff(base) 35 | 36 | adaptee = DictWriter() 37 | adapter = HashedArchiveWriter(adaptee) 38 | self._add_stuff(adapter) 39 | 40 | self.assertEqual(base.dict, adaptee.dict) 41 | 42 | def test_hash_set(self): 43 | """ Check that there is a hash for each file (excluding directories) """ 44 | adaptee = DictWriter() 45 | hasher = HashedArchiveWriter(adaptee) 46 | self._add_stuff(hasher) 47 | 48 | self.assertEqual(sorted(path for path in adaptee.dict.keys() if not path.endswith('/')), 49 | sorted(hasher.hashes.keys())) 50 | 51 | def test_file_hashes(self): 52 | """ Check that hashes for files written to disk match computed hashes """ 53 | adaptee = DictWriter() 54 | hasher = HashedArchiveWriter(adaptee) 55 | self._add_stuff(hasher) 56 | 57 | with tempfile.TemporaryDirectory("scionlab_tests_test_archive") as tmpdir: 58 | filer = FileArchiveWriter(tmpdir) 59 | self._add_stuff(filer) 60 | 61 | # Get hashes from files on disk: 62 | hashes = {} 63 | for f in pathlib.Path(tmpdir).iterdir(): 64 | if f.is_file(): 65 | hashes[f.name] = hashlib.sha1(f.read_bytes()).hexdigest() 66 | 67 | # Should be the same hashes: 68 | self.assertEqual(hasher.hashes, hashes) 69 | 70 | def _add_stuff(self, archive): 71 | archive.write_text("test.txt", "henlo") 72 | archive.write_json("test.json", _TEST_DICT) 73 | archive.write_toml("test.toml", _TEST_DICT) 74 | archive.write_yaml("test.yaml", _TEST_DICT) 75 | archive.write_config("test.ini", _TEST_CONFIG) 76 | archive.add("test.py", __file__) 77 | archive.add_dir("testdir") 78 | -------------------------------------------------------------------------------- /scionlab/tests/test_as_ids.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from unittest import TestCase 16 | from parameterized import parameterized 17 | from scionlab.scion import as_ids 18 | 19 | 20 | class ASIDTests(TestCase): 21 | @parameterized.expand([ 22 | ('0:0:0', 0), 23 | ('0:0:1', 1), 24 | ('0:0:abcd', 0xABCD), 25 | ('0:1:0', 2**16), 26 | ('0:f00d:0', 0xF00D * 2**16), 27 | ('1:0:0', 2**32), 28 | ('3b73:0:0', 0x3B73 * 2**32), 29 | ('ff00:1:3a', 0xFF00 * 2**32 + 2**16 + 0x3A), 30 | ]) 31 | def test_parse_format(self, as_id_str, expected_int): 32 | parsed = as_ids.parse(as_id_str) 33 | self.assertEqual(parsed, expected_int) 34 | 35 | parsed = as_ids.parse(as_id_str.upper()) 36 | self.assertEqual(parsed, expected_int) 37 | 38 | formatted = as_ids.format(parsed) 39 | self.assertEqual(formatted, as_id_str.lower()) 40 | 41 | @parameterized.expand([ 42 | '', 43 | '0', 44 | '::', 45 | '0:0:abcde', 46 | '1-0:0:0', 47 | ' 0:0:0 ', 48 | ]) 49 | def test_parse_fail(self, as_id_str): 50 | try: 51 | parsed = as_ids.parse(as_id_str) 52 | except ValueError: 53 | return 54 | self.fail("Expected failure, returned %x instead" % parsed) 55 | -------------------------------------------------------------------------------- /scionlab/tests/test_maintenance_mode.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.test import TestCase, override_settings 16 | from django.urls import reverse 17 | 18 | 19 | @override_settings(MAINTENANCE_MODE=True) 20 | class MaintenanceModeTests(TestCase): 21 | 22 | def test_get(self): 23 | response = self.client.get('/user/') 24 | self.check_response_maintenance(response) 25 | 26 | def test_get_exceptions(self): 27 | response = self.client.get('/') 28 | self.assertEqual(response.status_code, 200) 29 | self.assertTrue(b'Maintenance' in response.content) 30 | 31 | def test_post(self): 32 | response = self.client.post( 33 | reverse('login'), 34 | {'username': 'foo', 'password': 'bar'}, 35 | follow=True 36 | ) 37 | self.check_response_maintenance(response) 38 | 39 | def test_get_admin(self): 40 | response = self.client.get('/admin/login/') 41 | self.assertEqual(response.status_code, 200) 42 | 43 | def check_response_maintenance(self, response): 44 | self.assertEqual(response.status_code, 503) 45 | self.assertTrue(b"SCIONLab - Site Maintenance" in response.content) 46 | -------------------------------------------------------------------------------- /scionlab/tests/test_migration_trc_pem.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from importlib import import_module 16 | from django.test import SimpleTestCase 17 | 18 | migration_trc_pem = import_module('scionlab.migrations.0005_trc_pem') 19 | 20 | _b64der = "TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2NpbmcgZWxpdCBncmF2aWRhLg==" 21 | _pem = """-----BEGIN TRC----- 22 | TG9yZW0gaXBzdW0gZG9sb3Igc2l0IGFtZXQsIGNvbnNlY3RldHVyIGFkaXBpc2Np 23 | bmcgZWxpdCBncmF2aWRhLg== 24 | -----END TRC----- 25 | """ 26 | 27 | 28 | class MigrateTRCPemTests(SimpleTestCase): 29 | def test_forward(self): 30 | pem = migration_trc_pem.trc_base64der_to_pem(_b64der) 31 | self.assertEqual(pem, _pem) 32 | # sanity check: ensure that lines have expected length 33 | bodyLines = pem.splitlines()[1:-1] 34 | self.assertEqual(len(bodyLines[0]), 64) 35 | self.assertLess(len(bodyLines[1]), 64) 36 | 37 | def test_backward(self): 38 | der = migration_trc_pem.trc_pem_to_base64der(_pem) 39 | self.assertEqual(der, _b64der) 40 | -------------------------------------------------------------------------------- /scionlab/tests/test_portmap.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | from unittest import TestCase 17 | from parameterized import parameterized 18 | from scionlab.util.portmap import PortMap 19 | 20 | _PARAMETERIZED_TEST_IPS = [('127.0.0.1',), (None,)] 21 | 22 | 23 | class PortMapTests(TestCase): 24 | @parameterized.expand(_PARAMETERIZED_TEST_IPS) 25 | def test_add(self, ip): 26 | port = 30000 27 | portmap = PortMap() 28 | portmap.add(ip, port) 29 | self.assertTrue(portmap.is_used(ip, port)) 30 | self.assertTrue(portmap.is_used(None, port)) 31 | 32 | @parameterized.expand(_PARAMETERIZED_TEST_IPS) 33 | def test_empty(self, ip): 34 | port = PortMap().get_port(ip, min=30000) 35 | self.assertEqual(port, 30000) 36 | port = PortMap().get_port(ip, min=30000, max=31000) 37 | self.assertEqual(port, 30000) 38 | port = PortMap().get_port(ip, min=30000, max=31000, preferred=30100) 39 | self.assertEqual(port, 30100) 40 | port = PortMap().get_port(ip, min=30000, max=31000, preferred=12345) # 41 | self.assertEqual(port, 12345) 42 | 43 | @parameterized.expand(_PARAMETERIZED_TEST_IPS) 44 | def test_used_after_get(self, ip): 45 | min_port = 30000 46 | 47 | portmap = PortMap() 48 | self.assertTrue(portmap.is_free(ip, min_port)) 49 | port = portmap.get_port(ip, min_port) 50 | self.assertEqual(port, min_port) 51 | self.assertTrue(portmap.is_used(ip, port)) 52 | self.assertTrue(portmap.is_used(None, port)) 53 | 54 | @parameterized.expand(_PARAMETERIZED_TEST_IPS) 55 | def test_sequential(self, ip): 56 | min_port = 30000 57 | 58 | portmap = PortMap() 59 | port1 = portmap.get_port(ip, min_port) 60 | port2 = portmap.get_port(ip, min_port) 61 | port3 = portmap.get_port(ip, min_port, preferred=port1) # ignored preferred 62 | 63 | self.assertEqual([port1, port2, port3], [min_port, min_port+1, min_port+2]) 64 | 65 | with self.assertRaises(RuntimeError): 66 | portmap.get_port(ip, min=min_port, max=port3) 67 | -------------------------------------------------------------------------------- /scionlab/tests/test_user.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import os 16 | import re 17 | from django.core import mail 18 | from django.test import TestCase 19 | from django.urls import reverse 20 | from django_webtest import WebTest 21 | from scionlab.fixtures.testuser import ( 22 | TESTUSER_EMAIL, 23 | TESTUSER_PWD 24 | ) 25 | 26 | 27 | class LoginRequiredRedirectTests(TestCase): 28 | def setUp(self): 29 | os.environ['RECAPTCHA_DISABLE'] = 'True' # Disable captcha 30 | 31 | def tearDown(selfself): 32 | del os.environ['RECAPTCHA_DISABLE'] # Reenable captcha 33 | 34 | fixtures = ['testdata'] 35 | 36 | def test_not_logged_in(self): 37 | """ 38 | A view with required login redirects to the login page if the user is 39 | not logged in. 40 | """ 41 | requested_url = reverse('user') 42 | 43 | response = self.client.get(requested_url, follow=True) 44 | self.assertEqual(len(response.redirect_chain), 1) 45 | redirected_url = response.redirect_chain[0][0] 46 | 47 | expected_url = '%s?next=%s' % (reverse('login'), requested_url) 48 | self.assertEqual(redirected_url, expected_url) 49 | 50 | self.assertTemplateUsed( 51 | response, 52 | 'registration/login.html', 53 | "Expected login template." 54 | ) 55 | 56 | # Attempt log in: 57 | response = self.client.post( 58 | redirected_url, 59 | {'username': TESTUSER_EMAIL, 'password': TESTUSER_PWD}, 60 | follow=True 61 | ) 62 | # Now we should land on the originally requested page 63 | self.assertEqual(len(response.redirect_chain), 1) 64 | self.assertEqual(response.redirect_chain[0][0], requested_url) 65 | 66 | def test_logged_in(self): 67 | """ 68 | A view with required login is accessible if the user is logged in. 69 | """ 70 | login_success = self.client.login(username=TESTUSER_EMAIL, password=TESTUSER_PWD) 71 | self.assertTrue(login_success) 72 | 73 | response = self.client.get(reverse('user')) 74 | self.assertEqual(response.status_code, 200) 75 | 76 | self.assertTemplateNotUsed( 77 | response, 78 | 'registration/login.html', 79 | "Expected no login template." 80 | ) 81 | 82 | 83 | class PasswordWebTests(WebTest): 84 | def setUp(self): 85 | os.environ['RECAPTCHA_DISABLE'] = 'True' # Disable captcha 86 | 87 | def tearDown(selfself): 88 | del os.environ['RECAPTCHA_DISABLE'] # Reenable captcha 89 | 90 | fixtures = ['testdata'] 91 | 92 | def test_reset_pwd(self): 93 | """ 94 | Reset the password 95 | """ 96 | reset_init_form = self.app.get(reverse('password_reset')).form 97 | reset_init_form['email'] = TESTUSER_EMAIL 98 | reset_init_form.submit() 99 | 100 | self.assertEqual(len(mail.outbox), 1) 101 | reset_mail = mail.outbox[0] 102 | self.assertEqual(reset_mail.recipients(), [TESTUSER_EMAIL]) 103 | reset_mail_message = reset_mail.message().as_string() 104 | links = re.findall(r'http://testserver(/\S*)', reset_mail_message, re.MULTILINE) 105 | self.assertEqual(len(links), 1) 106 | reset_link = links[0] 107 | 108 | new_password = 'scionscion' 109 | pwd_form = self.app.get(reset_link).follow().form 110 | pwd_form['new_password1'] = pwd_form['new_password2'] = new_password 111 | pwd_changed = pwd_form.submit().follow() 112 | 113 | login_form = pwd_changed.click(linkid='id_login').form 114 | login_form['username'] = TESTUSER_EMAIL 115 | login_form['password'] = new_password 116 | response = login_form.submit() 117 | self.assertEqual(response.status_int, 302) 118 | -------------------------------------------------------------------------------- /scionlab/urls.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.views.generic.base import TemplateView 16 | from django.contrib import admin 17 | from django.contrib.auth.decorators import login_required 18 | from django.urls import include, path, re_path 19 | from django.views.generic.base import RedirectView 20 | 21 | from scionlab.views.user_as_views import ( 22 | UserASesView, 23 | UserASCreateView, 24 | UserASDeleteView, 25 | UserASActivateView, 26 | UserASDetailView, 27 | UserASGetConfigView) 28 | from scionlab.views.registration_view import UserRegistrationView, UserRegistrationResendView 29 | from scionlab.views.api import GetHostConfig, PostHostDeployedConfigVersion, gone 30 | from scionlab.views.topology import topology_png, topology_json 31 | 32 | urlpatterns = [ 33 | path('', TemplateView.as_view(template_name='scionlab/home.html'), name='home'), 34 | 35 | # Admin space 36 | path('admin/', admin.site.urls), 37 | 38 | # Authentication 39 | # django.contrib.auth: auth views for logout, password reset/change 40 | path('', include('django.contrib.auth.urls')), 41 | 42 | # django-registration patterns 43 | path('registration/register/', 44 | UserRegistrationView.as_view(), 45 | name='registration_form'), 46 | path('registration/resend', 47 | UserRegistrationResendView.as_view(), 48 | name='registration_resend'), 49 | path('registration/confirm', 50 | TemplateView.as_view(template_name='django_registration/registration_confirm.html'), 51 | name='registration_confirm'), 52 | path('registration/', include('django_registration.backends.activation.urls')), 53 | 54 | # user pages 55 | path('user/', login_required(UserASesView.as_view()), name='user'), 56 | path('user/as/add', login_required(UserASCreateView.as_view()), name='user_as_add'), 57 | # TODO(matzf): maybe we need a slugified AS-id to use in the URL instead of the PK 58 | path('user/as//delete', 59 | login_required(UserASDeleteView.as_view()), 60 | name='user_as_delete'), 61 | path('user/as//activate', 62 | login_required(UserASActivateView.as_view(active=True)), 63 | name='user_as_activate'), 64 | path('user/as//deactivate', 65 | login_required(UserASActivateView.as_view(active=False)), 66 | name='user_as_deactivate'), 67 | path('user/as//config', 68 | login_required(UserASGetConfigView.as_view()), 69 | name='user_as_config'), 70 | path('user/as/', 71 | login_required(UserASDetailView.as_view()), 72 | name='user_as_detail'), 73 | 74 | path('topology.png', topology_png, name='topology.png'), 75 | path('topology', 76 | RedirectView.as_view(url='/topology.png')), 77 | 78 | # API: 79 | path('api/v4/host//config', 80 | GetHostConfig.as_view(), 81 | name='api_get_config'), 82 | path('api/v4/host//deployed_config_version', 83 | PostHostDeployedConfigVersion.as_view(), 84 | name='api_post_deployed_version'), 85 | path('api/v4/topology/topology', 86 | topology_json, 87 | name='api_topology'), 88 | # no longer supported versions of the API 89 | re_path(r'^api/host/', gone), 90 | re_path(r'^api/v2/host/', gone), 91 | re_path(r'^api/v3/host/', gone), 92 | path('api/v2/topology/topology', 93 | RedirectView.as_view(url='/api/v4/topology/topology', permanent=True)), 94 | path('api/v3/topology/topology', 95 | RedirectView.as_view(url='/api/v4/topology/topology', permanent=True)), 96 | ] 97 | -------------------------------------------------------------------------------- /scionlab/util/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | def flatten(lst): 17 | """ 18 | Transform a list of lists into a 1D list 19 | :param List[List[Any]] lst: the list to flatten 20 | :return List[Any]: 21 | """ 22 | return [y for x in lst for y in x] 23 | -------------------------------------------------------------------------------- /scionlab/util/django.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | def value_set(query_set, field_name): 17 | """ 18 | Short-hand for creating a set out of a values-query for a single model field 19 | """ 20 | return set(query_set.values_list(field_name, flat=True)) 21 | -------------------------------------------------------------------------------- /scionlab/util/hashers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2019 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import base64 16 | from collections import OrderedDict 17 | from django.contrib.auth.hashers import BasePasswordHasher, mask_hash 18 | from django.utils.crypto import constant_time_compare 19 | from django.utils.translation import gettext_noop as _ 20 | 21 | 22 | class SCryptPasswordHasher(BasePasswordHasher): 23 | """ 24 | Password hasher using the scrypt algorithm. 25 | """ 26 | 27 | algorithm = "scrypt" 28 | library = ("scrypt", "scrypt",) 29 | Nlog2 = 15 # N == 2 ** 15 == 32768 30 | r = 8 31 | p = 1 32 | keyLen = 64 33 | 34 | def salt(self): 35 | # Use a b64-encoded salt because we need to store the imported salt values, which are 36 | # non-ascii. 37 | return _b64enc(super().salt().encode()) 38 | 39 | def encode(self, password, salt, Nlog2=None, r=None, p=None, keyLen=None): 40 | assert password is not None 41 | assert salt and '$' not in salt 42 | Nlog2 = Nlog2 or self.Nlog2 43 | r = r or self.r 44 | p = p or self.p 45 | keyLen = keyLen or self.keyLen 46 | 47 | scrypt = self._load_library() 48 | hash = scrypt.hash(password, _b64dec(salt), 2**Nlog2, r, p, keyLen) 49 | return self._format(Nlog2, r, p, salt, _b64enc(hash)) 50 | 51 | def verify(self, password, encoded): 52 | Nlog2, r, p, salt, hash = self._parse(encoded) 53 | keyLen = len(_b64dec(hash)) 54 | encoded_2 = self.encode(password, salt, Nlog2, r, p, keyLen) 55 | return constant_time_compare(encoded, encoded_2) 56 | 57 | def safe_summary(self, encoded): 58 | Nlog2, r, p, salt, hash = self._parse(encoded) 59 | return OrderedDict([ 60 | (_('algorithm'), self.algorithm), 61 | (_('N'), 2**Nlog2), 62 | (_('r'), r), 63 | (_('p'), p), 64 | (_('salt'), mask_hash(salt, show=2)), 65 | (_('hash'), mask_hash(hash)), 66 | ]) 67 | 68 | def harden_runtime(self, password, encoded): 69 | Nlog2, r, p, salt, hash = self._parse(encoded) 70 | 71 | # get same number of iterations as with self.Nlog2: 72 | # 2**Nlog2 + sum(2**n for n in range(Nlog2, self.Nlog2)) = 2**self.Nlog2 73 | for n in range(Nlog2, self.Nlog2): 74 | self.encode(password, salt, n, r, p) 75 | 76 | def _parse(self, encoded): 77 | algorithm, Nlog2, r, p, salt, hash = encoded.split('$', 6) 78 | assert algorithm == self.algorithm 79 | return int(Nlog2), int(r), int(p), salt, hash 80 | 81 | def _format(self, Nlog2, r, p, salt, hash): 82 | return "%s$%d$%d$%d$%s$%s" % (self.algorithm, Nlog2, r, p, salt, hash) 83 | 84 | 85 | def _b64enc(bytes): 86 | return base64.b64encode(bytes).decode('ascii').strip() 87 | 88 | 89 | def _b64dec(string): 90 | return base64.b64decode(string.encode('ascii')) 91 | -------------------------------------------------------------------------------- /scionlab/util/http.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | import base64 16 | import binascii 17 | from django.http import HttpResponse 18 | 19 | 20 | class HttpResponseAttachment(HttpResponse): 21 | """ 22 | Simple HttpResponse to send content with Content-Disposition "attachment". 23 | In contrast to django.http.FileResponse, this is non-streaming. 24 | """ 25 | def __init__(self, filename, *args, **kwargs): 26 | super().__init__(*args, **kwargs) 27 | self['Content-Disposition'] = 'attachment; filename="{}"'.format(filename) 28 | 29 | 30 | class HttpResponseAuthenticate(HttpResponse): 31 | """ 32 | Response to unauthenticated requests to views requiring Basic authentication. 33 | """ 34 | status_code = 401 35 | 36 | def __init__(self, realm, *args, **kwargs): 37 | """ 38 | :param str realm: User visible realm 39 | """ 40 | super().__init__(*args, **kwargs) 41 | self['WWW-Authenticate'] = 'Basic realm="%s", charset="UTF-8"' % realm 42 | 43 | 44 | def _parse_basic_auth(request): 45 | """ 46 | Helper for basicauth. Extract password, username from Authorization Basic header. 47 | :returns: username, password as (str, str) or (None, None) if header not present or malformed. 48 | """ 49 | if 'HTTP_AUTHORIZATION' in request.META: 50 | auth = request.META['HTTP_AUTHORIZATION'].split() 51 | if len(auth) == 2 and auth[0].lower() == "basic": 52 | try: 53 | auth_str = base64.b64decode(auth[1], validate=True).decode() 54 | uname_pwd = auth_str.split(':') 55 | if len(uname_pwd) == 2: 56 | return tuple(uname_pwd) 57 | except binascii.Error: 58 | pass 59 | except UnicodeDecodeError: 60 | pass 61 | return (None, None) 62 | 63 | 64 | def basicauth(authenticate, realm=""): 65 | """ 66 | View decorator for basic authentication. 67 | :param authenticate: function (username: str, password: str) -> bool 68 | :param str realm: User visible realm 69 | """ 70 | def view_decorator(view): 71 | def wrapper(request, *args, **kwargs): 72 | uname, passwd = _parse_basic_auth(request) 73 | if uname and passwd and authenticate(uname, passwd): 74 | return view(request, *args, **kwargs) 75 | else: 76 | return HttpResponseAuthenticate(realm) 77 | return wrapper 78 | return view_decorator 79 | -------------------------------------------------------------------------------- /scionlab/util/portmap.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from scionlab.defines import MAX_PORT 16 | 17 | 18 | class PortMap: 19 | """ 20 | Simple helper for managing/assigning ports. 21 | Keeps a set of used ports per IP. 22 | The IP `None` is used for ports bound on any address (0.0.0.0 / ::). 23 | 24 | Note: this map does not care about the type used to represent the IPs; it just assumes 25 | that identical values represent identical addresses. 26 | """ 27 | 28 | def __init__(self): 29 | self.ports = dict() # ports[ip] = set of used ports 30 | 31 | def get_port(self, ip, min, max=MAX_PORT, preferred=None): 32 | """ 33 | Get a free port for this IP address in the given range. 34 | The returned port is immediately marked as used for the queried IP address. 35 | :param ip: IP address or `None` for the unspecified address 36 | :param int min: Start of acceptable port range 37 | :param int max: Optional, end (included) of acceptable port range 38 | :param int preferred: Optional, preferred port. Will be selected if free (range not checked) 39 | :returns: Port 40 | :raises: RuntimeError if no free port could be found 41 | """ 42 | port = self._find_free_port(ip, min, max, preferred) 43 | if port is None: 44 | raise RuntimeError('No free port available in range') 45 | self.add(ip, port) 46 | return port 47 | 48 | def _find_free_port(self, ip, min, max, preferred): 49 | """ 50 | Find a free port for this IP address in the given range. 51 | """ 52 | if preferred and self.is_free(ip, preferred): 53 | return preferred 54 | for port in range(min, max): 55 | if self.is_free(ip, port): 56 | return port 57 | return None 58 | 59 | def is_free(self, ip, port): 60 | """ 61 | Check if the given IP/port combination is free. 62 | :param ip: IP address or `None` for the unspecified address 63 | :param int port: Port 64 | :returns: True iff port is free 65 | """ 66 | return not self.is_used(ip, port) 67 | 68 | def is_used(self, ip, port): 69 | """ 70 | check if the given ip/port combination is used. 71 | :param ip: ip address or `none` for the unspecified address 72 | :param int port: port 73 | :returns: true iff port is used 74 | """ 75 | if ip is not None: 76 | return port in self.ports.get(ip, set()) or port in self.ports.get(None, set()) 77 | else: 78 | return any(port in portset for portset in self.ports.values()) 79 | 80 | def add(self, ip, port): 81 | """ 82 | add a ip/port combination to the set of used ports. this has no effect if it 83 | is already present in the set of used ports. 84 | :param ip: ip address or `none` for the unspecified address 85 | :param int port: port 86 | :returns bool: Whether or not the added port is already present 87 | """ 88 | assert isinstance(port, int) 89 | if ip not in self.ports: 90 | ports = set() 91 | self.ports[ip] = ports 92 | else: 93 | ports = self.ports[ip] 94 | clash = port in ports 95 | ports.add(port) 96 | return clash 97 | 98 | def update(self, ip, ports): 99 | """ 100 | Add many IP/port combination to the set of used ports. This has no effect if it 101 | is already present in the set of used ports. 102 | :param ip: IP address or `None` for the unspecified address 103 | :param iterable ports: Ports 104 | """ 105 | assert all(isinstance(port, int) for port in ports) 106 | self.ports.setdefault(ip, set()).update(ports) 107 | -------------------------------------------------------------------------------- /scionlab/views/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/netsec-ethz/scionlab/9cd14431c0081af4a1a6ab8827cdacf818ef1179/scionlab/views/__init__.py -------------------------------------------------------------------------------- /scionlab/views/registration_view.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | from django.urls import reverse_lazy 16 | from django_registration.backends.activation.views import RegistrationView 17 | from scionlab.models.user import User 18 | from scionlab.forms.registration_form import RegistrationFormWithCaptcha, RegistrationResendForm 19 | 20 | 21 | class UserRegistrationView(RegistrationView): 22 | success_url = reverse_lazy('registration_confirm') 23 | form_class = RegistrationFormWithCaptcha 24 | template_name = 'django_registration/registration_form.html' 25 | 26 | def register(self, form): 27 | return super().register(form) 28 | 29 | 30 | class UserRegistrationResendView(RegistrationView): 31 | success_url = reverse_lazy('registration_confirm') 32 | form_class = RegistrationResendForm 33 | template_name = 'django_registration/registration_resend.html' 34 | 35 | def register(self, form): 36 | email = form.cleaned_data['email'] 37 | user = User.objects.filter(email=email, is_active=False).first() 38 | if not user: 39 | return None # Do nothing, just pretend it's successful. 40 | self.send_activation_email(user) 41 | return user 42 | -------------------------------------------------------------------------------- /scionlab/wsgi.py: -------------------------------------------------------------------------------- 1 | # Copyright 2018 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | """ 16 | WSGI config for scionlab project. 17 | 18 | It exposes the WSGI callable as a module-level variable named ``application``. 19 | 20 | For more information on this file, see 21 | https://docs.djangoproject.com/en/2.1/howto/deployment/wsgi/ 22 | """ 23 | 24 | import os 25 | 26 | from django.core.wsgi import get_wsgi_application 27 | 28 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'scionlab.settings.production') 29 | 30 | application = get_wsgi_application() 31 | -------------------------------------------------------------------------------- /scripts/create-fixture-testdata.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright 2018 ETH Zurich 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -e 18 | 19 | # backup db 20 | tempdir=`mktemp -d` 21 | mv run/dev.sqlite3 $tempdir 22 | 23 | # init new db 24 | python manage.py migrate -v 1 25 | 26 | # create and dump data for fixture 27 | python manage.py shell -c 'from scionlab.fixtures.testdata import create_testdata; create_testdata()' 28 | python manage.py dumpdata --format=yaml scionlab > scionlab/fixtures/testdata.yaml 29 | 30 | # fixup timestamps to reduce noise 31 | # Note: these timestamps are only informative and are not supposed to change the behaviour in any way 32 | sed 's/\(created_date\|modified_date\|date_joined\): .*$/\1: 2019-10-02 03:00:00/' -i scionlab/fixtures/testdata.yaml 33 | 34 | # get db back 35 | mv $tempdir/dev.sqlite3 run/ 36 | -------------------------------------------------------------------------------- /scripts/deactivate_dormant.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | """ 17 | :mod: scripts.deactivate_dormant 18 | ========================= 19 | 20 | Run with python manage.py runscript deactivate_dormant --script-args 21 | 22 | Bulk deactivate inactive ASes for users who have not logged in for over a year. 23 | Reads a list ASes that have been inactive for a long time, created from data of the monitoring 24 | system (by inspecting router_interface_up at the attachment points). 25 | Expects newline separated AS ids, in the form "ffaa:1:abc". 26 | """ 27 | 28 | import datetime 29 | import pathlib 30 | from django.db import transaction 31 | from django.db.models import Q 32 | 33 | from scionlab.models.user_as import UserAS 34 | 35 | nologin_threshold = datetime.timedelta(days=365) 36 | 37 | 38 | def run(*args): 39 | if len(args) != 1: 40 | print("run with --script-args ") 41 | return 42 | filename = args[0] 43 | as_ids = pathlib.Path(filename).read_text().splitlines() 44 | deactivate_dormant(as_ids) 45 | 46 | 47 | @transaction.atomic 48 | def deactivate_dormant(as_ids): 49 | last_login_before = datetime.datetime.utcnow() - nologin_threshold 50 | q = UserAS.objects.filter( 51 | Q(as_id__in=as_ids), 52 | Q(owner__last_login__lt=last_login_before) | Q(owner__last_login=None), 53 | ) 54 | for user_as in q: 55 | print(user_as.as_id) 56 | user_as.update_active(False) 57 | -------------------------------------------------------------------------------- /scripts/init-test-db.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # 3 | # Copyright 2019 ETH Zurich 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | set -e 18 | 19 | rm run/dev.sqlite3 || true 20 | 21 | python manage.py migrate 22 | 23 | python manage.py loaddata scionlab/fixtures/testdata.yaml 24 | -------------------------------------------------------------------------------- /scripts/merge_ap_routers.py: -------------------------------------------------------------------------------- 1 | # Copyright 2021 ETH Zurich 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | 15 | 16 | """ 17 | :mod: scripts.merge_ap_routers 18 | ========================= 19 | 20 | Run with python manage.py runscript merge_ap_routers 21 | 22 | For each attachment point, merge all border router instances such that only a single router is used. 23 | This is a cleanup step. 24 | Previously, we've automatically distributed the interfaces for attachment points over many router 25 | instances with at most ~12 interfaces each (`AttachmentPoint.split_border_routers`). This was a 26 | workaround for some issue in an old version of the router. Now, this is no longer necessary and 27 | running many instances is harmful for the system's performance. 28 | """ 29 | 30 | from django.db import transaction 31 | 32 | from scionlab.models.user_as import AttachmentPoint 33 | 34 | 35 | @transaction.atomic 36 | def run(): 37 | for ap in AttachmentPoint.objects.all(): 38 | merge_router_instances(ap.AS.hosts.get()) 39 | 40 | 41 | def merge_router_instances(host): 42 | chosen = host.border_routers.first() 43 | for iface in host.interfaces.all(): 44 | iface.border_router = chosen 45 | iface.save() 46 | host.border_routers.exclude(pk=chosen.pk).delete() 47 | -------------------------------------------------------------------------------- /scripts/port_clash_fixup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | :mod: scripts.port_clash_fixup 18 | ========================= 19 | 20 | Prints existing port clashes and fixes them. 21 | For every clash, the interface that was modified the first is kept, but 22 | for all the other ones a new port is found, and the associated 23 | AP's configuration version is bumped. 24 | This is intended to be used only once. 25 | 26 | Run with python manage.py runscript port_clash_fixup 27 | """ 28 | 29 | 30 | from collections import defaultdict 31 | from django.db import transaction 32 | from scionlab.models.core import Host, Interface 33 | 34 | 35 | def run(): 36 | # get the clashes: 37 | clashes = dict() 38 | for h in Host.objects.all(): 39 | c = get_clashes(h) 40 | if len(c): 41 | clashes[h] = c 42 | # print them 43 | for k, v in clashes.items(): 44 | print('host {host} has {clashes} clashes:'.format( 45 | host=k, clashes=len(v))) 46 | for c in v: 47 | print('\tsocket = {socket} IDs = {ids}'.format(socket=c[0], ids=c[1])) 48 | # fix them 49 | for clashes in clashes.values(): 50 | for c in clashes: 51 | fix_clash(c[1]) 52 | 53 | 54 | def get_clashes(host): 55 | """ 56 | returns a list of (socket,[list_of_ids]) 57 | """ 58 | sockets = defaultdict(list) 59 | for iface in Interface.objects.filter(host=host): 60 | socket = (iface.get_public_ip(), iface.public_port) 61 | sockets[socket].append(iface.pk) 62 | clashes = list() 63 | for k, v in sockets.items(): 64 | if len(v) > 1: 65 | clashes.append((k, v)) 66 | return clashes 67 | 68 | 69 | @transaction.atomic 70 | def fix_clash(iface_ids): 71 | """ 72 | sorts the interfaces by modification data of the user AS, 73 | keeps the interface for the oldest modified user AS, and for the remaining interfaces 74 | selects a new port and bumps the configuration for the user AS host 75 | """ 76 | ifaces = Interface.objects.\ 77 | filter(pk__in=iface_ids).\ 78 | order_by('-link_as_interfaceA__interfaceB__AS__modified_date') 79 | # don't modify the AS that obtained that port the first 80 | ifaces = ifaces[:len(ifaces)-1] 81 | for iface in ifaces: 82 | iface.public_port = iface.host.find_public_port() 83 | iface.save() 84 | iface.host.bump_config() 85 | -------------------------------------------------------------------------------- /scripts/recreate-test-results.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Copyright 2019 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | 17 | # recreate expected output in scionlab/tests/data/test_config_tar/ 18 | RECREATE_TEST_RESULTS=1 python manage.py test -v0 scionlab.tests.test_config_tar 19 | -------------------------------------------------------------------------------- /scripts/vpn_cleanup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | # Copyright 2020 ETH Zurich 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # You may obtain a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | """ 17 | :mod: scripts.vpn_cleanup 18 | ========================= 19 | 20 | Spring cleaning for VPN. This is intended to be used only once. 21 | 22 | - Reset keys and certificates for VPN servers and clients (after resetting root CA) 23 | - Assign sensible, non-overlapping 10.x.0.0/16 subnet to each VPN, re-assign server and client IPs 24 | in these subnets. 25 | 26 | Run with python manage.py runscript vpn_cleanup 27 | """ 28 | 29 | 30 | import ipaddress 31 | 32 | from scionlab.models.core import Interface 33 | from scionlab.models.vpn import VPN, VPNClient 34 | 35 | 36 | def run(): 37 | recreate_keys_certs() 38 | 39 | iface_vpn_rel = record_vpn_relations() 40 | reassign_subnets() 41 | reassign_client_ips() 42 | update_vpn_ip_usage(iface_vpn_rel) 43 | 44 | 45 | def recreate_keys_certs(): 46 | for v in VPN.objects.all(): 47 | v.init_key() 48 | v.save() 49 | 50 | for c in VPNClient.objects.all(): 51 | c.init_key() 52 | c.save() 53 | 54 | 55 | def record_vpn_relations(): 56 | """ 57 | Before we change the ips of the VPN/VPNClient objects, we need to remember where these 58 | are used, as we don't have this relation directly in the DB -- only the IP address 59 | used tells us whether a link uses VPN. 60 | The VPN IPs are _only_ used as the public_ip of Interface-objects (i.e. not for Host.public_ip, 61 | nor for bind_ip or internal_ip). We only handle this case. 62 | 63 | As there can, in principle, be multiple matching VPNClient objects per Interface (overlapping 64 | subnets...) we try to resolve this ambiguity by checking if there is a related VPN on the 65 | opposite site of the Link. 66 | 67 | :returns: map from interface to the related VPN or VPNClient object 68 | """ 69 | 70 | iface_vpn_rel = {} 71 | for iface in Interface.objects.all(): 72 | vpn_rel = related_vpn_obj(iface) 73 | if vpn_rel: 74 | iface_vpn_rel[iface] = vpn_rel 75 | return iface_vpn_rel 76 | 77 | 78 | def related_vpn_obj(iface): 79 | server = iface.host.vpn_servers.filter(server_vpn_ip=iface.public_ip).first() 80 | # note: first because no host has >1 VPN server 81 | if server: 82 | return server 83 | 84 | clients = iface.host.vpn_clients.filter(ip=iface.public_ip) 85 | if not clients: 86 | return None 87 | if len(clients) == 1: 88 | return clients[0] 89 | active_clients = list(c for c in clients if c.active) 90 | if len(active_clients) == 1: 91 | return active_clients[0] 92 | 93 | related_vpn = iface.remote_interface().host.vpn_servers.first() 94 | if related_vpn: 95 | matching_clients = list(c for c in clients if c.vpn == related_vpn) 96 | if matching_clients: 97 | return matching_clients[0] 98 | return clients[0] 99 | 100 | 101 | def reassign_subnets(): 102 | for i, v in enumerate(VPN.objects.all().order_by('pk')): 103 | subnet = ipaddress.ip_network('10.%i.0.0/16' % (i+1)) 104 | server_vpn_ip = next(subnet.hosts()) 105 | v.subnet = str(subnet) 106 | v.server_vpn_ip = str(server_vpn_ip) 107 | v.save() 108 | 109 | 110 | def reassign_client_ips(): 111 | # Update 112 | for v in VPN.objects.all(): 113 | subnet = ipaddress.ip_network(v.subnet) 114 | hosts_generator = subnet.hosts() 115 | server_vpn_ip = next(hosts_generator) # skip server ip 116 | assert v.server_vpn_ip == str(server_vpn_ip) 117 | for c in v.clients.all().order_by('ip'): 118 | c.ip = str(next(hosts_generator)) 119 | c.save() 120 | 121 | 122 | def update_vpn_ip_usage(iface_vpn_rel): 123 | for iface, vpn_rel in iface_vpn_rel.items(): 124 | vpn_rel.refresh_from_db() 125 | if isinstance(vpn_rel, VPN): 126 | new_ip = vpn_rel.server_vpn_ip 127 | else: 128 | assert isinstance(vpn_rel, VPNClient) 129 | new_ip = vpn_rel.ip 130 | 131 | iface.update(public_ip=new_ip) 132 | -------------------------------------------------------------------------------- /static_bin/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM golang:1.21-bookworm 2 | 3 | # netsec-ethz/scion, last biweekly update on 2024/11/26 4 | ARG scion_commit=3b4ddc53e94d4ff0dd8ec8f3e8f1db6547d881bd 5 | 6 | RUN mkdir /scion 7 | WORKDIR /scion 8 | 9 | RUN git init && \ 10 | git remote add netsec-ethz https://github.com/netsec-ethz/scion.git && \ 11 | git fetch netsec-ethz && \ 12 | git -c advice.detachedHead=false checkout $scion_commit 13 | 14 | RUN startup_version=$(git describe --tags --always)-scionlab && \ 15 | GOOS=linux GOARCH=amd64 go build \ 16 | -ldflags="-s -w -X github.com/scionproto/scion/private/env.StartupVersion=$startup_version" \ 17 | -o bin/scion-pki-linux-amd64 \ 18 | ./scion-pki/cmd/scion-pki 19 | 20 | RUN startup_version=$(git describe --tags --always)-scionlab && \ 21 | GOOS=darwin GOARCH=amd64 go build \ 22 | -ldflags="-s -w -X github.com/scionproto/scion/private/env.StartupVersion=$startup_version" \ 23 | -o bin/scion-pki-darwin-amd64 \ 24 | ./scion-pki/cmd/scion-pki 25 | -------------------------------------------------------------------------------- /static_bin/README.md: -------------------------------------------------------------------------------- 1 | # Binary files needed by the scionlab project 2 | 3 | - `scion-pki` from netsec-ethz/scion, used to create TRCs. 4 | 5 | This binary is directly contained in the scionlab repository. 6 | This allows us to simply guarantee that we use the same dependency version 7 | during development, e.g. for running tests, and in the production deployment, 8 | without relying on external services. 9 | The alternative of e.g. installing from our debian packages would introduce 10 | tricky constraints to the release sequence -- the coordinator would require 11 | updated packages to function, but at the same time the coordinator should be 12 | available to serve updated configuration at the time the packages are 13 | released. 14 | 15 | 16 | ## How to update 17 | 18 | Requires docker. 19 | 20 | 1. Modify the `scion_commit` hash in the Dockerfile & update the date of the 21 | commit in the comment. 22 | 2. Run `build.sh`; this will build `scion-pki` (using `go build`) in a docker 23 | container and copy the resulting binary to this directory. 24 | 3. Commit the modified `Dockerfile` and `scion-pki` 25 | -------------------------------------------------------------------------------- /static_bin/build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | set -e 3 | # Copyright 2020 ETH Zurich 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); 6 | # you may not use this file except in compliance with the License. 7 | # You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, 13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | # See the License for the specific language governing permissions and 15 | # limitations under the License. 16 | 17 | cd "$(dirname "$0")" || return 18 | docker build -t scionlab-scion-pki-build - < Dockerfile 19 | cid="$(docker create scionlab-scion-pki-build)" 20 | docker cp "$cid":/scion/bin/scion-pki-linux-amd64 . 21 | docker cp "$cid":/scion/bin/scion-pki-darwin-amd64 . 22 | docker rm -v "$cid" 23 | docker image rm scionlab-scion-pki-build 24 | -------------------------------------------------------------------------------- /static_bin/scion-pki: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | goos=$(uname -s | tr '[:upper:]' '[:lower:]') 4 | goarch=$(uname -m | tr '[:upper:]' '[:lower:]') 5 | 6 | if [ "${goarch}" = "x86_64" ]; then 7 | goarch="amd64" 8 | fi 9 | 10 | filename="$(dirname "$0")/scion-pki-${goos}-${goarch}" 11 | 12 | if [ ! -e "${filename}" ]; then 13 | echo "Unsupported system: ${goos}-${goarch}" 14 | exit 1 15 | fi 16 | 17 | exec "${filename}" "$@" 18 | -------------------------------------------------------------------------------- /static_bin/scion-pki-darwin-amd64: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:f06f3efe0d1ce7f1270b2351f5a8e6a30a491082eaa492c7ad7d4ac50ced7d78 3 | size 22228256 4 | -------------------------------------------------------------------------------- /static_bin/scion-pki-linux-amd64: -------------------------------------------------------------------------------- 1 | version https://git-lfs.github.com/spec/v1 2 | oid sha256:4f8d7bf68d9177c0ed1c748b4de6b5c1366701aa8bf75a8a736d03187e84697a 3 | size 21268120 4 | --------------------------------------------------------------------------------