├── .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 |
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.
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 |
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 |
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 |
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 |
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 |
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 |
AS ID
33 |
Label
34 |
ISD
35 |
Parent ASes
36 |
IP/Port
37 |
Active
38 |
Configuration
39 |
40 |
41 |
42 | {% for user_as in object_list %}
43 |