diff --git a/.github/actions/build_and_push/action.yml b/.github/actions/build_and_push/action.yml new file mode 100644 index 00000000000..355ba5741a6 --- /dev/null +++ b/.github/actions/build_and_push/action.yml @@ -0,0 +1,85 @@ +name: Builds and pushes Docker image +description: Action to build and push images to docker + + +inputs: + dockerfile: + description: Path to the Dockerfile + default: ./Dockerfile + required: false + dockercontext: + description: Path to the Docker context + default: ./ + required: false + # image coordinates + image: + description: Name of the image to build + required: true + tags: + description: Image Tag(s) + required: false + default: latest + # OCI metadata annotations + oci_title: + description: Image Title (OCI annotation) + required: true + oci_description: + description: Image Description (OCI annotation) + required: true + # registry credentials + registry_username: + description: The username for Docker hub sign-in + required: true + registry_password: + description: The password for Docker hub sign-in + required: true + + +runs: + using: "composite" + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + uses: dorny/paths-filter@v2 + id: changes + with: + filters: | + src: + - '${{ inputs.dockercontext }}/**' + # - + # name: Set up Docker Buildx + # uses: docker/setup-buildx-action@v2 + # - + # name: Extract metadata (tags, labels) for Docker + # id: meta + # uses: docker/metadata-action@v4 + # with: + # images: ${{ inputs.image }} + # labels: | + # "org.opencontainers.image.vendor=52°North GmbH" + # "org.opencontainers.image.authors=https://52North.org/" + # "org.opencontainers.image.source=https://github.com/52North/geonode" + # "org.opencontainers.image.description=${{ inputs.oci_description }}" + # "org.opencontainers.image.title=${{ inputs.oci_title }}" + # "org.opencontainers.image.licenses=GPL-3.0" + # tags: | + # ${{ inputs.tags }} + # - + # name: Login to Docker registry + # uses: docker/login-action@v2 + # with: + # username: ${{ inputs.registry_username }} + # password: ${{ inputs.registry_password }} + # - + # name: Build and push + # uses: docker/build-push-action@v4 + # with: + # context: . + # file: ${{ inputs.dockerfile }} + # push: true + # tags: ${{ steps.meta.outputs.tags }} + # labels: ${{ steps.meta.outputs.labels }} + # cache-from: type=registry,ref=${{ inputs.image }}:buildcache + # cache-to: type=registry,ref=${{ inputs.image }}:buildcache,mode=max diff --git a/.github/workflows/52n-build-master.yml b/.github/workflows/52n-build-master.yml new file mode 100644 index 00000000000..9c19abc1d4c --- /dev/null +++ b/.github/workflows/52n-build-master.yml @@ -0,0 +1,71 @@ +name: "[52n-master -> latest] Builds GeoNode Docker Images" + +concurrency: + group: "geonode_build_master" + cancel-in-progress: true + +env: + TAG: latest + +on: + push: + branches: + - "52n-master" + +jobs: + build_and_push_geonode: + runs-on: ubuntu-22.04 + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + name: build and push geonode + uses: ./.github/actions/build_and_push + with: + image: 52north/geonode + tags: ${{ env.TAG }} + oci_title: "52°North GeoNode image" + oci_description: "GeoNode built from 52n fork" + registry_username: ${{ secrets.DOCKERHUB_USERNAME }} + registry_password: ${{ secrets.DOCKERHUB_TOKEN_52N_MASTER }} + + build_and_push_nginx: + runs-on: ubuntu-22.04 + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + name: build and push nginx + uses: ./.github/actions/build_and_push + with: + dockerfile: ./scripts/docker/nginx/Dockerfile + dockercontext: ./scripts/docker/nginx/ + image: 52north/geonode-nginx + tags: ${{ env.TAG }} + oci_title: "52°North Nginx image for GeoNode" + oci_description: "Nginx built for GeoNode from a 52n fork" + registry_username: ${{ secrets.DOCKERHUB_USERNAME }} + registry_password: ${{ secrets.DOCKERHUB_TOKEN_52N_MASTER }} + + build_and_push_geoserver: + runs-on: ubuntu-22.04 + env: + IMAGE: 52north/geonode-geoserver + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + name: build and push geoserver + uses: ./.github/actions/build_and_push + with: + dockerfile: ./scripts/docker/geoserver/Dockerfile + dockercontext: ./scripts/docker/geoserver/ + image: 52north/geonode-geoserver + tags: ${{ env.TAG }} + oci_title: "52°North GeoServer image for GeoNode" + oci_description: "GeoServer built for GeoNode from a 52n fork" + registry_username: ${{ secrets.DOCKERHUB_USERNAME }} + registry_password: ${{ secrets.DOCKERHUB_TOKEN_52N_MASTER }} diff --git a/.github/workflows/dockerhub-description.yml b/.github/workflows/dockerhub-description.yml new file mode 100644 index 00000000000..3dbe8fbb583 --- /dev/null +++ b/.github/workflows/dockerhub-description.yml @@ -0,0 +1,23 @@ +name: Update Docker Hub Description +on: + push: + branches: + - 52n-master + paths: + - README_52n.md + - .github/workflows/dockerhub-description.yml +jobs: + dockerHubDescription: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Docker Hub Description + uses: peter-evans/dockerhub-description@v3 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN_52N_MASTER }} + repository: 52north/geonode + short-description: "Geospatial content management system" + readme-filepath: ./README_52n.md + enable-url-completion: true \ No newline at end of file diff --git a/.gitignore b/.gitignore index 7a455624878..60f95d0efc8 100644 --- a/.gitignore +++ b/.gitignore @@ -96,4 +96,4 @@ scripts/spcgeonode/_volume_* !hooks/* .env - +.secret diff --git a/CHANGELOG.md b/CHANGELOG.md index ee54d4a6d2e..e2ac7e72bd1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,63 @@ # Change Log +## [4.1.2](https://github.com/GeoNode/geonode/tree/4.1.2) (2023-08-11) +## Bug Fixes +- Upgrade to importer 1.0.5 which fixes the import with filenames longer then 63 chars +- Fixed parsing and rendering of ISO TC211 spatial representetion type +## [4.1.1](https://github.com/GeoNode/geonode/tree/4.1.1) (2023-06-05) +## Security and Bug Fixes +- Upgrade to Ubuntu 22.10 +- Upgrade to Django 3.2.20 +- Fixed direct download URL not working in some cases when local files are not available +- Fixed assignment of regions crossing the dateline +- Fixed harvesting of ArcGIS REST ImageServer services +- Fixed some italian translations +- Disabling of the (deprecated) Monitoring module +## [4.1.0](https://github.com/GeoNode/geonode/tree/4.1.0) (2023-06-05) +### New upload engine +GeoNode integrates a brand new importer module based on [GDAL/OGR](https://gdal.org/), which offers increased robustness and reliability to the upload UI and API services. GeoPackage (vector), GeoJSON, KML/KMZ formats and a new CSV handler have been implemented. + +### Thesaurus faceting and date filtering +If thesaurus and thesaurus keywords are configured and assigned to resources, they will be available inside the filters panel, along with the number of associated resources. +Date filtering (from/top) has also been added. + +### Time series configurable after the upload +The configuration of (potential) time series at upload time was confusing for users, and not very robust. +With the new importer, the optional configuration of vector time series can be done afterward, through the Settings tab inside the Metadata editing page +Only vector fomats that provide date(time) fields natively are supported. Conversion from string fields is not implemented. + +### Related resources +This restore a functionality available in previous versions of GeoNode. +A tab inside the info panel has been added where relationships between datasets, maps and documents are reported. + +### Vector dataset attributes +A tab inside the info panel has been added showing the attributes of vector datasets + +### Remote documents +The API has been extended to permit the creation of document resources referencing remote URLs + +### ISO-19115 XML upload via API +The API now supports the upload of a metadata XML file along with the resource data + +### Software upgrades + + - [Geoserver 2.23.0](https://geoserver.org/announcements/2023/04/05/geoserver-2-23-0-released.html) is now the reference version. This version includes Geofence WPS rules which are employed by GeoNode to strengthen the security of the OGC/WPS processes. +- [MapStore 2022.02.xx](https://github.com/geosolutions-it/MapStore2/tree/2022.02.xx) +- [Django 3.2.19](https://docs.djangoproject.com/en/4.2/releases/3.2.19/) +- PostgreSQL 13 and PostGIS 3.3.3 + + +## Security and Bug Fixes +- [CVE-2023-26043](https://github.com/GeoNode/geonode/security/advisories/GHSA-mcmc-c59m-pqq8) +Fixed a vulnerability to XML External Entity (XXE) injection +- [CVE-2023-28442](https://github.com/GeoNode/geonode/security/advisories/GHSA-87mh-vw7c-5v6w) +Fixed information leak + +You can see the **full list of closed issues [here](https://github.com/GeoNode/geonode/compare/4.1.0...4.0.3)**. + +## System requirements +Python >3.9 is required to run GeoNode 4.1.0, since many of its dependencies have dropped support for older versions. + ## [4.0.2](https://github.com/GeoNode/geonode/tree/4.0.2) (2022-12-20) ### Breaking Changes diff --git a/README_52n.md b/README_52n.md new file mode 100644 index 00000000000..3e92b9009b5 --- /dev/null +++ b/README_52n.md @@ -0,0 +1,25 @@ +# 52°North Fork of GeoNode + +This image is built from a fork of [Geonode](https://github.com/geonode/geonode). +[52°North GmbH](https://52north.org) maintains an own fork of GeoNode in order to make necessary adjustments within projects which are not part of GeoNode core. + +However, we are interested to stay as close to upstream as possible, to benefit from ongoing development, but also to contribute features and fixes we develop in our projects. + +Starting from version `4` this image is built from the `52n-master` branch of the [`52north/geonode` repository](https://github.com/52North/geonode/tree/52n-master). +The repository builds and publishes three images: + +* [`52north/geonode`](https://hub.docker.com/r/52north/geonode) (this image) +* [`52north/geonode-nginx`](https://hub.docker.com/r/52north/geonode-nginx) +* [`52north/geonode-geoserver`](https://hub.docker.com/r/52north/geonode-geoserver) + +The Dockerfiles can be found under the [`./scripts/docker` folder](https://github.com/52North/geonode/tree/52n-master/scripts/docker). + +The official Docker configuration of [GeoServer for GeoNode](https://github.com/GeoNode/geoserver-docker) seems to be outdated. +Therefore, our fork adds a `./scripts/docker/geoserver` Docker config which is based on [the geonode-project](https://github.com/geonode/geonode-project) template. + +Depending on our current project contexts we merge regularly from upstream, and create new pull requests based on this fork. + +> **Note on version `3` tags** +> +> Images containing a `3.x` version tag were experimental and do have a different code base. +> These image are considered to be removed in the near future. \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 39864fb3294..fe499acc592 100644 --- a/requirements.txt +++ b/requirements.txt @@ -9,7 +9,7 @@ amqp==5.1.1 beautifulsoup4==4.12.2 httplib2<0.22.1 hyperlink==21.0.0 -idna>=2.5,<3.5 +idna>=2.5,<3.7 urllib3==1.26.17 Paver==1.3.4 python-slugify==8.0.1 diff --git a/scripts/docker/geoserver/Dockerfile b/scripts/docker/geoserver/Dockerfile new file mode 100644 index 00000000000..cf69384629f --- /dev/null +++ b/scripts/docker/geoserver/Dockerfile @@ -0,0 +1,113 @@ +ARG IMAGE_VERSION=9.0-jdk11-openjdk-slim-bullseye +ARG JAVA_HOME=/usr/local/openjdk-11 +FROM tomcat:$IMAGE_VERSION +LABEL GeoNode Development Team + +ARG GEOSERVER_CORS_ENABLED=False +ARG GEOSERVER_CORS_ALLOWED_ORIGINS=* +ARG GEOSERVER_CORS_ALLOWED_METHODS=GET,POST,PUT,DELETE,HEAD,OPTIONS +ARG GEOSERVER_CORS_ALLOWED_HEADERS=* +# +# Set GeoServer version and data directory +# +ENV GEOSERVER_VERSION=2.23.0 +ENV GEOSERVER_DATA_DIR="/geoserver_data/data" +ENV GEOSERVER_CORS_ENABLED=$GEOSERVER_CORS_ENABLED +ENV GEOSERVER_CORS_ALLOWED_ORIGINS=$GEOSERVER_CORS_ALLOWED_ORIGINS +ENV GEOSERVER_CORS_ALLOWED_METHODS=$GEOSERVER_CORS_ALLOWED_METHODS +ENV GEOSERVER_CORS_ALLOWED_HEADERS=$GEOSERVER_CORS_ALLOWED_HEADERS +# +# Download and install GeoServer +# +RUN apt-get update -y && apt-get install curl wget unzip -y +RUN cd /usr/local/tomcat/webapps \ + && wget --no-check-certificate --progress=bar:force:noscroll https://artifacts.geonode.org/geoserver/${GEOSERVER_VERSION}/geoserver.war -O geoserver.war \ + && unzip -q geoserver.war -d geoserver \ + && rm geoserver.war \ + && mkdir -p $GEOSERVER_DATA_DIR + +VOLUME $GEOSERVER_DATA_DIR + +# added by simonelanucara https://github.com/simonelanucara +# Optionally add JAI, ImageIO and Marlin Render for improved Geoserver performance +WORKDIR /tmp + +RUN wget --no-check-certificate https://repo1.maven.org/maven2/org/postgis/postgis-jdbc/1.3.3/postgis-jdbc-1.3.3.jar -O postgis-jdbc-1.3.3.jar && \ + wget --no-check-certificate https://maven.geo-solutions.it/org/hibernatespatial/hibernate-spatial-postgis/1.1.3.2/hibernate-spatial-postgis-1.1.3.2.jar -O hibernate-spatial-postgis-1.1.3.2.jar && \ + rm /usr/local/tomcat/webapps/geoserver/WEB-INF/lib/hibernate-spatial-h2-geodb-1.1.3.2.jar && \ + mv hibernate-spatial-postgis-1.1.3.2.jar /usr/local/tomcat/webapps/geoserver/WEB-INF/lib/ && \ + mv postgis-jdbc-1.3.3.jar /usr/local/tomcat/webapps/geoserver/WEB-INF/lib/ + +###########docker host############### +# Set DOCKERHOST variable if DOCKER_HOST exists +ARG DOCKERHOST=${DOCKERHOST} +# for debugging +RUN echo -n #1===>DOCKERHOST=${DOCKERHOST} +# +ENV DOCKERHOST ${DOCKERHOST} +# for debugging +RUN echo -n #2===>DOCKERHOST=${DOCKERHOST} + +###########docker host ip############# +# Set GEONODE_HOST_IP address if it exists +ARG GEONODE_HOST_IP=${GEONODE_HOST_IP} +# for debugging +RUN echo -n #1===>GEONODE_HOST_IP=${GEONODE_HOST_IP} +# +ENV GEONODE_HOST_IP ${GEONODE_HOST_IP} +# for debugging +RUN echo -n #2===>GEONODE_HOST_IP=${GEONODE_HOST_IP} +# If empty set DOCKER_HOST_IP to GEONODE_HOST_IP +ENV DOCKER_HOST_IP=${DOCKER_HOST_IP:-${GEONODE_HOST_IP}} +# for debugging +RUN echo -n #1===>DOCKER_HOST_IP=${DOCKER_HOST_IP} +# Trying to set the value of DOCKER_HOST_IP from DOCKER_HOST +RUN if ! [ -z ${DOCKER_HOST_IP} ]; \ + then echo export DOCKER_HOST_IP=${DOCKERHOST} | \ + sed 's/tcp:\/\/\([^:]*\).*/\1/' >> /root/.bashrc; \ + else echo "DOCKER_HOST_IP is already set!"; fi +# for debugging +RUN echo -n #2===>DOCKER_HOST_IP=${DOCKER_HOST_IP} + +# Set WEBSERVER public port +ARG PUBLIC_PORT=${PUBLIC_PORT} +# for debugging +RUN echo -n #1===>PUBLIC_PORT=${PUBLIC_PORT} +# +ENV PUBLIC_PORT=${PUBLIC_PORT} +# for debugging +RUN echo -n #2===>PUBLIC_PORT=${PUBLIC_PORT} + +# set nginx base url for geoserver +RUN echo export NGINX_BASE_URL=http://${NGINX_HOST}:${NGINX_PORT}/ | \ + sed 's/tcp:\/\/\([^:]*\).*/\1/' >> /root/.bashrc + +# copy the script and perform the run of scripts from entrypoint.sh +RUN mkdir -p /usr/local/tomcat/tmp +WORKDIR /usr/local/tomcat/tmp +COPY set_geoserver_auth.sh /usr/local/tomcat/tmp +COPY setup_auth.sh /usr/local/tomcat/tmp +COPY requirements.txt /usr/local/tomcat/tmp +COPY get_dockerhost_ip.py /usr/local/tomcat/tmp +COPY get_nginxhost_ip.py /usr/local/tomcat/tmp +COPY entrypoint.sh /usr/local/tomcat/tmp +COPY ./templates /templates +COPY multidump.sh /usr/local/tomcat/tmp +COPY multidump-alt.sh /usr/local/tomcat/tmp + +RUN apt-get update \ + && apt-get install -y procps less \ + && apt-get install -y python3 python3-pip python3-dev \ + && chmod +x /usr/local/tomcat/tmp/set_geoserver_auth.sh \ + && chmod +x /usr/local/tomcat/tmp/setup_auth.sh \ + && chmod +x /usr/local/tomcat/tmp/entrypoint.sh \ + && pip3 install pip --upgrade \ + && pip3 install -r requirements.txt \ + && chmod +x /usr/local/tomcat/tmp/get_dockerhost_ip.py \ + && chmod +x /usr/local/tomcat/tmp/get_nginxhost_ip.py + +RUN pip install j2cli + +ENV JAVA_OPTS="-Djava.awt.headless=true -Dgwc.context.suffix=gwc -XX:+UnlockDiagnosticVMOptions -XX:+LogVMOutput -XX:LogFile=/var/log/jvm.log -XX:MaxPermSize=512m -XX:PermSize=256m -Xms512m -Xmx2048m -XX:+UseConcMarkSweepGC -XX:ParallelGCThreads=4 -Dfile.encoding=UTF8 -Djavax.servlet.request.encoding=UTF-8 -Djavax.servlet.response.encoding=UTF-8 -Duser.timezone=GMT -Dorg.geotools.shapefile.datetime=false -DGS-SHAPEFILE-CHARSET=UTF-8 -DGEOSERVER_CSRF_DISABLED=true -DPRINT_BASE_URL=http://geoserver:8080/geoserver/pdf -Xbootclasspath/a:/usr/local/tomcat/webapps/geoserver/WEB-INF/lib/marlin-0.9.3.jar -Dsun.java2d.renderer=org.marlin.pisces.MarlinRenderingEngine" + +CMD ["/usr/local/tomcat/tmp/entrypoint.sh"] \ No newline at end of file diff --git a/scripts/docker/geoserver/README.md b/scripts/docker/geoserver/README.md new file mode 100644 index 00000000000..9d00a465066 --- /dev/null +++ b/scripts/docker/geoserver/README.md @@ -0,0 +1,132 @@ +# geoserver-docker + + +**The scripts/docker/geonode folder is a copy from geonode-project* to be able to build GeoServer image from this repository directly. In case of an update, just replace the whole folder.** + + +[GeoServer](http://geoserver.org) is an open source server for sharing geospatial data. +This is a docker image that eases setting up a GeoServer running specifically for [GeoNode](https://github.com/GeoNode/geoserver-geonode-ext) with an additional separated data directory. + +The image is based on the official Tomcat 9 image + +## Installation + +This image is available as a [trusted build on the docker hub](https://registry.hub.docker.com/r/geonode/geoserver/), and is the recommended method of installation. +Simple pull the image from the docker hub. + +```bash +$ docker pull geonode/geoserver +``` + +Alternatively you can build the image locally + +```bash +$ git clone https://github.com/geonode/geoserver-docker.git +$ cd geoserver-docker +$ docker build -t "geonode/geoserver" . +``` + +## Quick start + +You can quick start the image using the command line + +```bash +$ docker run --name "geoserver" -v /var/run/docker.sock:/var/run/docker.sock -d -p 8080:8080 geonode/geoserver +``` + +Point your browser to `http://localhost:8080/geoserver` and login using GeoServer's default username and password: + +* Username: admin +* Password: geoserver + +## How to use different versions + +There are mainly two different versions of this image which are useful for running **GeoNode** with different authentication system types. These versions are released as specific tags for two authentication mechanisms: + +**Cookie based authn**: +- [geonode/geoserver:2.9.x](https://hub.docker.com/r/geonode/geoserver/builds/bx7ydhghnlrfnsppduyva73/) + +**Oauth2 based authn**: +- [geonode/geoserver:2.9.x-oauth2](https://hub.docker.com/r/geonode/geoserver/builds/bwca5rtexeoegzgroavftdr/) +- [geonode/geoserver:2.10.x](https://hub.docker.com/r/geonode/geoserver/builds/bjohcnc29vm69acqjrvndxf/) +- [geonode/geoserver:2.12.x](https://hub.docker.com/r/geonode/geoserver/builds/bh7pyw5atmkcljurwsnzbs7/) +- [geonode/geoserver:2.13.x](https://hub.docker.com/r/geonode/geoserver/builds/btmjctbuvrjfnnrxrs4wyrs/) +- [geonode/geoserver:2.14.x](https://hub.docker.com/r/geonode/geoserver/builds/bj53pi8he8uksz6ggvrs3wc/) + +You can declare what version to use along with the data directory tag which corresponds to the same version. + +## Configuration + +### Data volume + +This GeoServer container keeps its configuration data at `/geoserver_data/data` which is exposed as volume in the dockerfile. +The volume allows for stopping and starting new containers from the same image without losing all the data and custom configuration. + +You may want to map this volume to a directory on the host. It will also ease the upgrade process in the future. Volumes can be mounted by passing the `-v` flag to the docker run command: + +```bash +-v /your/host/data/path:/geoserver_data/data +``` + +### Data volume container + +In case you are running Compose for automatically having GeoServer up and running then a data volume container will be mounted with a default preloaded *GEOSERVER_DATA_DIR* at the configuration data directory of the container. +Make sure that the image from the repository [data-docker](https://github.com/GeoNode/data-docker) is available from the [GeoNode Docker Hub](https://hub.docker.com/u/geonode/) or has been built locally: + +```bash +docker build -t geonode/geoserver_data . +``` + +#### Persistance behavior + +If you run: + +```bash +docker-compose stop +``` + +Data are retained in the *GEOSERVER_DATA_DIR* and can then be mounted in a new GeoServer instance by running again: + +```bash +docker-compose up +``` + +If you run: + +```bash +docker-compose down +``` + +Data are completely gone but you can ever start from the base GeoServer Data Directory built for Geonode. + +#### Data directory versions + +There has to be a correspondence one-to-one between the data directory version and the tag of the GeoServer image used in the Docker compose file. So at the end you can consume these images below: + +* **2.9.x**: [geonode/geoserver_data:2.9.x](https://hub.docker.com/r/geonode/geoserver_data/builds/bsus6alnddg4bc7icwymevp/) +* **2.9.x-oauth2**: [geonode/geoserver_data:2.9.x-oauth2](https://hub.docker.com/r/geonode/geoserver_data/builds/bwkxcupsunvuitzusi9gsnt/) +* **2.10.x**: [geonode/geoserver_data:2.10.x](https://hub.docker.com/r/geonode/geoserver_data/builds/b5jqhpzapkqxzyevjizccug/) +* **2.12.x**: [geonode/geoserver_data:2.12.x](https://hub.docker.com/r/geonode/geoserver_data/builds/byaaalw3lnasunpveyg3x4i/) +* **2.13.x**: [geonode/geoserver_data:2.13.x](https://hub.docker.com/r/geonode/geoserver_data/builds/bunuqzq7a7dk65iumjhkbtc/) +* **2.14.x**: [geonode/geoserver_data:2.14.x](https://hub.docker.com/r/geonode/geoserver_data/builds/blpdjzkrv7pm3stunzpn4pp/) + +### Database + +GeoServer recommends the usage of a spatial database + +#### PostGIS container (PostgreSQL + GIS Extension) + +If you want to use a [PostGIS](http://postgis.org/) container, you can link it to this image. You're free to use any PostGIS container. +An example with [kartooza/postgis](https://registry.hub.docker.com/u/kartoza/postgis/) image: + +```bash +$ docker run -d --name="postgis" kartoza/postgis +``` + +For further information see [kartooza/postgis](https://registry.hub.docker.com/u/kartoza/postgis/). + +Now start the GeoServer instance by adding the `--link` option to the docker run command: + +```bash +--link postgis:postgis +``` diff --git a/scripts/docker/geoserver/docker-compose.yml b/scripts/docker/geoserver/docker-compose.yml new file mode 100644 index 00000000000..5f3dc1cf34e --- /dev/null +++ b/scripts/docker/geoserver/docker-compose.yml @@ -0,0 +1,61 @@ +version: '3.9' + +services: + + postgis: + image: geonode/postgis:13 + ports: + - "25432:5432" + volumes: + - /srv/docker/geoserver/postgis:/var/lib/postgresql + #volumes_from: + #- pgstore + healthcheck: + test: "pg_isready -d postgres -U postgres" + restart: on-failure + + geoserver: + image: geonode/geoserver:2.23.0 + build: + context: . + args: + - DOCKERHOST + - GEONODE_HOST_IP + - PUBLIC_PORT=80 + links: + - postgis + ports: + - "8080:8080" + volumes: + - /geoserver_data/data + environment: + - DOCKERHOST + - GEONODE_HOST_IP + - PUBLIC_PORT=80 + - DOCKER_HOST_IP + - DJANGO_URL=http://localhost/ + depends_on: + postgis: + condition: service_completed_successfully + data-dir-conf: + condition: service_healthy + healthcheck: + test: curl --fail -s http://localhost:8080/geoserver/rest/workspaces/geonode.html || exit 1 + interval: 1m30s + timeout: 10s + retries: 3 + restart: on-failure + + data-dir-conf: + image: geonode/geoserver_data:2.23.0 + container_name: geoserver_data_dir # named data container + entrypoint: sleep infinity + volumes: + - /geoserver_data/data + healthcheck: + test: "ls -A '/geoserver_data/data' | wc -l" + restart: on-failure + +volumes: + # reference to the named data container that holds the preloaded geoserver data directory + geoserver_data_dir: \ No newline at end of file diff --git a/scripts/docker/geoserver/entrypoint.sh b/scripts/docker/geoserver/entrypoint.sh new file mode 100644 index 00000000000..f9af5f42024 --- /dev/null +++ b/scripts/docker/geoserver/entrypoint.sh @@ -0,0 +1,152 @@ +#!/bin/bash +set -e + +source /root/.bashrc + +# control the value of DOCKER_HOST_IP variable +if [ -z ${DOCKER_HOST_IP} ] +then + + echo "DOCKER_HOST_IP is empty so I'll run the python utility \n" + echo export DOCKER_HOST_IP=`python3 /usr/local/tomcat/tmp/get_dockerhost_ip.py` >> /root/.override_env + echo "The calculated value is now DOCKER_HOST_IP='$DOCKER_HOST_IP' \n" + +else + + echo "DOCKER_HOST_IP is filled so I'll leave the found value '$DOCKER_HOST_IP' \n" + +fi + +# control the values of LB settings if present +if [ ${GEONODE_LB_HOST_IP} ] +then + + echo "GEONODE_LB_HOST_IP is filled so I replace the value of '$DOCKER_HOST_IP' with '$GEONODE_LB_HOST_IP' \n" + echo export DOCKER_HOST_IP=${GEONODE_LB_HOST_IP} >> /root/.override_env + +fi + +if [ ${GEONODE_LB_PORT} ] +then + + echo "GEONODE_LB_PORT is filled so I replace the value of '$PUBLIC_PORT' with '$GEONODE_LB_PORT' \n" + echo export PUBLIC_PORT=${GEONODE_LB_PORT} >> /root/.override_env + +fi + +if [ ! -z "${GEOSERVER_JAVA_OPTS}" ] +then + + echo "GEOSERVER_JAVA_OPTS is filled so I replace the value of '$JAVA_OPTS' with '$GEOSERVER_JAVA_OPTS' \n" + JAVA_OPTS=${GEOSERVER_JAVA_OPTS} + +fi + +# control the value of NGINX_BASE_URL variable +if [ -z `echo ${NGINX_BASE_URL} | sed 's/http:\/\/\([^:]*\).*/\1/'` ] +then + echo "NGINX_BASE_URL is empty so I'll use the static nginx hostname \n" + # echo export NGINX_BASE_URL=`python3 /usr/local/tomcat/tmp/get_nginxhost_ip.py` >> /root/.override_env + # TODO rework get_nginxhost_ip to get URL with static hostname from nginx service name + # + exposed port of that container i.e. http://geonode:80 + echo export NGINX_BASE_URL=http://geonode:80 >> /root/.override_env + echo "The calculated value is now NGINX_BASE_URL='$NGINX_BASE_URL' \n" +else + echo "NGINX_BASE_URL is filled so I'll leave the found value '$NGINX_BASE_URL' \n" +fi + +# set basic tagname +TAGNAME=( "baseUrl" ) + +if ! [ -f ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/config.xml ] +then + + echo "Configuration file '$GEOSERVER_DATA_DIR'/security/auth/geonodeAuthProvider/config.xml is not available so it is gone to skip \n" + +else + + # backup geonodeAuthProvider config.xml + cp ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/config.xml ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/config.xml.orig + # run the setting script for geonodeAuthProvider + /usr/local/tomcat/tmp/set_geoserver_auth.sh ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/config.xml ${GEOSERVER_DATA_DIR}/security/auth/geonodeAuthProvider/ ${TAGNAME} > /dev/null 2>&1 + +fi + +# backup geonode REST role service config.xml +cp "${GEOSERVER_DATA_DIR}/security/role/geonode REST role service/config.xml" "${GEOSERVER_DATA_DIR}/security/role/geonode REST role service/config.xml.orig" +# run the setting script for geonode REST role service +/usr/local/tomcat/tmp/set_geoserver_auth.sh "${GEOSERVER_DATA_DIR}/security/role/geonode REST role service/config.xml" "${GEOSERVER_DATA_DIR}/security/role/geonode REST role service/" ${TAGNAME} > /dev/null 2>&1 + +# set oauth2 filter tagname +TAGNAME=( "accessTokenUri" "userAuthorizationUri" "redirectUri" "checkTokenEndpointUrl" "logoutUri" ) + +# backup geonode-oauth2 config.xml +cp ${GEOSERVER_DATA_DIR}/security/filter/geonode-oauth2/config.xml ${GEOSERVER_DATA_DIR}/security/filter/geonode-oauth2/config.xml.orig +# run the setting script for geonode-oauth2 +/usr/local/tomcat/tmp/set_geoserver_auth.sh ${GEOSERVER_DATA_DIR}/security/filter/geonode-oauth2/config.xml ${GEOSERVER_DATA_DIR}/security/filter/geonode-oauth2/ "${TAGNAME[@]}" > /dev/null 2>&1 + +# set global tagname +TAGNAME=( "proxyBaseUrl" ) + +# backup global.xml +cp ${GEOSERVER_DATA_DIR}/global.xml ${GEOSERVER_DATA_DIR}/global.xml.orig +# run the setting script for global configuration +/usr/local/tomcat/tmp/set_geoserver_auth.sh ${GEOSERVER_DATA_DIR}/global.xml ${GEOSERVER_DATA_DIR}/ ${TAGNAME} > /dev/null 2>&1 + +# set correct amqp broker url +sed -i -e 's/localhost/rabbitmq/g' ${GEOSERVER_DATA_DIR}/notifier/notifier.xml + +# exclude wrong dependencies +sed -i -e 's/xom-\*\.jar/xom-\*\.jar,bcprov\*\.jar/g' /usr/local/tomcat/conf/catalina.properties + +# J2 templating for this docker image we should also do it for other configuration files in /usr/local/tomcat/tmp + +declare -a geoserver_datadir_template_dirs=("geofence") + +for template in in ${geoserver_datadir_template_dirs[*]}; do + #Geofence templates + if [ "$template" == "geofence" ]; then + cp -R /templates/$template/* ${GEOSERVER_DATA_DIR}/geofence + + for f in $(find ${GEOSERVER_DATA_DIR}/geofence/ -type f -name "*.j2"); do + echo -e "Evaluating template\n\tSource: $f\n\tDest: ${f%.j2}" + /usr/local/bin/j2 $f > ${f%.j2} + rm -f $f + done + + fi +done + +# configure CORS (inspired by https://github.com/oscarfonts/docker-geoserver) +# if enabled, this will add the filter definitions +# to the end of the web.xml +# (this will only happen if our filter has not yet been added before) +if [ "${GEOSERVER_CORS_ENABLED}" = "true" ] || [ "${GEOSERVER_CORS_ENABLED}" = "True" ]; then + if ! grep -q DockerGeoServerCorsFilter "$CATALINA_HOME/webapps/geoserver/WEB-INF/web.xml"; then + echo "Enable CORS for $CATALINA_HOME/webapps/geoserver/WEB-INF/web.xml" + sed -i "\::i\\ + \n\ + DockerGeoServerCorsFilter\n\ + org.apache.catalina.filters.CorsFilter\n\ + \n\ + cors.allowed.origins\n\ + ${GEOSERVER_CORS_ALLOWED_ORIGINS}\n\ + \n\ + \n\ + cors.allowed.methods\n\ + ${GEOSERVER_CORS_ALLOWED_METHODS}\n\ + \n\ + \n\ + cors.allowed.headers\n\ + ${GEOSERVER_CORS_ALLOWED_HEADERS}\n\ + \n\ + \n\ + \n\ + DockerGeoServerCorsFilter\n\ + /*\n\ + " "$CATALINA_HOME/webapps/geoserver/WEB-INF/web.xml"; + fi +fi + +# start tomcat +exec env JAVA_OPTS="${JAVA_OPTS}" catalina.sh run \ No newline at end of file diff --git a/scripts/docker/geoserver/get_dockerhost_ip.py b/scripts/docker/geoserver/get_dockerhost_ip.py new file mode 100644 index 00000000000..7b5a42ed310 --- /dev/null +++ b/scripts/docker/geoserver/get_dockerhost_ip.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 + +import logging + +import docker + +BOOTSTRAP_IMAGE_CHEIP = 'codenvy/che-ip:nightly' +# AF: why call before definition? print _docker_host_ip() + +def _docker_host_ip(): + client = docker.from_env() + ip_list = client.containers.run(BOOTSTRAP_IMAGE_CHEIP, + network_mode='host' + ).split("\n") + if len(ip_list) > 1: + logging.info("Docker daemon is running on more than one \ +address {0}".format(ip_list)) + logging.info("Only the first address:{0} will be returned!".format( + ip_list[0] + )) + else: + logging.info("Docker daemon is running at the following \ +address {0}".format(ip_list[0])) + return ip_list[0] diff --git a/scripts/docker/geoserver/get_nginxhost_ip.py b/scripts/docker/geoserver/get_nginxhost_ip.py new file mode 100644 index 00000000000..c6a67d8490d --- /dev/null +++ b/scripts/docker/geoserver/get_nginxhost_ip.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 + +import logging +import os + +import docker + +client = docker.from_env() +# print(client.info()) +# TODO avoid this script can fail and fall in the loop where the geoserver +# service is not available and consequently the nginx too which has geoserver +# as a reference link +for network in client.networks.list(): + if 'geonode' in network.name: + geonode_network = network.name + else: + geonode_network = 'geonode_default' + +try: + containers = { + c.attrs['Config']['Image']: c.attrs['NetworkSettings']['\ +Networks'][geonode_network]['\ +IPAddress'] for c in client.containers.list() if c.status in 'running' + } + for item in containers.items(): + if "geonode/nginx" in item[0]: + ipaddr = item[1] + + try: + os.environ["NGINX_BASE_URL"] = "http://" + ipaddr + ":" + "80" + nginx_base_url = "http://{}:80".format(ipaddr) + except NameError as er: + logging.info("NGINX container is not running maybe exited! Running\ +containers are:{0}".format(containers)) +except KeyError as ke: + logging.info("There has been a problem with the docker\ +network which has raised the following exception: {0}".format(ke)) +else: + # nginx_base_url = None + pass +finally: + try: + print(nginx_base_url) + except NameError as ne: + print("http://geonode:80") diff --git a/scripts/docker/geoserver/multidump-alt.sh b/scripts/docker/geoserver/multidump-alt.sh new file mode 100644 index 00000000000..cc237e17bec --- /dev/null +++ b/scripts/docker/geoserver/multidump-alt.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +if [ $# -ne 3 ]; then + echo "Usage: $0 pid interval count" + exit 1 +fi + +PID=$1 +INTERVAL=$2 +COUNT=$3 + +top -bH -d $INTERVAL -n $COUNT -p $PID >> top.out 2>&1 & +for i in `seq $COUNT`; do + kill -3 $PID + sleep $INTERVAL +done diff --git a/scripts/docker/geoserver/multidump.sh b/scripts/docker/geoserver/multidump.sh new file mode 100644 index 00000000000..21dfd2ba660 --- /dev/null +++ b/scripts/docker/geoserver/multidump.sh @@ -0,0 +1,18 @@ +#!/bin/sh + +if [ $# -ne 3 ]; then + echo "Usage: $0 pid interval count" + exit 1 +fi + +PID=$1 +INTERVAL=$2 +COUNT=$3 + +top -bH -d $INTERVAL -n $COUNT -p $PID >> top.out 2>&1 & +for i in `seq $COUNT`; do + echo "stack trace $i of $COUNT" >> jstack.out + jstack -l $PID >> jstack.out + echo "--------------------" >> jstack.out + sleep $INTERVAL +done diff --git a/scripts/docker/geoserver/requirements.txt b/scripts/docker/geoserver/requirements.txt new file mode 100644 index 00000000000..0b31242fdae --- /dev/null +++ b/scripts/docker/geoserver/requirements.txt @@ -0,0 +1 @@ +docker==3.1.1 diff --git a/scripts/docker/geoserver/set_geoserver_auth.sh b/scripts/docker/geoserver/set_geoserver_auth.sh new file mode 100644 index 00000000000..27dd11ef54e --- /dev/null +++ b/scripts/docker/geoserver/set_geoserver_auth.sh @@ -0,0 +1,91 @@ +#!/bin/bash + +auth_conf_source="$1" +auth_conf_target="$2" +# Creating a temporary file for sed to write the changes to +temp_file="xml.tmp" +touch $temp_file + +source /root/.bashrc +source /root/.override_env + +test -z "$auth_conf_source" && echo "You must specify a source file" && exit 1 +test -z "$auth_conf_target" && echo "You must specify a target conf directory" && exit 1 + +test ! -f "$auth_conf_source" && echo "Source $auth_conf_source does not exist or is not a file" && exit 1 +test ! -d "$auth_conf_target" && echo "Target directory $auth_conf_target does not exist or is not a directory" && exit 1 + +# for debugging +echo -e "NGINX_BASE_URL=${NGINX_BASE_URL}\n" +if [ "$PUBLIC_PORT" == "443" ]; then + SUBSTITUTION_URL="https://${DOCKER_HOST_IP}" + if [ "$PUBLIC_PORT" != "443" ]; then + SUBSTITUTION_URL="https://${DOCKER_HOST_IP}:${PUBLIC_PORT}" + fi +else + SUBSTITUTION_URL="http://${DOCKER_HOST_IP}" + if [ "$PUBLIC_PORT" != "80" ]; then + SUBSTITUTION_URL="http://${DOCKER_HOST_IP}:${PUBLIC_PORT}" + fi +fi + +echo -e "SUBSTITUTION_URL=$SUBSTITUTION_URL\n" +echo -e "auth_conf_source=$auth_conf_source\n" +echo -e "auth_conf_target=$auth_conf_target\n" + +# Elegance is the key -> adding an empty last line for Mr. “sed” to pick up +echo " " >> "$auth_conf_source" + +cat "$auth_conf_source" + +tagname=( ${@:3:5} ) + +# for debugging +for i in "${tagname[@]}" +do + echo "tagname=<$i>" +done + +echo "DEBUG: Starting... [Ok]\n" + +for i in "${tagname[@]}" +do + echo "DEBUG: Working on '$auth_conf_source' for tagname <$i>" + # Extracting the value from the <$tagname> element + # echo -ne "<$i>$tagvalue" | xmlstarlet sel -t -m "//a" -v . -n + tagvalue=`grep "<$i>.*<.$i>" "$auth_conf_source" | sed -e "s/^.*<$i/<$i/" | cut -f2 -d">"| cut -f1 -d"<"` + + echo "DEBUG: Found the current value for the element <$i> - '$tagvalue'" + + # Setting new substituted value + case $i in + proxyBaseUrl ) + if [ ${GEONODE_LB_HOST_IP} ] + then + echo "DEBUG: Editing '$auth_conf_source' for tagname <$i> and replacing its value with '$SUBSTITUTION_URL'" + newvalue=`echo -ne "$tagvalue" | sed -re "s@http://localhost(:8.*0)@$SUBSTITUTION_URL@"` + else + echo "DEBUG: Editing '$auth_conf_source' for tagname <$i> and replacing its value with '$NGINX_BASE_URL'" + newvalue=`echo -ne "$tagvalue" | sed -re "s@http://localhost(:8.*0)@$NGINX_BASE_URL@"` + fi;; + accessTokenUri | checkTokenEndpointUrl | baseUrl ) + echo "DEBUG: Editing '$auth_conf_source' for tagname <$i> and replacing its value with '$NGINX_BASE_URL'" + newvalue=`echo -ne "$tagvalue" | sed -re "s@http://localhost(:8.*0)@$NGINX_BASE_URL@"`;; + userAuthorizationUri | redirectUri | logoutUri ) + echo "DEBUG: Editing '$auth_conf_source' for tagname <$i> and replacing its value with '$SUBSTITUTION_URL'" + newvalue=`echo -ne "$tagvalue" | sed -re "s@http://localhost(:8.*0)@$SUBSTITUTION_URL@"`;; + *) echo -n "an unknown variable has been found";; + esac + + echo "DEBUG: Found the new value for the element <$i> - '$newvalue'" + # Replacing element’s value with $SUBSTITUTION_URL + # echo -ne "<$i>$tagvalue" | xmlstarlet sel -t -m "//a" -v . -n + sed -e "s@<$i>$tagvalue<\/$i>@<$i>$newvalue<\/$i>@g" "$auth_conf_source" > "$temp_file" + cp "$temp_file" "$auth_conf_source" +done +# Writing our changes back to the original file ($auth_conf_source) +# no longer needed +# mv $temp_file $auth_conf_source + +echo "DEBUG: Finished... [Ok] --- Final xml file is \n" +cat "$auth_conf_source" diff --git a/scripts/docker/geoserver/setup_auth.sh b/scripts/docker/geoserver/setup_auth.sh new file mode 100644 index 00000000000..6f9373b978c --- /dev/null +++ b/scripts/docker/geoserver/setup_auth.sh @@ -0,0 +1,3 @@ +#!/bin/sh +sed -i.bak 's@\([^<][^<]*\)@'"$DJANGO_URL"'@'\ + /geoserver_data/data/security/auth/geonodeAuthProvider/config.xml \ No newline at end of file diff --git a/scripts/docker/geoserver/templates/geofence/geofence-datasource-ovr.properties.j2 b/scripts/docker/geoserver/templates/geofence/geofence-datasource-ovr.properties.j2 new file mode 100644 index 00000000000..7b18d3e55f3 --- /dev/null +++ b/scripts/docker/geoserver/templates/geofence/geofence-datasource-ovr.properties.j2 @@ -0,0 +1,12 @@ +geofenceVendorAdapter.databasePlatform=org.hibernatespatial.postgis.PostgisDialect +geofenceDataSource.driverClassName=org.postgresql.Driver +geofenceDataSource.url=jdbc:postgresql://{{ DATABASE_HOST }}:{{ DATABASE_PORT }}/{{ GEONODE_GEODATABASE }} +geofenceDataSource.username={{ GEONODE_GEODATABASE }} +geofenceDataSource.password={{ GEONODE_GEODATABASE_PASSWORD }} +geofenceEntityManagerFactory.jpaPropertyMap[hibernate.default_schema]={{ GEONODE_GEODATABASE_SCHEMA }} + +# avoid hibernate transaction issues +geofenceDataSource.testOnBorrow=true +geofenceDataSource.validationQuery=SELECT 1 +geofenceEntityManagerFactory.jpaPropertyMap[hibernate.testOnBorrow]=true +geofenceEntityManagerFactory.jpaPropertyMap[hibernate.validationQuery]=SELECT 1 \ No newline at end of file diff --git a/setup.py b/setup.py index 498d391199e..86f10350f9e 100644 --- a/setup.py +++ b/setup.py @@ -28,6 +28,7 @@ setup( version=__import__("geonode").get_version(), long_description=open("README.md").read(), + long_description_content_type='text/markdown', package_data={ "": ["*.*"], # noqa "": ["static/*.*"], # noqa @@ -35,4 +36,8 @@ "": ["templates/*.*"], # noqa "templates": ["*.*"], }, + exclude_package_data={ + "": ["uploaded/*.*"], # noqa + "uploaded": ["*.*"], + } )