diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2c4c7ec --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +##Ignore site configuration +.gitmodules +site-config/* + +## Ignore site configuration +*/docker-compose.override.yml diff --git a/LICENSE b/LICENSE index 261eeb9..29f81d8 100644 --- a/LICENSE +++ b/LICENSE @@ -1,201 +1,201 @@ - Apache License - Version 2.0, January 2004 - http://www.apache.org/licenses/ - - TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION - - 1. Definitions. - - "License" shall mean the terms and conditions for use, reproduction, - and distribution as defined by Sections 1 through 9 of this document. - - "Licensor" shall mean the copyright owner or entity authorized by - the copyright owner that is granting the License. - - "Legal Entity" shall mean the union of the acting entity and all - other entities that control, are controlled by, or are under common - control with that entity. For the purposes of this definition, - "control" means (i) the power, direct or indirect, to cause the - direction or management of such entity, whether by contract or - otherwise, or (ii) ownership of fifty percent (50%) or more of the - outstanding shares, or (iii) beneficial ownership of such entity. - - "You" (or "Your") shall mean an individual or Legal Entity - exercising permissions granted by this License. - - "Source" form shall mean the preferred form for making modifications, - including but not limited to software source code, documentation - source, and configuration files. - - "Object" form shall mean any form resulting from mechanical - transformation or translation of a Source form, including but - not limited to compiled object code, generated documentation, - and conversions to other media types. - - "Work" shall mean the work of authorship, whether in Source or - Object form, made available under the License, as indicated by a - copyright notice that is included in or attached to the work - (an example is provided in the Appendix below). - - "Derivative Works" shall mean any work, whether in Source or Object - form, that is based on (or derived from) the Work and for which the - editorial revisions, annotations, elaborations, or other modifications - represent, as a whole, an original work of authorship. For the purposes - of this License, Derivative Works shall not include works that remain - separable from, or merely link (or bind by name) to the interfaces of, - the Work and Derivative Works thereof. - - "Contribution" shall mean any work of authorship, including - the original version of the Work and any modifications or additions - to that Work or Derivative Works thereof, that is intentionally - submitted to Licensor for inclusion in the Work by the copyright owner - or by an individual or Legal Entity authorized to submit on behalf of - the copyright owner. For the purposes of this definition, "submitted" - means any form of electronic, verbal, or written communication sent - to the Licensor or its representatives, including but not limited to - communication on electronic mailing lists, source code control systems, - and issue tracking systems that are managed by, or on behalf of, the - Licensor for the purpose of discussing and improving the Work, but - excluding communication that is conspicuously marked or otherwise - designated in writing by the copyright owner as "Not a Contribution." - - "Contributor" shall mean Licensor and any individual or Legal Entity - on behalf of whom a Contribution has been received by Licensor and - subsequently incorporated within the Work. - - 2. Grant of Copyright License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - copyright license to reproduce, prepare Derivative Works of, - publicly display, publicly perform, sublicense, and distribute the - Work and such Derivative Works in Source or Object form. - - 3. Grant of Patent License. Subject to the terms and conditions of - this License, each Contributor hereby grants to You a perpetual, - worldwide, non-exclusive, no-charge, royalty-free, irrevocable - (except as stated in this section) patent license to make, have made, - use, offer to sell, sell, import, and otherwise transfer the Work, - where such license applies only to those patent claims licensable - by such Contributor that are necessarily infringed by their - Contribution(s) alone or by combination of their Contribution(s) - with the Work to which such Contribution(s) was submitted. If You - institute patent litigation against any entity (including a - cross-claim or counterclaim in a lawsuit) alleging that the Work - or a Contribution incorporated within the Work constitutes direct - or contributory patent infringement, then any patent licenses - granted to You under this License for that Work shall terminate - as of the date such litigation is filed. - - 4. Redistribution. You may reproduce and distribute copies of the - Work or Derivative Works thereof in any medium, with or without - modifications, and in Source or Object form, provided that You - meet the following conditions: - - (a) You must give any other recipients of the Work or - Derivative Works a copy of this License; and - - (b) You must cause any modified files to carry prominent notices - stating that You changed the files; and - - (c) You must retain, in the Source form of any Derivative Works - that You distribute, all copyright, patent, trademark, and - attribution notices from the Source form of the Work, - excluding those notices that do not pertain to any part of - the Derivative Works; and - - (d) If the Work includes a "NOTICE" text file as part of its - distribution, then any Derivative Works that You distribute must - include a readable copy of the attribution notices contained - within such NOTICE file, excluding those notices that do not - pertain to any part of the Derivative Works, in at least one - of the following places: within a NOTICE text file distributed - as part of the Derivative Works; within the Source form or - documentation, if provided along with the Derivative Works; or, - within a display generated by the Derivative Works, if and - wherever such third-party notices normally appear. The contents - of the NOTICE file are for informational purposes only and - do not modify the License. You may add Your own attribution - notices within Derivative Works that You distribute, alongside - or as an addendum to the NOTICE text from the Work, provided - that such additional attribution notices cannot be construed - as modifying the License. - - You may add Your own copyright statement to Your modifications and - may provide additional or different license terms and conditions - for use, reproduction, or distribution of Your modifications, or - for any such Derivative Works as a whole, provided Your use, - reproduction, and distribution of the Work otherwise complies with - the conditions stated in this License. - - 5. Submission of Contributions. Unless You explicitly state otherwise, - any Contribution intentionally submitted for inclusion in the Work - by You to the Licensor shall be under the terms and conditions of - this License, without any additional terms or conditions. - Notwithstanding the above, nothing herein shall supersede or modify - the terms of any separate license agreement you may have executed - with Licensor regarding such Contributions. - - 6. Trademarks. This License does not grant permission to use the trade - names, trademarks, service marks, or product names of the Licensor, - except as required for reasonable and customary use in describing the - origin of the Work and reproducing the content of the NOTICE file. - - 7. Disclaimer of Warranty. Unless required by applicable law or - agreed to in writing, Licensor provides the Work (and each - Contributor provides its Contributions) on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or - implied, including, without limitation, any warranties or conditions - of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A - PARTICULAR PURPOSE. You are solely responsible for determining the - appropriateness of using or redistributing the Work and assume any - risks associated with Your exercise of permissions under this License. - - 8. Limitation of Liability. In no event and under no legal theory, - whether in tort (including negligence), contract, or otherwise, - unless required by applicable law (such as deliberate and grossly - negligent acts) or agreed to in writing, shall any Contributor be - liable to You for damages, including any direct, indirect, special, - incidental, or consequential damages of any character arising as a - result of this License or out of the use or inability to use the - Work (including but not limited to damages for loss of goodwill, - work stoppage, computer failure or malfunction, or any and all - other commercial damages or losses), even if such Contributor - has been advised of the possibility of such damages. - - 9. Accepting Warranty or Additional Liability. While redistributing - the Work or Derivative Works thereof, You may choose to offer, - and charge a fee for, acceptance of support, warranty, indemnity, - or other liability obligations and/or rights consistent with this - License. However, in accepting such obligations, You may act only - on Your own behalf and on Your sole responsibility, not on behalf - of any other Contributor, and only if You agree to indemnify, - defend, and hold each Contributor harmless for any liability - incurred by, or claims asserted against, such Contributor by reason - of your accepting any such warranty or additional liability. - - END OF TERMS AND CONDITIONS - - APPENDIX: How to apply the Apache License to your work. - - To apply the Apache License to your work, attach the following - boilerplate notice, with the fields enclosed by brackets "[]" - replaced with your own identifying information. (Don't include - the brackets!) The text should be enclosed in the appropriate - comment syntax for the file format. We also recommend that a - file or class name and description of purpose be included on the - same "printed page" as the copyright notice for easier - identification within third-party archives. - - Copyright [yyyy] [name of copyright owner] - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/README.md b/README.md index 4967df5..b57c10e 100644 --- a/README.md +++ b/README.md @@ -1 +1,182 @@ -# bridgehead \ No newline at end of file +# Bridgehead + +The Bridgehead is a secure, low-effort solution to connect your research institution to a federated research network. It bundles interoperable, open-source software components into a turnkey package for installation on one of your secure servers. The Bridgehead is pre-configured with sane defaults, centrally monitored and with an absolute minimum of "moving parts" on your side, making it an extremely low-maintenance gateway to data sharing. + +This repository is the starting point for any information and tools you will need to deploy a Bridgehead. If you have questions, please [contact us](mailto:verbis-support@dkfz-heidelberg.de). + +1. [Requirements](#requirements) + - [Hardware](#hardware) + - [System](#system) + - [Git](#git) + - [Docker](#docker) +2. [Deployment](#deployment) + - [Installation](#installation) + - [Register with Samply.Beam](#register-with-samplybeam) + - [Starting and stopping your Bridgehead](#starting-and-stopping-your-bridgehead) + - [Auto-starting your Bridgehead when the server starts](#auto-starting-your-bridgehead-when-the-server-starts) +3. [Additional Services](#additional-Services) + - [Monitoring](#monitoring) + - [Register with a Directory](#register-with-a-Directory) +4. [Site-specific configuration](#site-specific-configuration) + - [HTTPS Access](#https-access) + - [Locally Managed Secrets](#locally-managed-secrets) + - [Git Proxy Configuration](#git-proxy-configuration) + - [Docker Daemon Proxy Configuration](#docker-daemon-proxy-configuration) + - [Non-Linux OS](#non-linux-os) +5. [License](#license) + +## Requirements + +### Hardware + +Hardware requirements strongly depend on the specific use-cases of your network as well as on the data it is going to serve. Most use-cases are well-served with the following configuration: + +- 4 CPU cores +- 32 GB RAM +- 160GB Hard Drive, SSD recommended + +### Software + +You are strongly recommended to install the Bridgehead under a Linux operating system (but see the section [Non-Linux OS](#non-linux-os)). You will need root (administrator) priveleges on this machine in order to perform the deployment. We recommend the newest Ubuntu LTS server release. + +Ensure the following software (or newer) is installed: + +- git >= 2.0 +- docker >= 20.10.1 +- docker-compose >= 2.xx (`docker-compose` and `docker compose` are both supported). +- systemd + +We recommend to install Docker(-compose) from its official sources as described on the [Docker website](https://docs.docker.com). + +Note for Ubuntu: Please note that snap versions of Docker are not supported. + +### Network + +Since it needs to carry sensitive patient data, Bridgeheads are intended to be deployed within your institution's secure network and behave well even in networks in strict security settings, e.g. firewall rules. The only connectivity required is an outgoing HTTPS proxy. TLS termination is supported, too (see [below](#tls-terminating-proxies)) + +Note for Ubuntu: Please note that the uncomplicated firewall (ufw) is known to conflict with Docker [here](https://github.com/chaifeng/ufw-docker). + +## Deployment + +### Base Installation + +First, clone the repository to the directory `/srv/docker/bridgehead`: + +```shell +sudo mkdir -p /srv/docker/ +sudo git clone https://github.com/samply/bridgehead.git /srv/docker/bridgehead +``` + +Then, run the installation script: + +```shell +cd /srv/docker/bridgehead +sudo ./bridgehead install +``` + +... and follow the instructions on the screen. You should then be prompted to do the next step: + +### Register with Samply.Beam + +Many Bridgehead services rely on the secure, performant and flexible messaging middleware called [Samply.Beam](https://github.com/samply/beam). You will need to register ("enroll") with Samply.Beam by creating a cryptographic key pair for your bridgehead: + +``` shell +cd /srv/docker/bridgehead +sudo ./bridgehead enroll +``` + +... and follow the instructions on the screen. You should then be prompted to do the next step: + +### Starting and stopping your Bridgehead + +If you followed the above steps, your Bridgehead should already be configured to autostart (via systemd). If you would like to start/stop manually: + +To start, run + +```shell +sudo systemctl start bridgehead@.service +``` + +To stop, run + +```shell +sudo systemctl stop bridgehead@.service +``` + +To enable/disable autostart, run + +```shell +sudo systemctl [enable|disable] bridgehead@.service +``` + +## Site-specific configuration + +### HTTPS Access + +Even within your internal network, the Bridgehead enforces HTTPS for all services. During the installation, a self-signed, long-lived certificate was created for you. To increase security, you can simply replace the files under `/etc/bridgehead/traefik-tls` with ones from established certification authorities such as [Let's Encrypt](https://letsencrypt.org) or [DFN-AAI](https://www.aai.dfn.de). + +### TLS terminating proxies + +All of the Bridgehead's outgoing connections are secured by transport encryption (TLS) and a Bridgehead will refuse to connect if certificate verification fails. If your local forward proxy server performs TLS termination, please place its CA certificate in `/etc/bridgehead/trusted-ca-certs` as a `.pem` file, e.g. `/etc/bridgehead/trusted-ca-certs/mylocalca.pem`. Then, all Bridgehead components will pick up this certificate and trust it for outgoing connections. + +### File structure + +- `/srv/docker/bridgehead` contains this git repository with the shell scripts and *project-specific configuration*. In here, all files are identical for all sites. You should not make any changes here. +- `/etc/bridgehead` contains your *site-specific configuration* synchronized from your site-specific git repository as part of the [base installation](#base-installation). To change anything here, please consult your git repository (find out its URL via `git -C /etc/bridgehead remote -v`). + - `/etc/bridgehead/.conf` is your main site-specific configuration, all bundled into one concise config file. Do not change it here but via the central git repository. + - `/etc/bridgehead/.local.conf` contains site-specific parameters to be known to your Bridgehead only, e.g. local access credentials. The file is ignored via git, and you may edit it here via a text editor. + - `/etc/bridgehead/traefik-tls` contains your Bridgehead's reverse proxies TLS certificates for [HTTPS access](#https-access). + - `/etc/bridgehead/pki` contains your Bridgehead's private key (e.g., but not limited to Samply.Beam), generated as part of the [Samply.Beam enrollment](#register-with-samplybeam). + - `/etc/bridgehead/trusted-ca-certs` contains third-party certificates to be trusted by the Bridgehead. For example, you want to place the certificates of your [TLS-terminating proxy](#network) here. + +Your Bridgehead's actual data is not stored in the above directories, but in named docker volumes, see `docker volume ls` and `docker volume inspect `. + +## Things you should know + +### Auto-Updates + +Your Bridgehead will automatically and regularly check for updates. Whenever something has been updates (e.g., one of the git repositories or one of the docker images), your Bridgehead is automatically restarted. This should happen automatically and does not need any configuration. + +If you would like to understand what happens exactly and when, please check the systemd units deployed during the [installation](#base-installation) via `systemctl cat bridgehead-update@.service` and `systemctl cat bridgehead-update@/dev/null 2>&1 && pwd ) + SOURCE=$(readlink "$SOURCE") + [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located + done + DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) + echo $DIR +} + +BASE=$(baseDir) +cd $BASE + +source ./lib/functions.sh + +ACTION=$1 +export PROJECT=$2 + +if [[ -z $1 || -z $2 ]]; then + printUsage + exit 1 +fi + +case "$PROJECT" in + ccp) + #nothing extra to do + ;; + bbmri) + #nothing extra to do + ;; + *) + printUsage + exit 1 + ;; +esac + +loadVars() { + # Load variables from /etc/bridgehead and /srv/docker/bridgehead + set -a + source /etc/bridgehead/$PROJECT.conf || fail_and_report 1 "/etc/bridgehead/$PROJECT.conf not found" + if [ -e /etc/bridgehead/$PROJECT.local.conf ]; then + log INFO "Applying /etc/bridgehead/$PROJECT.local.conf" + source /etc/bridgehead/$PROJECT.local.conf || fail_and_report 1 "Found /etc/bridgehead/$PROJECT.local.conf but failed to import" + fi + fetchVarsFromVaultByFile /etc/bridgehead/$PROJECT.conf || fail_and_report 1 "Unable to fetchVarsFromVaultByFile" + [ -e ./$PROJECT/vars ] && source ./$PROJECT/vars + set +a + + OVERRIDE=${OVERRIDE:=""} + if [ -f "$PROJECT/docker-compose.override.yml" ]; then + log INFO "Applying $PROJECT/docker-compose.override.yml" + OVERRIDE+=" -f ./$PROJECT/docker-compose.override.yml" + fi + detectCompose + setHostname +} + +case "$ACTION" in + start) + loadVars + hc_send log "Bridgehead $PROJECT startup: Checking requirements ..." + checkRequirements + hc_send log "Bridgehead $PROJECT startup: Requirements checked out. Now starting bridgehead ..." + export LDM_LOGIN=$(getLdmPassword) + exec $COMPOSE -f ./$PROJECT/docker-compose.yml $OVERRIDE up --abort-on-container-exit + ;; + stop) + loadVars + exec $COMPOSE -f ./$PROJECT/docker-compose.yml $OVERRIDE down + ;; + update) + loadVars + exec ./lib/update-bridgehead.sh $PROJECT + ;; + install) + source ./lib/prepare-system.sh + loadVars + exec ./lib/install-bridgehead.sh $PROJECT + ;; + uninstall) + exec ./lib/uninstall-bridgehead.sh $PROJECT + ;; + enroll) + loadVars + if [ -e $PRIVATEKEYFILENAME ]; then + log ERROR "Private key already exists at $PRIVATEKEYFILENAME. Please delete first to proceed." + exit 1 + fi + docker run --rm -ti -v /etc/bridgehead/pki:/etc/bridgehead/pki samply/beam-enroll:latest --output-file $PRIVATEKEYFILENAME --proxy-id $PROXY_ID --admin-email $SUPPORT_EMAIL + chmod 600 $PRIVATEKEYFILENAME + ;; + preRun | preUpdate) + fixPermissions + ;; + postRun | postUpdate) + ;; + *) + printUsage + exit 1 + ;; +esac + +exit 0 diff --git a/ccp/docker-compose.yml b/ccp/docker-compose.yml new file mode 100644 index 0000000..989cc84 --- /dev/null +++ b/ccp/docker-compose.yml @@ -0,0 +1,114 @@ +version: "3.7" + +services: + traefik: + container_name: bridgehead-traefik + image: traefik:latest + command: + - --entrypoints.web.address=:80 + - --entrypoints.websecure.address=:443 + - --providers.docker=true + - --providers.docker.exposedbydefault=false + - --providers.file.directory=/configuration/ + - --api.dashboard=true + - --accesslog=true + - --entrypoints.web.http.redirections.entrypoint.to=websecure + - --entrypoints.web.http.redirections.entrypoint.scheme=https + labels: + - "traefik.enable=true" + - "traefik.http.routers.dashboard.rule=PathPrefix(`/api`) || PathPrefix(`/dashboard`)" + - "traefik.http.routers.dashboard.entrypoints=websecure" + - "traefik.http.routers.dashboard.service=api@internal" + - "traefik.http.routers.dashboard.tls=true" + - "traefik.http.routers.dashboard.middlewares=auth" + - "traefik.http.middlewares.auth.basicauth.users=${LDM_LOGIN}" + ports: + - 80:80 + - 443:443 + volumes: + - /etc/bridgehead/traefik-tls:/certs:ro + - ../lib/traefik-configuration/:/configuration:ro + - /var/run/docker.sock:/var/run/docker.sock:ro + + forward_proxy: + container_name: bridgehead-forward-proxy + image: samply/bridgehead-forward-proxy:latest + environment: + HTTPS_PROXY: ${HTTPS_PROXY_URL} + USERNAME: ${HTTPS_PROXY_USERNAME} + PASSWORD: ${HTTPS_PROXY_PASSWORD} + volumes: + - /etc/bridgehead/trusted-ca-certs:/docker/custom-certs/:ro + + landing: + container_name: bridgehead-landingpage + image: samply/bridgehead-landingpage:master + labels: + - "traefik.enable=true" + - "traefik.http.routers.landing.rule=PathPrefix(`/`)" + - "traefik.http.services.landing.loadbalancer.server.port=80" + - "traefik.http.routers.landing.tls=true" + environment: + HOST: ${HOST} + PROJECT: ${PROJECT} + SITE_NAME: ${SITE_NAME} + + blaze: + image: "samply/blaze:0.18" + container_name: bridgehead-ccp-blaze + environment: + BASE_URL: "http://bridgehead-ccp-blaze:8080" + JAVA_TOOL_OPTIONS: "-Xmx4g" + LOG_LEVEL: "debug" + ENFORCE_REFERENTIAL_INTEGRITY: "false" + volumes: + - "blaze-data:/app/data" + labels: + - "traefik.enable=true" + - "traefik.http.routers.blaze_ccp.rule=PathPrefix(`/ccp-localdatamanagement`)" + - "traefik.http.middlewares.ccp_b_strip.stripprefix.prefixes=/ccp-localdatamanagement" + - "traefik.http.services.blaze_ccp.loadbalancer.server.port=8080" + - "traefik.http.routers.blaze_ccp.middlewares=ccp_b_strip,auth" + - "traefik.http.routers.blaze_ccp.tls=true" + + spot: + image: samply/spot:latest + container_name: bridgehead-spot + environment: + SECRET: ${SPOT_BEAM_SECRET_LONG} + APPID: spot + PROXY_ID: ${PROXY_ID} + LDM_URL: http://bridgehead-ccp-blaze:8080/fhir + BEAM_PROXY: http://beam-proxy:8081 + depends_on: + - "beam-proxy" + - "blaze" + + beam-proxy: + image: "samply/beam-proxy:develop" + container_name: bridgehead-beam-proxy + environment: + BROKER_URL: ${BROKER_URL} + PROXY_ID: ${PROXY_ID} + APP_0_ID: spot + APP_0_KEY: ${SPOT_BEAM_SECRET_SHORT} + APP_1_ID: report-hub + APP_1_KEY: ${REPORTHUB_BEAM_SECRET_SHORT} + PRIVKEY_FILE: /run/secrets/proxy.pem + RUST_LOG: debug + ALL_PROXY: http://forward_proxy:3128 + TLS_CA_CERTIFICATES_DIR: /conf/trusted-ca-certs + secrets: + - proxy.pem + depends_on: + - "forward_proxy" + volumes: + - /etc/bridgehead/trusted-ca-certs:/conf/trusted-ca-certs:ro + + +volumes: + blaze-data: + +secrets: + proxy.pem: + file: /etc/bridgehead/pki/${SITE_ID}.priv.pem diff --git a/ccp/nngm-compose.yml b/ccp/nngm-compose.yml new file mode 100644 index 0000000..c212fed --- /dev/null +++ b/ccp/nngm-compose.yml @@ -0,0 +1,32 @@ +version: "3.7" + +services: + connector: + container_name: bridgehead-connector + image: docker.verbis.dkfz.de/ccp/connector:bk2 + environment: + POSTGRES_PASSWORD: ${CONNECTOR_POSTGRES_PASSWORD} + NNGM_MAGICPL_APIKEY: ${NNGM_MAGICPL_APIKEY} + NNGM_MAINZELLISTE_APIKEY: ${NNGM_MAINZELLISTE_APIKEY} + NNGM_CTS_APIKEY: ${NNGM_CTS_APIKEY} + NNGM_CRYPTKEY: ${NNGM_CRYPTKEY} + restart: always + labels: + - "traefik.enable=true" + - "traefik.http.routers.connector.rule=PathPrefix(`/ccp-connector`)" + - "traefik.http.services.connector.loadbalancer.server.port=8080" + - "traefik.http.routers.connector.tls=true" + + connector_db: + image: postgres:9.5-alpine + container_name: bridgehead-ccp-connector-db + volumes: + - "connector_db_data:/var/lib/postgresql/data" + environment: + POSTGRES_DB: "samplyconnector" + POSTGRES_USER: "samplyconnector" + POSTGRES_PASSWORD: ${CONNECTOR_POSTGRES_PASSWORD} + restart: always + +volumes: + connector_db_data: diff --git a/ccp/nngm-setup.sh b/ccp/nngm-setup.sh new file mode 100644 index 0000000..d5b80eb --- /dev/null +++ b/ccp/nngm-setup.sh @@ -0,0 +1,9 @@ +#!/bin/bash + +function nngmSetup() { + if [ -n "$NNGM_CTS_APIKEY" ]; then + log INFO "nNGM setup detected -- will start nNGM Connector." + OVERRIDE+=" -f ./$PROJECT/nngm-compose.yml" + fi + CONNECTOR_POSTGRES_PASSWORD="$(echo \"This is a salt string to generate one consistent password. It is not required to be secret.\" | openssl rsautl -sign -inkey /etc/bridgehead/pki/${SITE_ID}.priv.pem | base64 | head -c 30)" +} diff --git a/ccp/vars b/ccp/vars new file mode 100644 index 0000000..f5f734e --- /dev/null +++ b/ccp/vars @@ -0,0 +1,13 @@ +BROKER_ID=broker.dev.ccp-it.dktk.dkfz.de +BROKER_URL=https://${BROKER_ID} +PROXY_ID=${SITE_ID}.${BROKER_ID} +SPOT_BEAM_SECRET_SHORT="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 20)" +SPOT_BEAM_SECRET_LONG="ApiKey spot.${PROXY_ID} ${SPOT_BEAM_SECRET_SHORT}" +REPORTHUB_BEAM_SECRET_SHORT="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 20)" +REPORTHUB_BEAM_SECRET_LONG="ApiKey report-hub.${PROXY_ID} ${REPORTHUB_BEAM_SECRET_SHORT}" +SUPPORT_EMAIL=support-ccp@dkfz-heidelberg.de +PRIVATEKEYFILENAME=/etc/bridgehead/pki/${SITE_ID}.priv.pem + +# This will load nngm setup. Effective only if nngm configuration is defined. +source $PROJECT/nngm-setup.sh +nngmSetup diff --git a/lib/functions.sh b/lib/functions.sh new file mode 100644 index 0000000..a539e0d --- /dev/null +++ b/lib/functions.sh @@ -0,0 +1,141 @@ +#!/bin/bash -e + +detectCompose() { + if [[ "$(docker compose version 2>/dev/null)" == *"Docker Compose version"* ]]; then + COMPOSE="docker compose" + else + COMPOSE="docker-compose" + # This is intended to fail on startup in the next prereq check. + fi +} + +getLdmPassword() { + if [ -n "$LDM_PASSWORD" ]; then + docker run --rm httpd:alpine htpasswd -nb $PROJECT $LDM_PASSWORD | tr -d '\n' | tr -d '\r' + else + echo -n "" + fi +} + +exitIfNotRoot() { + if [ "$EUID" -ne 0 ]; then + log "ERROR" "Please run as root" + fail_and_report 1 "Please run as root" + fi +} + +checkOwner(){ + COUNT=$(find $1 ! -user $2 |wc -l) + if [ $COUNT -gt 0 ]; then + log ERROR "$COUNT files in $1 are not owned by user $2. Run find $1 ! -user $2 to see them, chown -R $2 $1 to correct this issue." + return 1 + fi + return 0 +} + +printUsage() { + echo "Usage: bridgehead start|stop|update|install|uninstall|enroll PROJECTNAME" + echo "PROJECTNAME should be one of ccp|bbmri" +} + +checkRequirements() { + if ! lib/prerequisites.sh $@; then + log "ERROR" "Validating Prerequisites failed, please fix the error(s) above this line." + fail_and_report 1 "Validating prerequisites failed." + else + return 0 + fi +} + +fetchVarsFromVault() { + [ -e /etc/bridgehead/vault.conf ] && source /etc/bridgehead/vault.conf + + if [ -z "$BW_MASTERPASS" ] || [ -z "$BW_CLIENTID" ] || [ -z "$BW_CLIENTSECRET" ]; then + log "ERROR" "Please supply correct credentials in /etc/bridgehead/vault.conf." + return 1 + fi + + set +e + + PASS=$(BW_MASTERPASS="$BW_MASTERPASS" BW_CLIENTID="$BW_CLIENTID" BW_CLIENTSECRET="$BW_CLIENTSECRET" docker run --rm -e BW_MASTERPASS -e BW_CLIENTID -e BW_CLIENTSECRET -e http_proxy samply/bridgehead-vaultfetcher $@) + RET=$? + + if [ $RET -ne 0 ]; then + echo "Code: $RET" + echo $PASS + return $RET + fi + + eval $(echo -e "$PASS" | sed 's/\r//g') + + set -e + + return 0 +} + +fetchVarsFromVaultByFile() { + VARS_TO_FETCH="" + + for line in $(cat $@); do + if [[ $line =~ .*=[\"]*\[\"]*.* ]]; then + VARS_TO_FETCH+="$(echo -n $line | sed 's/=.*//') " + fi + done + + if [ -z "$VARS_TO_FETCH" ]; then + return 0 + fi + + log INFO "Fetching $(echo $VARS_TO_FETCH | wc -w) secrets from Vault ..." + + fetchVarsFromVault $VARS_TO_FETCH + + return 0 +} + +assertVarsNotEmpty() { + MISSING_VARS="" + + for VAR in $@; do + if [ -z "${!VAR}" ]; then + MISSING_VARS+="$VAR " + fi + done + + if [ -n "$MISSING_VARS" ]; then + log "ERROR" "Mandatory variables not defined: $MISSING_VARS" + return 1 + fi + + return 0 +} + +fixPermissions() { + CHOWN=$(which chown) + sudo $CHOWN -R bridgehead /etc/bridgehead /srv/docker/bridgehead +} + +source lib/monitoring.sh + +report_error() { + CODE=$1 + shift + log ERROR "$@" + hc_send $CODE "$@" +} + +fail_and_report() { + report_error $@ + exit $1 +} + +setHostname() { + if [ -z "$HOST" ]; then + export HOST=$(hostname -f) + log DEBUG "Using auto-detected hostname $HOST." + fi +} + +##Setting Network properties +# currently not needed +#export HOSTIP=$(MSYS_NO_PATHCONV=1 docker run --rm --add-host=host.docker.internal:host-gateway ubuntu cat /etc/hosts | grep 'host.docker.internal' | awk '{print $1}'); diff --git a/lib/gitpassword.sh b/lib/gitpassword.sh new file mode 100755 index 0000000..17756d6 --- /dev/null +++ b/lib/gitpassword.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +if [ "$1" != "get" ]; then + echo "Usage: $0 get" + exit 1 +fi + +baseDir() { + # see https://stackoverflow.com/questions/59895 + SOURCE=${BASH_SOURCE[0]} + while [ -h "$SOURCE" ]; do # resolve $SOURCE until the file is no longer a symlink + DIR=$( cd -P "$( dirname "$SOURCE" )" >/dev/null 2>&1 && pwd ) + SOURCE=$(readlink "$SOURCE") + [[ $SOURCE != /* ]] && SOURCE=$DIR/$SOURCE # if $SOURCE was a relative symlink, we need to resolve it relative to the path where the symlink file was located + done + DIR=$( cd -P "$( dirname "$SOURCE" )/.." >/dev/null 2>&1 && pwd ) + echo $DIR +} + +BASE=$(baseDir) +cd $BASE + +source lib/functions.sh + +assertVarsNotEmpty SITE_ID || fail_and_report 1 "gitpassword.sh failed: SITE_ID is empty." + +PARAMS="$(cat)" +GITHOST=$(echo "$PARAMS" | grep "^host=" | sed 's/host=\(.*\)/\1/g') + +fetchVarsFromVault GIT_PASSWORD + +if [ -z "${GIT_PASSWORD}" ]; then + fail_and_report 1 "gitpassword.sh failed: Git password not found." +fi + +cat < /etc/sudoers.d/bridgehead-"${PROJECT}" +# This has been added by the Bridgehead installer. Remove with bridgehead uninstall. +Cmnd_Alias BRIDGEHEAD${PROJECT^^} = \\ + /bin/systemctl start bridgehead@${PROJECT}.service, \\ + /bin/systemctl stop bridgehead@${PROJECT}.service, \\ + /bin/systemctl restart bridgehead@${PROJECT}.service, \\ + /bin/systemctl restart bridgehead@*.service, \\ + /bin/chown -R bridgehead /etc/bridgehead /srv/docker/bridgehead, \\ + /usr/bin/chown -R bridgehead /etc/bridgehead /srv/docker/bridgehead + +bridgehead ALL= NOPASSWD: BRIDGEHEAD${PROJECT^^} +EOF + +# TODO: Determine whether this should be located in setup-bridgehead (triggered through bridgehead install) or in update bridgehead (triggered every hour) +if [ -z "$LDM_PASSWORD" ]; then + log "INFO" "Now generating a password for the local data management. Please save the password for your ETL process!" + generated_passwd="$(cat /proc/sys/kernel/random/uuid | sed 's/[-]//g' | head -c 32)" + + log "INFO" "Your generated credentials are:\n user: $PROJECT\n password: $generated_passwd" + echo -e "## Local Data Management Basic Authentication\n# User: $PROJECT\nLDM_PASSWORD=$generated_passwd" >> /etc/bridgehead/${PROJECT}.local.conf; +fi + +log "INFO" "Registering system units for bridgehead and bridgehead-update" +cp -v \ + lib/systemd/bridgehead\@.service \ + lib/systemd/bridgehead-update\@.service \ + lib/systemd/bridgehead-update\@.timer \ + /etc/systemd/system/ + +systemctl daemon-reload + +log INFO "Trying to update your bridgehead ..." + +systemctl start bridgehead-update@"${PROJECT}".service + +log "INFO" "Enabling autostart of bridgehead@${PROJECT}.service" +systemctl enable bridgehead@"${PROJECT}".service + +log "INFO" "Enabling auto-updates for bridgehead@${PROJECT}.service ..." +systemctl enable --now bridgehead-update@"${PROJECT}".timer + +STR="\n\n systemctl start bridgehead@${PROJECT}.service\n\nor by rebooting your machine." +if [ -e /etc/bridgehead/pki/${SITE_ID}.priv.pem ]; then + STR="Success. Next, start your bridgehead by running$STR" +else + STR="Success. Next, enroll into the $PROJECT broker by creating a cryptographic certificate. To do so, run\n\n /srv/docker/bridgehead/bridgehead enroll $PROJECT\n\nThen, you may start the bridgehead by running$STR" +fi + +log "INFO" "$STR" \ No newline at end of file diff --git a/lib/log.sh b/lib/log.sh new file mode 100644 index 0000000..c00333d --- /dev/null +++ b/lib/log.sh @@ -0,0 +1,7 @@ +#!/bin/bash + +log() { + SEVERITY="$1" + shift + echo -e "$(date +'%Y-%m-%d %T')" "$SEVERITY:" "$@" +} diff --git a/lib/monitoring.sh b/lib/monitoring.sh new file mode 100755 index 0000000..87e66bc --- /dev/null +++ b/lib/monitoring.sh @@ -0,0 +1,54 @@ +#!/bin/bash + +source lib/log.sh + +function hc_set_uuid(){ + HCUUID="$1" +} + +function hc_set_service(){ + HCSERVICE="$1" +} + +UPTIME= +USER_AGENT= + +function hc_send(){ + BASEURL="https://healthchecks.verbis.dkfz.de/ping" + if [ -n "$MONITOR_APIKEY" ]; then + hc_set_uuid $MONITOR_APIKEY + fi + + if [ -n "$HCSERVICE" ]; then + HCURL="$BASEURL/$PING_KEY/$HCSERVICE" + fi + if [ -n "$HCUUID" ]; then + HCURL="$BASEURL/$HCUUID" + fi + if [ ! -n "$HCURL" ]; then + log WARN "Did not report Healthcheck: Neither Healthcheck UUID nor service set. Please define MONITOR_APIKEY in /etc/bridgehead." + return 0 + fi + + if [ -z "$UPTIME" ]; then + UPTIME=$(docker ps -a --format 'table {{.Names}} \t{{.RunningFor}} \t {{.Status}} \t {{.Image}}' --filter name=bridgehead || echo "Unable to get docker statistics") + fi + + if [ -z "$USER_AGENT" ]; then + if [ "$USER" != "root" ]; then + COMMIT_ETC=$(git -C /etc/bridgehead rev-parse HEAD | cut -c -8) + COMMIT_SRV=$(git -C /srv/docker/bridgehead rev-parse HEAD | cut -c -8) + else + COMMIT_ETC=$(su -c 'git -C /etc/bridgehead rev-parse HEAD' bridgehead | cut -c -8) + COMMIT_SRV=$(su -c 'git -C /srv/docker/bridgehead rev-parse HEAD' bridgehead | cut -c -8) + fi + USER_AGENT="srv:$COMMIT_SRV etc:$COMMIT_ETC" + fi + + if [ -n "$2" ]; then + MSG="$2\n\nDocker stats:\n$UPTIME" + echo -e "$MSG" | https_proxy=$HTTPS_PROXY_URL curl -A "$USER_AGENT" -s -o /dev/null -X POST --data-binary @- "$HCURL"/"$1" || log WARN "Monitoring failed: Unable to send data to $HCURL/$1" + else + https_proxy=$HTTPS_PROXY_URL curl -A "$USER_AGENT" -s -o /dev/null "$HCURL"/"$1" || log WARN "Monitoring failed: Unable to send data to $HCURL/$1" + fi +} diff --git a/lib/prepare-system.sh b/lib/prepare-system.sh new file mode 100755 index 0000000..20285f1 --- /dev/null +++ b/lib/prepare-system.sh @@ -0,0 +1,67 @@ +#!/bin/bash -e + +source lib/log.sh +source lib/functions.sh + +log "INFO" "Preparing your system for bridgehead installation ..." + +# Create the bridgehead user +if id bridgehead &>/dev/null; then + log "INFO" "Existing user with id $(id -u bridgehead) will be used by the bridgehead system units." +else + log "INFO" "Now creating a system user to own the bridgehead's files." + useradd -M -g docker -N bridgehead || fail_and_report "" +fi + +# Clone the OpenSource repository of bridgehead +bridgehead_repository_url="https://github.com/samply/bridgehead.git" +if [ -d "/srv/docker/bridgehead" ]; then + current_owner=$(stat -c '%U' /srv/docker/bridgehead) + if [ "$(su -c 'git -C /srv/docker/bridgehead remote get-url origin' $current_owner)" == "$bridgehead_repository_url" ]; then + log "INFO" "Bridgehead's open-source repository has been found at /srv/docker/bridgehead" + else + log "ERROR" "The directory /srv/docker/bridgehead seems to exist, but doesn't contain a clone of $bridgehead_repository_url\nPlease delete the directory and try again." + exit 1 + fi +else + log "INFO" "Cloning $bridgehead_repository_url to /srv/docker/bridgehead" + mkdir -p /srv/docker/ + git clone bridgehead_repository_url /srv/docker/bridgehead -b feature/samplyBeam +fi + +case "$PROJECT" in + ccp) + site_configuration_repository_middle="git.verbis.dkfz.de/bridgehead-configurations/bridgehead-config-" + ;; + bbmri) + site_configuration_repository_middle="git.verbis.dkfz.de/bbmri-bridgehead-configs/" + ;; + *) + log ERROR "Internal error, this should not happen." + exit 1 + ;; +esac + +# Clone the site-configuration +if [ -d /etc/bridgehead ]; then + current_owner=$(stat -c '%U' /etc/bridgehead) + if [ "$(su -c 'git -C /etc/bridgehead remote get-url origin' $current_owner | grep $site_configuration_repository_middle)" ]; then + log "INFO" "Your site config repository in /etc/bridgehead seems to be installed correctly." + else + log "WARN" "Your site configuration repository in /etc/bridgehead seems to have another origin than git.verbis.dkfz.de. Please check if the repository is correctly cloned!" + fi +else + log "INFO" "Now cloning your site configuration repository for you." + read -p "Please enter your site: " site + read -s -p "Please enter the bridgehead's access token for your site configuration repository (will not be echoed): " access_token + site_configuration_repository_url="https://bytoken:${access_token}@${site_configuration_repository_middle}$(echo $site | tr '[:upper:]' '[:lower:]').git" + git clone $site_configuration_repository_url /etc/bridgehead + if [ $? -gt 0 ]; then + log "ERROR" "Unable to clone your configuration repository. Please obtain correct access data and try again." + fi +fi + +chown -R bridgehead /etc/bridgehead /srv/docker/bridgehead + +log INFO "System preparation is completed and private key is present." + diff --git a/lib/prerequisites.sh b/lib/prerequisites.sh new file mode 100755 index 0000000..8ce7051 --- /dev/null +++ b/lib/prerequisites.sh @@ -0,0 +1,83 @@ +#!/bin/bash + +source lib/functions.sh + +detectCompose + +if ! id "bridgehead" &>/dev/null; then + log ERROR "User bridgehead does not exist. Please run bridgehead install $PROJECT" + exit 1 +fi + +checkOwner /srv/docker/bridgehead bridgehead || exit 1 +checkOwner /etc/bridgehead bridgehead || exit 1 + +## Check if user is a su +log INFO "Checking if all prerequisites are met ..." +prerequisites="git docker" +for prerequisite in $prerequisites; do + $prerequisite --version 2>&1 + is_available=$? + if [ $is_available -gt 0 ]; then + fail_and_report 79 "Prerequisite not fulfilled - $prerequisite is not available!" + fi + # TODO: Check for specific version +done + +log INFO "Checking if sudo is installed ..." +if [ ! -d /etc/sudoers.d ]; then + fail_and_report 1 "/etc/sudoers.d does not exist. Please install sudo package." +fi + +log INFO "Checking configuration ..." + +## Download submodule +if [ ! -d "/etc/bridgehead/" ]; then + fail_and_report 1 "Please set up the config folder at /etc/bridgehead. Instruction are in the readme." +fi + +# TODO: Check all required variables here in a generic loop + +#check if project env is present +if [ -d "/etc/bridgehead/${PROJECT}.conf" ]; then + fail_and_report 1 "Project config not found. Please copy the template from ${PROJECT} and put it under /etc/bridgehead-config/${PROJECT}.conf." +fi + +# TODO: Make sure you're in the right directory, or, even better, be independent from the working directory. + +log INFO "Checking ssl cert for accessing bridgehead via https" + +if [ ! -d "/etc/bridgehead/traefik-tls" ]; then + log WARN "TLS certs for accessing bridgehead via https missing, we'll now create a self-signed one. Please consider getting an officially signed one (e.g. via Let's Encrypt ...) and put into /etc/bridgehead/traefik-tls" + mkdir -p /etc/bridgehead/traefik-tls +fi + +if [ ! -e "/etc/bridgehead/traefik-tls/fullchain.pem" ]; then + openssl req -x509 -newkey rsa:4096 -nodes -keyout /etc/bridgehead/traefik-tls/privkey.pem -out /etc/bridgehead/traefik-tls/fullchain.pem -days 3650 -subj "/CN=$HOST" +fi + +if [ -e /etc/bridgehead/vault.conf ]; then + if [ "$(stat -c "%a %U" /etc/bridgehead/vault.conf)" != "600 bridgehead" ]; then + fail_and_report 1 "/etc/bridgehead/vault.conf has wrong owner/permissions. To correct this issue, run chmod 600 /etc/bridgehead/vault.conf && chown bridgehead /etc/bridgehead/vault.conf." + fi +fi + +checkPrivKey() { + if [ -e /etc/bridgehead/pki/${SITE_ID}.priv.pem ]; then + log INFO "Success - private key found." + else + log ERROR "Unable to find private key at /etc/bridgehead/pki/${SITE_ID}.priv.pem. To fix, please run\n bridgehead enroll ${PROJECT}\nand follow the instructions." + return 1 + fi + log INFO "Success - all prerequisites are met!" + hc_send log "Success - all prerequisites are met!" + return 0 +} + +if [[ "$@" =~ "noprivkey" ]]; then + log INFO "Skipping check for private key for now." +else + checkPrivKey || exit 1 +fi + +exit 0 diff --git a/lib/systemd/bridgehead-update@.service b/lib/systemd/bridgehead-update@.service new file mode 100644 index 0000000..3a7f347 --- /dev/null +++ b/lib/systemd/bridgehead-update@.service @@ -0,0 +1,12 @@ +[Unit] +Description=Bridgehead (%i) Update Service + +[Service] +Type=oneshot +User=bridgehead +ExecStartPre=-/srv/docker/bridgehead/bridgehead preUpdate %i +ExecStart=/srv/docker/bridgehead/bridgehead update %i +ExecStopPost=-/srv/docker/bridgehead/bridgehead postUpdate %i + +[Install] +WantedBy=multi-user.target diff --git a/lib/systemd/bridgehead-update@.timer b/lib/systemd/bridgehead-update@.timer new file mode 100644 index 0000000..4c8fada --- /dev/null +++ b/lib/systemd/bridgehead-update@.timer @@ -0,0 +1,8 @@ +[Unit] +Description=Hourly Updates of Bridgehead (%i) + +[Timer] +OnCalendar=*-*-* *:00:00 + +[Install] +WantedBy=basic.target diff --git a/lib/systemd/bridgehead@.service b/lib/systemd/bridgehead@.service new file mode 100644 index 0000000..7645793 --- /dev/null +++ b/lib/systemd/bridgehead@.service @@ -0,0 +1,15 @@ +[Unit] +Description=Bridgehead (%i) +Requires=docker.service + +[Service] +User=bridgehead +Restart=always +RestartSec=30 +ExecStartPre=-/srv/docker/bridgehead/bridgehead preRun %i +ExecStart=/srv/docker/bridgehead/bridgehead start %i +ExecStop=/srv/docker/bridgehead/bridgehead stop %i +ExecStopPost=-/srv/docker/bridgehead/bridgehead postRun %i + +[Install] +WantedBy=multi-user.target diff --git a/lib/traefik-configuration/certificates.yaml b/lib/traefik-configuration/certificates.yaml new file mode 100644 index 0000000..af392c9 --- /dev/null +++ b/lib/traefik-configuration/certificates.yaml @@ -0,0 +1,6 @@ +tls: + stores: + default: + defaultCertificate: + certFile: /certs/fullchain.pem + keyFile: /certs/privkey.pem diff --git a/lib/uninstall-bridgehead.sh b/lib/uninstall-bridgehead.sh new file mode 100755 index 0000000..ab1108e --- /dev/null +++ b/lib/uninstall-bridgehead.sh @@ -0,0 +1,24 @@ +#!/bin/bash -e + +source lib/functions.sh + +if [ $# -eq 0 ]; then + log "ERROR" "Please provide a Project as argument" + exit 1 +fi + +export PROJECT=$1 + +#checkRequirements // not needed when uninstalling + +log INFO "Removing bridgehead sudoers permissions." + +rm -vf /etc/sudoers.d/bridgehead-${PROJECT} + +log "INFO" "Stopping system units and removing bridgehead for ${PROJECT} ..." + +systemctl disable --now bridgehead@${PROJECT}.service bridgehead-update@${PROJECT}.timer bridgehead-update@${PROJECT}.service + +rm -v /etc/systemd/system/{bridgehead\@.service,bridgehead-update\@.timer,bridgehead-update\@.service} + +log "INFO" "Successfully removed bridgehead for ${PROJECT} from your system" diff --git a/lib/update-bridgehead.sh b/lib/update-bridgehead.sh new file mode 100755 index 0000000..3201fc5 --- /dev/null +++ b/lib/update-bridgehead.sh @@ -0,0 +1,109 @@ +#!/bin/bash +source lib/functions.sh + +AUTO_HOUSEKEEPING=${AUTO_HOUSEKEEPING:-true} + +if [ "$AUTO_HOUSEKEEPING" == "true" ]; then + A="Performing automatic maintenance: Cleaning docker images." + hc_send log "$A" + log INFO "$A" + docker system prune -a -f +else + log WARN "Automatic housekeeping disabled (variable AUTO_HOUSEKEEPING != \"true\")" +fi + +hc_send log "Checking for bridgehead updates ..." + +CONFFILE=/etc/bridgehead/$1.conf + +if [ ! -e $CONFFILE ]; then + fail_and_report 1 "Configuration file $CONFFILE not found." +fi + +source $CONFFILE + +assertVarsNotEmpty SITE_ID || fail_and_report 1 "Update failed: SITE_ID empty" +export SITE_ID + +checkOwner . bridgehead || fail_and_report 1 "Update failed: Wrong permissions in $(pwd)" +checkOwner /etc/bridgehead bridgehead || fail_and_report 1 "Update failed: Wrong permissions in /etc/bridgehead" + +CREDHELPER="/srv/docker/bridgehead/lib/gitpassword.sh" + +CHANGES="" + +# Check git updates +git_updated="false" +for DIR in /etc/bridgehead $(pwd); do + log "INFO" "Checking for updates to git repo $DIR ..." + OUT="$(git -C $DIR status --porcelain)" + if [ -n "$OUT" ]; then + log WARN "The working directory $DIR is modified. Changed files: $OUT" + report_error log "The working directory $DIR is modified. Changed files: $OUT" + fi + if [ "$(git -C $DIR config --get credential.helper)" != "$CREDHELPER" ]; then + log "INFO" "Configuring repo to use bridgehead git credential helper." + git -C $DIR config credential.helper "$CREDHELPER" + fi + old_git_hash="$(git -C $DIR rev-parse --verify HEAD)" + if [ -z "$HTTP_PROXY_URL" ]; then + log "INFO" "Git is using no proxy!" + OUT=$(git -C $DIR fetch 2>&1 && git -C $DIR pull 2>&1) + else + log "INFO" "Git is using proxy ${HTTP_PROXY_URL} from ${CONFFILE}" + OUT=$(git -c http.proxy=$HTTP_PROXY_URL -c https.proxy=$HTTPS_PROXY_URL -C $DIR fetch 2>&1 && git -c http.proxy=$HTTP_PROXY_URL -c https.proxy=$HTTPS_PROXY_URL -C $DIR pull 2>&1) + fi + if [ $? -ne 0 ]; then + report_error log "Unable to update git $DIR: $OUT" + fi + + new_git_hash="$(git -C $DIR rev-parse --verify HEAD)" + if [ "$old_git_hash" != "$new_git_hash" ]; then + CHANGE="Updated git repository in ${DIR} from commit $old_git_hash to $new_git_hash" + CHANGES+="- $CHANGE\n" + log "INFO" "$CHANGE" + # NOTE: Link generation doesn't work on repositories placed at an self-hosted instance of bitbucket. + # See: https://community.atlassian.com/t5/Bitbucket-questions/BitBucket-4-14-diff-between-any-two-commits/qaq-p/632974 + git_repository_url="$(git -C $DIR remote get-url origin)" + git_repository_url=${git_repository_url/.git/} + if [ "$( echo $git_repository_url | grep "github.com")" ]; then + # Ensure web link even if ssh is used + git_repository_url="${git_repository_url/git@github.com/https:\/\/github.com\/}" + log "INFO" "You can review all changes on the repository with $git_repository_url/compare/$old_git_hash...$new_git_hash" + elif [ "$( echo $git_repository_url | grep "git.verbis.dkfz.de")" ]; then + git_repository_url="${git_repository_url/ssh:\/\/git@git.verbis.dkfz.de/https:\/\/git.verbis.dkfz.de\/}" + git_repository_url="https://$(echo $git_repository_url | awk -F '@' '{print $2}')" + log "INFO" "You can review all changes on the repository with $git_repository_url/compare?from=$old_git_hash&to=$new_git_hash" + fi + git_updated="true" + fi +done + +# Check docker updates +log "INFO" "Checking for updates to running docker images ..." +docker_updated="false" +for IMAGE in $(cat $PROJECT/docker-compose.yml | grep -v "^#" | grep "image:" | sed -e 's_^.*image: \(.*\).*$_\1_g; s_\"__g'); do + log "INFO" "Checking for Updates of Image: $IMAGE" + if docker pull $IMAGE | grep "Downloaded newer image"; then + CHANGE="Image $IMAGE updated." + CHANGES+="- $CHANGE\n" + log "INFO" "$CHANGE" + docker_updated="true" + fi +done + +# If anything is updated, restart service +if [ $git_updated = "true" ] || [ $docker_updated = "true" ]; then + RES="Updates detected, now restarting bridgehead:\n$CHANGES" + log "INFO" "$RES" + hc_send log "$RES" + sudo /bin/systemctl restart bridgehead@*.service +else + RES="Nothing updated, nothing to restart." + log "INFO" "$RES" + hc_send log "$RES" +fi + +exit 0 + +# TODO: Print last commit explicit