Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Provide native image builds #27

Draft
wants to merge 8 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
91 changes: 90 additions & 1 deletion .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,23 @@ on:
branches: ["main"]

jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
cache: "maven"
- name: Run test with Maven
run: |
mvn -B -ntp test

build-maven:
runs-on: ubuntu-latest
needs: test
permissions:
packages: write
steps:
Expand Down Expand Up @@ -41,4 +56,78 @@ jobs:
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
export IMAGE_TAGS="${IMAGE_RAW_TAGS//$'\n'/,}"
mvn -B -ntp -P build-image,publish verify
mvn -B -ntp -P ci,build-image,publish package

build-maven-native:
runs-on: ubuntu-latest
needs: test
permissions:
packages: write
steps:
- uses: actions/checkout@v4
- name: Inject slug/short variables
uses: rlespinasse/github-slug-action@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
cache: "maven"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha
type=ref,event=branch
- name: Build with Maven
env:
IMAGE_NAME: ghcr.io/${{ github.repository }}-native
IMAGE_RAW_TAGS: ${{ steps.meta.outputs.tags }}
REGISTRY_USER: ${{ github.repository_owner }}
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
export IMAGE_TAGS="${IMAGE_RAW_TAGS//$'\n'/,}"
mvn -B -ntp -P ci,native,build-image,publish package

build-maven-native-keycloak:
runs-on: ubuntu-latest
needs: test
permissions:
packages: write
steps:
- uses: actions/checkout@v4
- name: Inject slug/short variables
uses: rlespinasse/github-slug-action@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: "17"
distribution: "temurin"
cache: "maven"
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Extract metadata (tags, labels) for Docker
id: meta
uses: docker/metadata-action@v5
with:
images: ghcr.io/${{ github.repository }}
tags: |
type=sha
type=ref,event=branch
- name: Build with Maven
env:
IMAGE_NAME: ghcr.io/${{ github.repository }}-native-keycloak
IMAGE_RAW_TAGS: ${{ steps.meta.outputs.tags }}
REGISTRY_USER: ${{ github.repository_owner }}
REGISTRY_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
export IMAGE_TAGS="${IMAGE_RAW_TAGS//$'\n'/,}"
mvn -B -ntp -P ci,native,keycloak,build-image,publish package
2 changes: 1 addition & 1 deletion .github/workflows/release.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ jobs:
run: |
export IMAGE_TAGS="${IMAGE_RAW_TAGS//$'\n'/,}"
if [[ $LATEST_TAG == "y" ]]; then export IMAGE_TAGS=$IMAGE_TAGS,ghcr.io/it-at-m/appswitcher-server:latest; fi
mvn -B -ntp -DskipTests -P build-image,publish verify
mvn -B -ntp -DskipTests -P ci,build-image,publish verify
- name: Create GitHub Release
id: create_release
uses: softprops/action-gh-release@v2
Expand Down
57 changes: 55 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<image.name>itatm/${project.artifactId}:${project.version}</image.name>
<image.name>itatm/${project.artifactId}</image.name>
</properties>

<dependencies>
Expand Down Expand Up @@ -115,6 +115,14 @@
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<profiles>demo</profiles>
</configuration>
</plugin>
<!-- native image building -->
<plugin>
<groupId>org.graalvm.buildtools</groupId>
<artifactId>native-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
Expand Down Expand Up @@ -176,6 +184,7 @@
<goals>
<goal>check</goal>
</goals>
<phase>test</phase>
</execution>
</executions>
</plugin>
Expand Down Expand Up @@ -211,6 +220,7 @@
<goals>
<goal>check</goal>
</goals>
<phase>test</phase>
</execution>
</executions>
</plugin>
Expand Down Expand Up @@ -247,10 +257,33 @@

<profiles>
<profile>
<id>build-image</id>
<id>lhm</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<configuration>
<docker>
<builderRegistry>
<username>${env.DOCKERHUB_USERNAME}</username>
<password>${env.DOCKERHUB_PASSWORD}</password>
<url>https://index.docker.io/v1/</url>
</builderRegistry>
</docker>
</configuration>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>ci</id>
<properties>
<image.name>${env.IMAGE_NAME}</image.name>
</properties>
</profile>
<profile>
<id>build-image</id>
<build>
<plugins>
<plugin>
Expand All @@ -264,6 +297,7 @@
<HTTPS_PROXY>${env.HTTPS_PROXY}</HTTPS_PROXY>
<NO_PROXY>${env.NO_PROXY}</NO_PROXY>
</env>
<verboseLogging>true</verboseLogging>
<tags>${env.IMAGE_TAGS}</tags>
<builder>
paketobuildpacks/builder-jammy-base:latest</builder>
Expand All @@ -281,6 +315,25 @@
</plugins>
</build>
</profile>
<profile>
<id>keycloak</id>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<id>process-aot</id>
<configuration>
<profiles>keycloak</profiles>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>
</profile>
<profile>
<id>publish</id>
<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,18 +22,28 @@
*/
package de.muenchen.oss.appswitcher;

import java.util.LinkedHashMap;
import java.util.Map;

import org.apache.tomcat.util.http.Rfc6265CookieProcessor;
import org.springframework.aot.hint.MemberCategory;
import org.springframework.aot.hint.RuntimeHints;
import org.springframework.aot.hint.RuntimeHintsRegistrar;
import org.springframework.aot.hint.TypeReference;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.embedded.tomcat.TomcatContextCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ImportRuntimeHints;
import org.springframework.context.annotation.Scope;
import org.springframework.context.annotation.ScopedProxyMode;
import org.springframework.web.context.WebApplicationContext;

import de.muenchen.oss.appswitcher.AppswitcherApplication.MyRuntimeHints;
import de.muenchen.oss.appswitcher.session.SessionBean;

@SpringBootApplication
@ImportRuntimeHints(value = { MyRuntimeHints.class })
public class AppswitcherApplication {

public static void main(String[] args) {
Expand All @@ -43,7 +53,7 @@ public static void main(String[] args) {
// Um Session Cookie auch als 3Party-Cookie in iframes zur Verfügung zu haben:
// https://stackoverflow.com/a/60860531
@Bean
public TomcatContextCustomizer sameSiteCookiesConfig() {
TomcatContextCustomizer sameSiteCookiesConfig() {
return context -> {
final Rfc6265CookieProcessor cookieProcessor = new Rfc6265CookieProcessor();
cookieProcessor.setSameSiteCookies("NONE");
Expand All @@ -57,8 +67,21 @@ public TomcatContextCustomizer sameSiteCookiesConfig() {
// Proxy to inject it into our singleton-scoped @Controller
proxyMode = ScopedProxyMode.TARGET_CLASS
)
public SessionBean todos() {
SessionBean todos() {
return new SessionBean();
}

static class MyRuntimeHints implements RuntimeHintsRegistrar {

@Override
public void registerHints(RuntimeHints hints, ClassLoader classLoader) {
// see https://github.com/spring-projects/spring-boot/issues/34206
hints.reflection().registerType(Map.class, MemberCategory.values());
hints.reflection().registerType(LinkedHashMap.class, MemberCategory.values());
hints.reflection().registerType(TypeReference.of("java.util.Map$Entry"), MemberCategory.values());
hints.reflection().registerType(TypeReference.of("java.util.LinkedHashMap$Entry"), MemberCategory.values());
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/
package de.muenchen.oss.appswitcher;

import java.util.LinkedHashMap;
import java.util.Map;

import org.springframework.boot.context.properties.ConfigurationProperties;
Expand All @@ -36,7 +37,7 @@
public class AppswitcherProperties {

@NestedConfigurationProperty
private Map<String, AppConfigurationProperties> apps;
private Map<String, AppConfigurationProperties> apps = new LinkedHashMap<>();

@NestedConfigurationProperty
private KeycloakConfigurationProperties keycloak;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,13 +28,16 @@
import org.springframework.context.annotation.Profile;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configurers.HeadersConfigurer.FrameOptionsConfig;
import org.springframework.security.oauth2.core.DelegatingOAuth2TokenValidator;
import org.springframework.security.oauth2.core.OAuth2TokenValidator;
import org.springframework.security.oauth2.jwt.Jwt;
import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder;
import org.springframework.security.web.SecurityFilterChain;

import static org.springframework.security.config.Customizer.withDefaults;

/**
* The central class for configuration of all security aspects.
*/
Expand All @@ -44,37 +47,34 @@

@Bean
@Profile("keycloak")
public SecurityFilterChain keycloakFilterChain(HttpSecurity http) throws Exception {
// @formatter:off
configureBase(http);
http
.authorizeHttpRequests()
.requestMatchers("/actuator/info", "/actuator/health","/actuator/health/**").permitAll()
.anyRequest().authenticated()
.and().oauth2Login();
// @formatter:on
SecurityFilterChain keycloakFilterChain(HttpSecurity http) throws Exception {
configureBase(http);
http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests
.requestMatchers("/actuator/info", "/actuator/health", "/actuator/health/**").permitAll().anyRequest()
.authenticated()).oauth2Login(withDefaults());
return http.build();
}

@Bean
@Profile("!keycloak")
public SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
SecurityFilterChain defaultFilterChain(HttpSecurity http) throws Exception {
configureBase(http);
http.authorizeHttpRequests().anyRequest().permitAll();
http.authorizeHttpRequests(authorizeHttpRequests -> authorizeHttpRequests.anyRequest().permitAll());
return http.build();
}

@Bean
@Profile("keycloak")
public JwtDecoder jwtDecoderByIssuerUri(@Value("${appswitcher.keycloak.jwk-set-uri}") String jwkSetUri) {
JwtDecoder jwtDecoderByIssuerUri(@Value("${appswitcher.keycloak.jwk-set-uri}") String jwkSetUri) {
NimbusJwtDecoder decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
OAuth2TokenValidator<Jwt> delegatingValidator = new DelegatingOAuth2TokenValidator<>();
decoder.setJwtValidator(delegatingValidator);
return decoder;
}

private void configureBase(HttpSecurity http) throws Exception {
http.csrf().disable().headers().frameOptions().disable().and().cors();
http.csrf(csrf -> csrf.disable()).headers(headers -> headers.frameOptions(FrameOptionsConfig::disable))

Check failure

Code scanning / CodeQL

Disabled Spring CSRF protection High

CSRF vulnerability due to protection being disabled.

Copilot Autofix AI 4 months ago

To fix the problem, we need to enable CSRF protection by removing the csrf.disable() call. This will ensure that CSRF tokens are required for state-changing requests, thereby protecting the application from CSRF attacks.

  • Remove the csrf.disable() call from the configureBase method.
  • Ensure that CSRF protection is enabled by default.
Suggested changeset 1
src/main/java/de/muenchen/oss/appswitcher/security/SecurityConfiguration.java

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/src/main/java/de/muenchen/oss/appswitcher/security/SecurityConfiguration.java b/src/main/java/de/muenchen/oss/appswitcher/security/SecurityConfiguration.java
--- a/src/main/java/de/muenchen/oss/appswitcher/security/SecurityConfiguration.java
+++ b/src/main/java/de/muenchen/oss/appswitcher/security/SecurityConfiguration.java
@@ -75,3 +75,3 @@
     private void configureBase(HttpSecurity http) throws Exception {
-        http.csrf(csrf -> csrf.disable()).headers(headers -> headers.frameOptions(FrameOptionsConfig::disable))
+        http.csrf(withDefaults()).headers(headers -> headers.frameOptions(FrameOptionsConfig::disable))
                 .cors(withDefaults());
EOF
@@ -75,3 +75,3 @@
private void configureBase(HttpSecurity http) throws Exception {
http.csrf(csrf -> csrf.disable()).headers(headers -> headers.frameOptions(FrameOptionsConfig::disable))
http.csrf(withDefaults()).headers(headers -> headers.frameOptions(FrameOptionsConfig::disable))
.cors(withDefaults());
Copilot is powered by AI and may make mistakes. Always verify output.
Positive Feedback
Negative Feedback

Provide additional feedback

Please help us improve GitHub Copilot by sharing more details about this comment.

Please select one or more of the options
.cors(withDefaults());
}

}