From e76b483dfc076f13efdf3fa7427c5d15527e7b81 Mon Sep 17 00:00:00 2001 From: gabriel-farache Date: Wed, 2 Aug 2023 14:35:05 +0200 Subject: [PATCH 1/2] Run Clair security analysis on each push --- .github/workflows/push.yml | 9 +++++ .github/workflows/test.yml | 28 ++++++++++++++ .gitignore | 5 +++ Makefile | 77 ++++++++++++++++++++++++++++++++++++++ 4 files changed, 119 insertions(+) diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index cd3d6f76e..35bcb25c7 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -18,6 +18,11 @@ jobs: distribution: 'temurin' java-version: '17' + - name: Setup Go environment + uses: actions/setup-go@v4.0.1 + with: + go-version: 'stable' + - name: Maven Verify run: mvn clean verify @@ -31,6 +36,10 @@ jobs: make build-images GIT_BRANCH=${GITHUB_REF##*/} make tag-images GIT_BRANCH=${GITHUB_REF##*/} + - name: "Security analysis of images" + run: | + make analyse-images stop-local-registry stop-clair + - name: Login to Docker Hub uses: docker/login-action@v2 with: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 6d237a08b..f0765ba44 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -224,3 +224,31 @@ jobs: uses: skjolber/maven-cache-github-action@v1 with: step: save + + security-checks: + runs-on: ubuntu-latest + needs: + - containers + steps: + - uses: actions/checkout@v3 + + - name: Setup Go environment + uses: actions/setup-go@v4.0.1 + with: + go-version: 'stable' + + - name: Download images + uses: ishworkh/docker-image-artifact-download@v1 + with: + image: "docker-compose_notification-service:latest" + + - name: Download images + uses: ishworkh/docker-image-artifact-download@v1 + with: + image: "docker-compose_workflow-service:latest" + + - name: "Security analysis of images" + run: | + make analyse-images stop-local-registry stop-clair + + diff --git a/.gitignore b/.gitignore index b78ec35aa..4b9a932f0 100644 --- a/.gitignore +++ b/.gitignore @@ -83,3 +83,8 @@ notification-service-sdk/.openapi-generator ############################## manifests.yaml hack/manifests/release + +############################## +## Security checks +############################## +clair diff --git a/Makefile b/Makefile index d1fc67574..6f47c7621 100644 --- a/Makefile +++ b/Makefile @@ -2,6 +2,8 @@ DOCKER ?= docker DOCKER-COMPOSE ?= docker-compose +CLAIR_TMP_DIR := . + TESTDBPASS := parodos TESTDBNAME := parodos TESTDBUSER := parodos @@ -147,6 +149,81 @@ tag-images: ## Tag docker images with git hash and branch name $(DOCKER) tag docker-compose_workflow-service:latest $(ORG)$(WORKFLOW_SERVICE_IMAGE):$(GIT_BRANCH) $(DOCKER) tag docker-compose_notification-service:latest $(ORG)$(NOTIFICATION_SERVICE_IMAGE):$(GIT_BRANCH) +deploy-local-registry: +ifneq ($(shell $(DOCKER) container inspect -f '{{.State.Running}}' registry 2> /dev/null), true) + $(DOCKER) run -d -p 5000:5000 --restart=always --name registry registry:2 +endif + $(DOCKER) tag docker-compose_workflow-service:latest localhost:5000/docker-compose_workflow-service:latest + $(DOCKER) tag docker-compose_notification-service:latest localhost:5000/docker-compose_notification-service:latest + $(DOCKER) push localhost:5000/docker-compose_workflow-service:latest + $(DOCKER) push localhost:5000/docker-compose_notification-service:latest + +stop-local-registry: + $(DOCKER) stop registry + $(DOCKER) rm registry + +deploy-clair: deploy-local-registry + cd $(CLAIR_TMP_DIR) ; git clone https://github.com/quay/clair ; cd clair; $(DOCKER) compose up -d + sleep 15m + @echo "Clair is up and running" + +stop-clair: + cd $(CLAIR_TMP_DIR)/clair ; $(DOCKER) compose down + +.PHONY: clairctl +CLAIRCTL = $(shell pwd)/bin/clairctl +clairctl: ## Download clairctl locally if necessary. +ifeq (,$(wildcard $(CLAIRCTL))) +ifeq (,$(shell which clairctl 2>/dev/null)) + @{ \ + go install github.com/quay/clair/v4/cmd/clairctl@latest ;\ + } +endif +CLAIRCTL = $(shell which clairctl) +endif + +run-images-analysis: clairctl + $(eval BRIDGE=$(shell $(DOCKER) network inspect -f '{{json .IPAM.Config}}' bridge | jq -r .[].Gateway)) + $(CLAIRCTL) --config $(CLAIR_TMP_DIR)/clair/local-dev/clair/config.yaml report -o json $(BRIDGE):5000/docker-compose_notification-service:latest | jq .vulnerabilities > $(CLAIR_TMP_DIR)/docker-compose_notification-service_report.json + $(CLAIRCTL) --config $(CLAIR_TMP_DIR)/clair/local-dev/clair/config.yaml report -o json $(BRIDGE):5000/docker-compose_workflow-service:latest | jq .vulnerabilities > $(CLAIR_TMP_DIR)/docker-compose_workflow-service_report.json + +.SILENT: analyse-images +analyse-images: deploy-clair run-images-analysis analyse-images-fast + # + +.SILENT: analyse-images-fast +analyse-images-fast: + $(eval FIXABLE_CRITICAL_NOTIFICATION?=$(shell cat $(CLAIR_TMP_DIR)/docker-compose_notification-service_report.json | jq '[.[] | select(.normalized_severity=="Critical") | select(.fixed_in_version!="")] | length')) + $(eval FIXABLE_HIGH_NOTIFICATION?=$(shell cat $(CLAIR_TMP_DIR)/docker-compose_notification-service_report.json | jq '[.[] | select(.normalized_severity=="High") | select(.fixed_in_version!="")] | length')) + $(eval FIXABLE_CRITICAL_WORKFLOW?=$(shell cat $(CLAIR_TMP_DIR)/docker-compose_workflow-service_report.json | jq '[.[] | select(.normalized_severity=="Critical") | select(.fixed_in_version!="")] | length')) + $(eval FIXABLE_HIGH_WORKFLOW?=$(shell cat $(CLAIR_TMP_DIR)/docker-compose_workflow-service_report.json | jq '[.[] | select(.normalized_severity=="High") | select(.fixed_in_version!="")] | length')) + $(eval CRITICAL_NOTIFICATION?=$(shell cat $(CLAIR_TMP_DIR)/docker-compose_notification-service_report.json |jq '[.[] | select(.normalized_severity=="Critical")] | length')) + $(eval HIGH_NOTIFICATION?=$(shell cat $(CLAIR_TMP_DIR)/docker-compose_notification-service_report.json | jq '[.[] | select(.normalized_severity=="High")] | length')) + $(eval CRITICAL_WORKFLOW?=$(shell cat $(CLAIR_TMP_DIR)/docker-compose_workflow-service_report.json | jq '[.[] | select(.normalized_severity=="Critical")] | length')) + $(eval HIGH_WORKFLOW?=$(shell cat $(CLAIR_TMP_DIR)/docker-compose_workflow-service_report.json | jq '[.[] | select(.normalized_severity=="High")] | length')) + + $(eval FIXABLE_CRITICAL_NOTIFICATION=$(shell [ $(FIXABLE_CRITICAL_NOTIFICATION) > 0 ] && echo "$(FIXABLE_CRITICAL_NOTIFICATION)" || echo "0")) + $(eval FIXABLE_HIGH_NOTIFICATION=$(shell [ $(FIXABLE_HIGH_NOTIFICATION) > 0 ] && echo "$(FIXABLE_HIGH_NOTIFICATION)" || echo "0")) + $(eval FIXABLE_CRITICAL_WORKFLOW=$(shell [ $(FIXABLE_CRITICAL_WORKFLOW) > 0 ] && echo "$(FIXABLE_CRITICAL_WORKFLOW)" || echo "0")) + $(eval FIXABLE_HIGH_WORKFLOW=$(shell [ $(FIXABLE_HIGH_WORKFLOW) > 0 ] && echo "$(FIXABLE_HIGH_WORKFLOW)" || echo "0")) + + echo -e "Notification service: \n\t$(CRITICAL_NOTIFICATION) critical issues found; $(FIXABLE_CRITICAL_NOTIFICATION) fixable\n\t$(HIGH_NOTIFICATION) high issues found; $(FIXABLE_HIGH_NOTIFICATION) fixable\n\ +Workflow service:\n\t$(CRITICAL_WORKFLOW) critical issues found; $(FIXABLE_CRITICAL_WORKFLOW) fixable\n\t$(HIGH_WORKFLOW) high issues found; $(FIXABLE_HIGH_WORKFLOW) fixable\n" + if [ "${FIXABLE_CRITICAL_NOTIFICATION}" -gt 0 ] ; then \ + echo "$(FIXABLE_CRITICAL_NOTIFICATION) fixable critical issues found for notification service" ; \ + exit 1 ; \ + elif [ "${FIXABLE_CRITICAL_WORKFLOW}" -gt 0 ] ; then \ + echo "$(FIXABLE_CRITICAL_WORKFLOW) fixable critical issues found for workflow service" ; \ + exit 1 ; \ + elif [ "${FIXABLE_HIGH_NOTIFICATION}" -gt 0 ] ; then \ + echo "$(FIXABLE_HIGH_NOTIFICATION) fixable high issues found for notification service" ; \ + exit 1 ; \ + elif [ "${FIXABLE_HIGH_WORKFLOW}" -gt 0 ] ; then \ + echo "$(FIXABLE_HIGH_WORKFLOW) fixable high issues found for workflow service" ; \ + exit 1 ; \ + fi + @echo "Remember to clean $(CLAIR_TMP_DIR)!" + push-images: ## Push docker images to quay.io registry $(eval TAG?=$(GIT_HASH)) $(DOCKER) push $(ORG)$(WORKFLOW_SERVICE_IMAGE):$(TAG) From 8bef7c69371bcd2589c431328ffdd4ce153fb136 Mon Sep 17 00:00:00 2001 From: gabriel-farache Date: Thu, 3 Aug 2023 20:49:01 +0200 Subject: [PATCH 2/2] Fix Critical and High vulnerabilities --- notification-service/pom.xml | 1 + .../security/SecurityConfiguration.java | 5 +++-- pom.xml | 3 ++- workflow-service/pom.xml | 17 +++++++++++++++++ .../parodos/security/SecurityConfiguration.java | 9 ++++++--- 5 files changed, 29 insertions(+), 6 deletions(-) diff --git a/notification-service/pom.xml b/notification-service/pom.xml index 4b610bc32..93d3b2725 100644 --- a/notification-service/pom.xml +++ b/notification-service/pom.xml @@ -100,6 +100,7 @@ com.h2database h2 + ${h2.version} diff --git a/notification-service/src/main/java/com/redhat/parodos/notification/security/SecurityConfiguration.java b/notification-service/src/main/java/com/redhat/parodos/notification/security/SecurityConfiguration.java index 059d7e4b2..4295acd2e 100644 --- a/notification-service/src/main/java/com/redhat/parodos/notification/security/SecurityConfiguration.java +++ b/notification-service/src/main/java/com/redhat/parodos/notification/security/SecurityConfiguration.java @@ -31,6 +31,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.stereotype.Component; /** @@ -64,9 +65,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { // @formatter:off http .authorizeHttpRequests(auth ->auth - .requestMatchers(HttpMethod.OPTIONS, "/**") + .requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.OPTIONS.name())) .permitAll() - .requestMatchers("/api/**") + .requestMatchers(new AntPathRequestMatcher("/api/**")) .fullyAuthenticated() .anyRequest().permitAll()) .httpBasic(Customizer.withDefaults()) diff --git a/pom.xml b/pom.xml index 1a3d16910..405452a39 100644 --- a/pom.xml +++ b/pom.xml @@ -55,7 +55,7 @@ 1.5 3.2.1 3.3.0 - 3.1.0 + 3.1.2 0.0.34 5.10.0 2.2 @@ -68,6 +68,7 @@ 2022.0.3 1.8.5 2.0 + 2.2.220 workflow-engine diff --git a/workflow-service/pom.xml b/workflow-service/pom.xml index 76c50f855..c9e327df6 100644 --- a/workflow-service/pom.xml +++ b/workflow-service/pom.xml @@ -76,6 +76,22 @@ org.springframework.security spring-security-ldap + + org.codehaus.plexus + plexus-archiver + 4.8.0 + + + + commons-fileupload + commons-fileupload + 1.5 + + + org.codehaus.jettison + jettison + 1.5.4 + org.springframework.ldap spring-ldap-core @@ -97,6 +113,7 @@ com.h2database h2 + ${h2.version} org.postgresql diff --git a/workflow-service/src/main/java/com/redhat/parodos/security/SecurityConfiguration.java b/workflow-service/src/main/java/com/redhat/parodos/security/SecurityConfiguration.java index 8e9aadabf..098106df5 100644 --- a/workflow-service/src/main/java/com/redhat/parodos/security/SecurityConfiguration.java +++ b/workflow-service/src/main/java/com/redhat/parodos/security/SecurityConfiguration.java @@ -32,6 +32,7 @@ import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder; import org.springframework.security.ldap.userdetails.InetOrgPersonContextMapper; import org.springframework.security.web.SecurityFilterChain; +import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.stereotype.Component; /** @@ -70,12 +71,14 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth -> auth - .requestMatchers(HttpMethod.OPTIONS, "/**") + .requestMatchers(new AntPathRequestMatcher("/**", HttpMethod.OPTIONS.name())) .permitAll() - .requestMatchers("/api/**", "/actuator/shutdown") + .requestMatchers(new AntPathRequestMatcher("/api/**")) + .fullyAuthenticated() + .requestMatchers(new AntPathRequestMatcher("/actuator/shutdown")) .fullyAuthenticated() .anyRequest().permitAll()) - .httpBasic(Customizer.withDefaults()) + .httpBasic(Customizer.withDefaults()) .headers(httpSecurityHeadersConfigurer -> httpSecurityHeadersConfigurer.frameOptions(HeadersConfigurer.FrameOptionsConfig::disable)) .formLogin(form -> form.loginProcessingUrl("/login")) .logout(httpSecurityLogoutConfigurer -> httpSecurityLogoutConfigurer