summaryrefslogtreecommitdiff
path: root/topics/deploy
diff options
context:
space:
mode:
Diffstat (limited to 'topics/deploy')
-rw-r--r--topics/deploy/deployment.gmi36
-rw-r--r--topics/deploy/installation.gmi354
-rw-r--r--topics/deploy/machines.gmi46
-rw-r--r--topics/deploy/our-virtuoso-instances.gmi41
-rw-r--r--topics/deploy/useful-shell-scripts.gmi52
-rw-r--r--topics/deploy/uthsc-vpn-with-free-software.gmi60
-rw-r--r--topics/deploy/uthsc-vpn.scm44
7 files changed, 633 insertions, 0 deletions
diff --git a/topics/deploy/deployment.gmi b/topics/deploy/deployment.gmi
new file mode 100644
index 0000000..b844821
--- /dev/null
+++ b/topics/deploy/deployment.gmi
@@ -0,0 +1,36 @@
+# Deploy GeneNetwork
+
+# Description
+
+This page attempts to document the deployment process we have for GeneNetwork. We use Guix system containers for deployment of CI/CD and the Guix configuration for the CI/CD container should be considered the authoritative reference.
+
+=> https://github.com/genenetwork/genenetwork-machines/blob/main/genenetwork-development.scm
+
+See also
+
+=> ./guix-system-containers-and-how-we-use-them
+
+## genenetwork2
+
+To install GN2 by hand for development we also track
+
+=> ./developing-against-gn
+=> ./installation
+
+## genenetwork3
+
+Deployment-specific settings for genenetwork3 should be written to a configuration file and the path to that file should be set in the GN3_CONF environment variable.
+
+Here's a typical configuration file. Please take care to customize it to your specific requirements.
+```
+SPARQL_ENDPOINT="http://localhost:9082/sparql"
+DATA_DIR="/export/data/genenetwork"
+XAPIAN_DB_PATH="/export/data/genenetwork-xapian"
+```
+DATA_DIR must contain two directories—genotype_files and synteny.
+
+## deploy guix
+
+See also
+
+=> guix-profiles.gmi
diff --git a/topics/deploy/installation.gmi b/topics/deploy/installation.gmi
new file mode 100644
index 0000000..ef25079
--- /dev/null
+++ b/topics/deploy/installation.gmi
@@ -0,0 +1,354 @@
+# Installation
+
+This document is WIP and still a mixture of old and new docs.
+
+Large system deployments can get very complex. In this document we explain the GeneNetwork reproducible deployment system which is based on GNU Guix The Guix system can be used to install GN with all its files and dependencies.
+
+Note that the official deployment works through a Guix VM. This is described in
+
+=> ./deployment
+
+# Check list
+
+To run GeneNetwork the following services need to function:
+
+* [ ] GNU Guix with a guix profile for genenetwork2
+* [ ] A path to the (static) genotype files
+* [?] Gn-proxy for authentication
+* [ ] The genenetwork3 service
+* [ ] Redis
+* [ ] Mariadb
+
+# Installing Guix packages
+
+Make sure to install GNU Guix using the binary download instructions on the main website. Follow the instructions on Note the download amounts to several GBs of data. Debian-derived distros may support
+
+```
+apt-get install guix
+```
+
+# Creating a GNU Guix profile
+
+We run a GNU Guix channel with packages at
+
+=> https://gitlab.com/genenetwork/guix-bioinformatics
+
+The README has instructions hosting a channel (recommended!), but sometimes we use the GUIX_PACKAGE_PATH instead. First upgrade to a recent guix with
+
+```
+mkdir ~/opt
+guix pull -p ~/opt/guix-pull
+```
+
+It should upgrade (ignore the locales warnings). You can optionally specify the specific git checkout of guix with
+
+```
+guix pull -p ~/opt/guix-pull --commit=f04883d
+```
+
+which is useful when you need to roll back to an earlier version (sometimes our channel goes out of sync). Next, we install GeneNetwork2 with
+
+```
+source ~/opt/guix-pull/etc/profile
+git clone https://git.genenetwork.org/guix-bioinformatics/guix-bioinformatics.git ~/guix-bioinformatics
+```
+
+you probably also need guix-past (the upstream channel for older packages):
+
+```
+git clone https://gitlab.inria.fr/guix-hpc/guix-past.git ~/guix-past
+cd ~/guix-past
+env GUIX_PACKAGE_PATH=$HOME/guix-bioinformatics:$HOME/guix-past/modules ~/opt/guix-pull/bin/guix package -i genenetwork2 -p ~/opt/genenetwork2
+```
+
+Ignore the warnings. Guix should install the software without trying to build everything. If you system insists on building all packages, try the `--dry-run` switch and fix the [[https://guix.gnu.org/manual/en/html_node/Substitute-Server-Authorization.html][substitutes]]. You may add the `--substitute-urls="http://guix.genenetwork.org https://ci.guix.gnu.org https://mirror.hydra.gnu.org"` switch.
+
+The guix.genenetwork.org has most of our packages pre-built(!). To use it on your own machine the public key is
+
+```
+(public-key
+ (ecc
+ (curve Ed25519)
+ (q #9F56EAB5CE37AA15693C31F451140588240F259676C137E31C0CA70EC4D1B534#)
+ )
+ )
+```
+
+Once we have a GNU Guix profile, a running database (see below) and the file storage,
+we should be ready to fire up GeneNetwork:
+
+# Running GN2
+
+Check out the source with git:
+
+```
+git clone git@github.com:genenetwork/genenetwork2.git
+cd genenetwork2
+```
+
+You may want to use the testing branch.
+
+Run GN2 with earlier created Guix profile
+
+```
+export GN2_PROFILE=$HOME/opt/genenetwork2
+env TMPDIR=$HOME/tmp WEBSERVER_MODE=DEBUG LOG_LEVEL=DEBUG SERVER_PORT=5012 GENENETWORK_FILES=/export/data/genenetwork/genotype_files SQL_URI=mysql://webqtlout:webqtlout@localhost/db_webqtl ./bin/genenetwork2 etc/default_settings.py -gunicorn-dev
+```
+
+The script comes with debug and logging switches can be particularly useful when
+developing GN2. Location and files are examples.
+
+It may be useful to tunnel the web server to your local browser with an ssh tunnel:
+
+## Testing on an ssh tunnnel
+
+If you want to test a service running on the server on a certain port (say 8202) use
+
+ ssh -L 8202:127.0.0.1:8202 -f -N myname@penguin2.genenetwork.org
+
+And browse on your local machine to http://localhost:8202/
+
+# BELOW INFORMATION NEEDS TO BE UPDATED
+
+* Run gn-proxy
+
+GeneNetwork requires a separate gn-proxy server which handles
+authorisation and access control. For instructions see the
+[[https://github.com/genenetwork/gn-proxy][README]]. Note it may already be running on our servers!
+
+* Run Redis
+
+Redis part of GN2 deployment and will be started by the ./bin/genenetwork2
+startup script.
+
+* Run MariaDB server
+** Install MariaDB with GNU GUIx
+
+These are the steps you can take to install a fresh installation of
+mariadb (which comes as part of the GNU Guix genenetwork2 install).
+
+As root configure the Guix profile
+
+: . ~/opt/genenetwork2/etc/profile
+
+and run for example
+
+#+BEGIN_SRC bash
+adduser mariadb && addgroup mariadb
+mkdir -p /export2/mariadb/database
+chown mariadb.mariadb -R /export2/mariadb/
+mkdir -p /var/run/mysqld
+chown mariadb.mariadb /var/run/mysqld
+su mariadb
+mysql --version
+ mysql Ver 15.1 Distrib 10.1.45-MariaDB, for Linux (x86_64) using readline 5.1
+mysql_install_db --user=mariadb --datadir=/export2/mariadb/database
+mysqld -u mariadb --datadir=/exportdb/mariadb/database/mariadb --explicit_defaults_for_timestamp -P 12048"
+#+END_SRC
+
+If you want to run as root you may have to set
+
+: /etc/my.cnf
+: [mariadbd]
+: user=root
+
+You also need to set
+
+: ft_min_word_len = 3
+
+To make sure word text searches (shh) work and rebuild the tables if
+required.
+
+To check error output in a file on start-up run with something like
+
+: mariadbd -u mariadb --console --explicit_defaults_for_timestamp --datadir=/gnu/mariadb --log-error=~/test.log
+
+Other tips are that Guix installs mariadbd in your profile, so this may work
+
+: /home/user/.guix-profile/bin/mariadbd -u mariadb --explicit_defaults_for_timestamp --datadir=/gnu/mariadb
+
+When you get errors like:
+
+: qlalchemy.exc.IntegrityError: (_mariadb_exceptions.IntegrityError) (1215, 'Cannot add foreign key constraint')
+
+you may need to set
+
+: set foreign_key_checks=0
+
+** Load the small database in MySQL
+
+At this point we require the underlying distribution to install and
+run mysqld (see next section for GNU Guix). Currently we have two databases for deployment,
+'db_webqtl_s' is the small testing database containing experiments
+from BXD mice and 'db_webqtl_plant' which contains all plant related
+material.
+
+Download one database from
+
+http://ipfs.genenetwork.org/ipfs/QmRUmYu6ogxEdzZeE8PuXMGCDa8M3y2uFcfo4zqQRbpxtk
+
+After installation unzip the database binary in the MySQL directory
+
+#+BEGIN_SRC sh
+cd ~/mysql
+p7zip -d db_webqtl_s.7z
+chown -R mysql:mysql db_webqtl_s/
+chmod 700 db_webqtl_s/
+chmod 660 db_webqtl_s/*
+#+END_SRC
+
+restart MySQL service (mysqld). Login as root
+
+: mysql_upgrade -u root --force
+
+: myslq -u root
+
+and
+
+: mysql> show databases;
+: +--------------------+
+: | Database |
+: +--------------------+
+: | information_schema |
+: | db_webqtl_s |
+: | mysql |
+: | performance_schema |
+: +--------------------+
+
+Set permissions and match password in your settings file below:
+
+: mysql> grant all privileges on db_webqtl_s.* to gn2@"localhost" identified by 'webqtl';
+
+You may need to change "localhost" to whatever domain you are
+connecting from (mysql will give an error).
+
+Note that if the mysql connection is not working, try connecting to
+the IP address and check server firewall, hosts.allow and mysql IP
+configuration (see below).
+
+Note for the plant database you can rename it to db_webqtl_s, or
+change the settings in etc/default_settings.py to match your path.
+
+* Get genotype files
+
+The script looks for genotype files. You can find them in
+http://ipfs.genenetwork.org/ipfs/QmXQy3DAUWJuYxubLHLkPMNCEVq1oV7844xWG2d1GSPFPL
+
+#+BEGIN_SRC sh
+mkdir -p $HOME/genotype_files
+cd $HOME/genotype_files
+
+#+END_SRC
+
+* GN2 Dependency Graph
+
+List of all runtime dependencies for GN2 as installed by GNU Guix.
+
+https://genenetwork.org/environments/
+
+* Working with the GN2 source code
+
+See [[development.org]].
+
+* Read more
+
+If you want to understand the architecture of GN2 read
+[[Architecture.org]]. The rest of this document is mostly on deployment
+of GN2.
+
+* Trouble shooting
+
+** ImportError: No module named jinja2
+
+If you have all the Guix packages installed this error points out that
+the environment variables are not set. Copy-paste the paths into your
+terminal (mainly so PYTHON_PATH and R_LIBS_SITE are set) from the
+information given by guix:
+
+: guix package --search-paths
+
+On one system:
+
+: export PYTHONPATH="$HOME/.guix-profile/lib/python3.8/site-packages"
+: export R_LIBS_SITE="$HOME/.guix-profile/site-library/"
+: export GEM_PATH="$HOME/.guix-profile/lib/ruby/gems/2.2.0"
+
+and perhaps a few more.
+** ERROR: 'can not find directory $HOME/gn2_data' or 'can not find directory $HOME/genotype_files/genotype'
+
+The default settings file looks in your $HOME/gn2_data. Since these
+files come with a Guix installation you should take a hint from the
+values in the installed version of default_settings.py (see above in
+this document).
+
+You can use the GENENETWORK_FILES switch to set the datadir, for example
+
+: env GN2_PROFILE=~/opt/gn-latest GENENETWORK_FILES=/gnu/data/gn2_data ./bin/genenetwork2
+
+** Can't run a module
+
+In rare cases, development modules are not brought in with Guix
+because no source code is available. This can lead to missing modules
+on a running server. Please check with the authors when a module
+is missing.
+** Rpy2 error 'show' now found
+
+This error
+
+: __show = rpy2.rinterface.baseenv.get("show")
+: LookupError: 'show' not found
+
+means that R was updated in your path, and that Rpy2 needs to be
+recompiled against this R - don't you love informative messages?
+
+In our case it means that GN's PYTHONPATH is not in sync with
+R_LIBS_SITE. Please check your GNU Guix GN2 installation paths,
+you man need to reinstall. Note that this may be the point you
+may want to start using profiles (see profile section).
+
+** Mysql can't connect server through socket ERROR
+
+The following error
+
+: sqlalchemy.exc.OperationalError: (_mysql_exceptions.OperationalError) (2002, 'Can\'t connect to local MySQL server through socket \'/run/mysqld/mysqld.sock\' (2 "No such file or directory")')
+
+means that MySQL is trying to connect locally to a non-existent MySQL
+server, something you may see in a container. Typically replicated with something like
+
+: mysql -h localhost
+
+try to connect over the network interface instead, e.g.
+
+: mysql -h 127.0.0.1
+
+if that works run genenetwork after setting SQL_URI to something like
+
+: export SQL_URI=mysql://gn2:mysql_password@127.0.0.1/db_webqtl_s
+
+* NOTES
+
+** Deploying GN2 official
+
+Let's see how fast we can deploy a second copy of GN2.
+
+- [ ] Base install
+ + [ ] First install a Debian server with GNU Guix on board
+ + [ ] Get Guix build going
+ - [ ] Build the correct version of Guix
+ - [ ] Check out the correct gn-stable version of guix-bioinformatics http://git.genenetwork.org/pjotrp/guix-bioinformatics
+ - [ ] guix package -i genenetwork2 -p /usr/local/guix-profiles/gn2-stable
+ + [ ] Create a gn2 user and home with space
+ + [ ] Install redis
+ - [ ] add to systemd
+ - [ ] update redis.cnf
+ - [ ] update database
+ + [ ] Install mariadb (currently debian mariadb-server)
+ - [ ] add to systemd
+ - [ ] system stop mysql
+ - [ ] update mysql.cnf
+ - [ ] update database (see gn-services/services/mariadb.md)
+ - [ ] check tables
+ + [ ] run gn2
+ + [ ] update nginx
+ + [ ] install genenetwork3
+ - [ ] add to systemd
diff --git a/topics/deploy/machines.gmi b/topics/deploy/machines.gmi
new file mode 100644
index 0000000..49c3ee6
--- /dev/null
+++ b/topics/deploy/machines.gmi
@@ -0,0 +1,46 @@
+# Machines
+
+```
+- [X] lambda01 172.23.18.212 (7c:c2:55:11:9c:ac)
+- [ ] tux03i 172.23.17.181 (00:0a:f7:c1:00:8d) - 10 Gbs
+ [X] tux03 128.169.5.101 (00:0a:f7:c1:00:8b) - 1 Gbs
+- [X] tux06 172.23.17.188 (14:23:f2:4e:29:10)
+- [X] tux07 172.23.17.191 (14:23:f2:4e:7d:60)
+- [X] tux08 172.23.17.186 (14:23:f2:4f:4e:b0)
+- [X] tux09 172.23.17.182 (14:23:f2:4e:49:10)
+- [X] space 128.169.5.175 (e4:3d:1a:80:6c:40)
+- [ ] octopus01f 172.23.18.221 (2c:ea:7f:60:bf:61)
+- [ ] octopus02f 172.23.22.159 (2c:ea:7f:60:bd:61)
+- [ ] octopus03f 172.23.19.187 (2c:ea:7f:60:ac:2b)
+- [ ] octopus04f 172.23.17.254 (2c:ea:7f:60:af:14)
+```
+
+## Out-of-band access
+
+c for console or control
+
+```
+- [ ] DNS entries no longer visible
+- [X] lambda01-c 172.23.17.173 (3c:ec:ef:aa:e5:50)
+- [X] tux01-c 172.23.31.85 (58:8A:5A:F9:3A:22)
+- [X] tux02-c 172.23.30.40 (58:8A:5A:F0:E6:E4)
+- [X] tux03-c 172.23.31.87 (D0:94:66:37:39:D5)
+- [X] tux04-c 172.23.18.222 (C8:4B:D6:97:03:7D)
+- [X] tux05-c 172.23.22.218 (C8:4B:D6:97:05:C3)
+- [X] tux06-c 172.23.18.219 (C8:4B:D6:9B:8B:3A)
+- [X] tux07-c 172.23.21.85 (C8:4B:D6:9B:8B:8E)
+- [X] tux08-c 172.23.17.161 (C8:4B:D6:9B:8B:76)
+- [X] tux09-c 172.23.17.187 (C8:4B:D6:9B:8D:14)
+- [X] space-c 172.23.31.74 (CC:48:3A:13:DB:45)
+- [X] octopus01-c 172.23.31.39 (2C:EA:7F:F2:9E:10)
+- [X] octopus02-c 172.23.31.37 (2C:EA:7F:F2:D8:74)
+- [X] octopus03-c 172.23.31.94 (2C:EA:7F:F2:CC:34)
+- [X] octopus04-c 172.23.31.38 (2C:EA:7F:F2:CF:F4)
+- [X] octopus05-c 172.23.31.95 (2C:EA:7F:F2:D2:7C)
+- [X] octopus06-c 172.23.31.36 (2C:EA:7F:F2:CF:A4)
+- [X] octopus07-c 172.23.31.65 (2C:EA:7F:F2:D8:14)
+- [X] octopus08-c 172.23.31.66 (2C:EA:7F:F2:CA:D4)
+- [X] octopus09-c 172.23.31.71 (2C:EA:7F:F2:D3:74)
+- [X] octopus10-c 172.23.31.33 (2C:EA:7F:F2:D2:6C)
+- [X] octopus11-c 172.23.31.64 (2C:EA:7F:F2:CE:84)
+```
diff --git a/topics/deploy/our-virtuoso-instances.gmi b/topics/deploy/our-virtuoso-instances.gmi
new file mode 100644
index 0000000..0336018
--- /dev/null
+++ b/topics/deploy/our-virtuoso-instances.gmi
@@ -0,0 +1,41 @@
+# Our virtuoso instances
+
+We run three instances of virtuoso.
+
+* virtuoso on tux01 for internal use by the production server
+* virtuoso on tux02 in the development (aka CI/CD) container for internal use by the CD
+* virtuoso on tux02 as a public SPARQL endpoint
+
+The public SPARQL endpoint is accessible at
+=> https://sparql.genenetwork.org/sparql
+
+## Configuration
+
+All our virtuoso instances are deployed in Guix system containers. The configuration for these containers is at
+
+=> https://github.com/genenetwork/genenetwork-machines/blob/main/virtuoso.scm Configuration for internal production virtuoso on tux01
+=> https://github.com/genenetwork/genenetwork-machines/blob/main/genenetwork-development.scm Configuration for internal CD virtuoso on tux02
+=> https://github.com/genenetwork/genenetwork-machines/blob/main/public-sparql.scm Configuration for public SPARQL endpoint virtuoso on tux02
+
+## Ports
+
+The ports these virtuoso instances are deployed on is subject to change, and you should really look up the Guix configuration file to find the correct port. But, here is a summary for quick lookup. Please fix this if it is inaccurate.
+
+### Internal production virtuoso on tux01
+
+* Server port: 8891
+* SPARQL endpoint port: 8892
+
+### Internal CD virtuoso on tux02
+
+* Server port: 9081
+* SPARQL endpoint port: 9082
+
+### Public SPARQL endpoint virtuoso on tux02
+
+* Server port: 8981
+* SPARQL endpoint port: 8982
+
+## Passwords
+
+Password based authentication is required to mutate the data in these virtuoso instances. These passwords are available with Pjotr and Arun. Please contact them if you need access.
diff --git a/topics/deploy/useful-shell-scripts.gmi b/topics/deploy/useful-shell-scripts.gmi
new file mode 100644
index 0000000..908928e
--- /dev/null
+++ b/topics/deploy/useful-shell-scripts.gmi
@@ -0,0 +1,52 @@
+# Useful Shell Scripts
+
+I make it a habit to put all my user-scripts in `~/bin/' and add that to my `$PATH'. Another useful tip is to prepend all my scripts using a "," - this allows me to quickly take advantage of zsh's autocomplete. For the curious, you could also adopt quiuy as part of the scripts - it has the advantage of adding more semantic meaning to your scripts.
+
+Most of these scripts were borrowed from:
+
+=> https://git.sr.ht/~whereiseveryone/dot/tree/master/item/bin
+
+Here are the scripts that make me more efficient on the terminal:
+
+* Run a script in the context of a directory: ",run-with-dir"
+
+```
+#!/bin/sh
+set -eo pipefail
+
+# Run a command in specific directory
+run_within_dir() {
+ target_dir="$1"
+ previous_dir=$(pwd)
+ shift
+ cd $target_dir && "$@"
+ cd $previous_dir
+}
+
+run_within_dir $@
+```
+
+If you are in `$HOME', you can do something like: "run-within-dir /tmp"
+
+* Choose a guix profile on the fly: ",choose-profile":
+
+```
+#!/bin/env sh
+
+# To run this use source!
+
+GUIX_PROFILE="$(guix package --list-profiles | fzf --multi)"
+
+export GUIX_PROFILE
+. "$GUIX_PROFILE/etc/profile"
+```
+
+* Run magit from any terminal: ",magit". You can take of fzf's autocomplete
+
+```
+#!/bin/env sh
+
+emacsclient --eval "(projectile-vc \"$PWD/$@\")"
+```
+
+Should these scripts become too many, a repository will be created and a link added to point there.
diff --git a/topics/deploy/uthsc-vpn-with-free-software.gmi b/topics/deploy/uthsc-vpn-with-free-software.gmi
new file mode 100644
index 0000000..651fb83
--- /dev/null
+++ b/topics/deploy/uthsc-vpn-with-free-software.gmi
@@ -0,0 +1,60 @@
+# UTHSC VPN with free software
+
+It is possible to connect to the UTHSC VPN using only free software. For this, you need the openconnect-sso package. openconnect-sso is a wrapper around openconnect that handles the web-based single sign-on and runs openconnect with the right arguments.
+=> https://github.com/vlaci/openconnect-sso/ openconnect-sso
+=> https://www.infradead.org/openconnect/ openconnect
+
+To connect, run openconnect-sso as follows. A browser window will pop up for you to complete the Duo authentication. Once done, you will be connected to the VPN.
+```
+$ openconnect-sso --server uthscvpn1.uthsc.edu --authgroup UTHSC
+```
+Note that openconnect-sso should be run as a regular user, not as root. After passing Duo authentication, openconnect-sso will try to gain root priviliges to set up the network routes. At that point, it will prompt you for your password using sudo.
+
+## Avoid tunneling all your network traffic through the VPN (aka Split Tunneling)
+
+openconnect, by default, tunnels all your traffic through the VPN. This is not good for your privacy. It is better to tunnel only the traffic destined to the specific hosts that you want to access. This can be done using the vpn-slice script.
+=> https://github.com/dlenski/vpn-slice/ vpn-slice
+
+For example, to connect to the UTHSC VPN but only access the hosts tux01 and tux02e through the VPN, run the following command.
+```
+$ openconnect-sso --server uthscvpn1.uthsc.edu --authgroup UTHSC -- --script 'vpn-slice tux01 tux02e'
+```
+The vpn-slice script looks up the hostnames tux01 and tux02e on the VPN DNS and adds /etc/hosts entries and routes to your system. vpn-slice can also set up more complicated routes. To learn more, read the vpn-slice documentation.
+
+## Unsafe legacy TLS renegotiation
+
+The UTHSC VPN still requires unsafe legacy TLS renegotiation. This is disabled by default on the latest Guix. We need to re-enable it by configuring openssl.cnf as described on the following stackoverflow page.
+=> https://stackoverflow.com/questions/71603314/ssl-error-unsafe-legacy-renegotiation-disabled
+Here's a quick summary. Put the following in some file, say /tmp/openssl.cnf
+```
+openssl_conf = openssl_init
+
+[openssl_init]
+ssl_conf = ssl_sect
+
+[ssl_sect]
+system_default = system_default_sect
+
+[system_default_sect]
+Options = UnsafeLegacyRenegotiation
+```
+Set the environment variable OPENSSL_CONF to point to this file.
+```
+export OPENSSL_CONF=/tmp/openssl.cnf
+```
+Then, run the openconnect-sso client as usual.
+
+## Putting it all together using Guix G-expressions
+
+Remembering to do all these steps is a hassle. Writing a shell script to automate this is a good idea, but why write shell scripts when we have G-expressions! Here's a G-expression script that I prepared earlier.
+=> uthsc-vpn.scm
+Download it, tweak the %hosts variable to specify the hosts you are interested in, and run it like so:
+```
+$(guix build -f uthsc-vpn.scm)
+```
+
+## Acknowledgement
+
+Many thanks to Pjotr Prins and Erik Garrison without whose earlier work this guide would not be possible.
+=> https://github.com/pjotrp/linux-at-university-of-tennessee
+=> https://github.com/ekg/openconnect-sso-docker
diff --git a/topics/deploy/uthsc-vpn.scm b/topics/deploy/uthsc-vpn.scm
new file mode 100644
index 0000000..c714731
--- /dev/null
+++ b/topics/deploy/uthsc-vpn.scm
@@ -0,0 +1,44 @@
+(use-modules ((gnu packages guile-xyz) #:select (guile-ini guile-lib guile-smc))
+ ((gnu packages vpn) #:select (openconnect-sso vpn-slice))
+ (guix gexp))
+
+;; Put in the hosts you are interested in here.
+(define %hosts
+ (list "octopus01"
+ "tux01.genenetwork.org"))
+
+(define (ini-file name scm)
+ "Return a file-like object representing INI file with @var{name} and
+@var{scm} data."
+ (computed-file name
+ (with-extensions (list guile-ini guile-lib guile-smc)
+ #~(begin
+ (use-modules (srfi srfi-26)
+ (ini))
+
+ (call-with-output-file #$output
+ (cut scm->ini #$scm #:port <>))))))
+
+(define uthsc-vpn
+ (with-imported-modules '((guix build utils))
+ #~(begin
+ (use-modules (guix build utils))
+
+ (setenv "OPENSSL_CONF"
+ #$(ini-file "openssl.cnf"
+ #~'((#f
+ ("openssl_conf" . "openssl_init"))
+ ("openssl_init"
+ ("ssl_conf" . "ssl_sect"))
+ ("ssl_sect"
+ ("system_default" . "system_default_sect"))
+ ("system_default_sect"
+ ("Options" . "UnsafeLegacyRenegotiation")))))
+ (invoke #$(file-append openconnect-sso "/bin/openconnect-sso")
+ "--server" "uthscvpn1.uthsc.edu"
+ "--authgroup" "UTHSC"
+ "--"
+ "--script" (string-join (cons #$(file-append vpn-slice "/bin/vpn-slice")
+ '#$%hosts))))))
+
+(program-file "uthsc-vpn" uthsc-vpn)