From 378c12694d46a24c0a74725d59971e1b3bb38609 Mon Sep 17 00:00:00 2001 From: vityaman Date: Tue, 7 Jan 2025 15:43:50 +0300 Subject: [PATCH 1/5] #68 Change TLS keys generation --- backend/.gitignore | 4 + .../authik/src/main/resources/application.yml | 2 + backend/config/crypto/ca.cnf | 14 +++ backend/config/crypto/csr.cnf | 23 ++++ backend/config/crypto/keys.bash | 116 ++++++++++++------ .../main/resources/application-foundation.yml | 2 +- .../src/main/resources/application.yml | 2 +- .../src/main/resources/application.yml | 2 + .../people/src/main/resources/application.yml | 2 + 9 files changed, 129 insertions(+), 38 deletions(-) create mode 100644 backend/config/crypto/ca.cnf create mode 100644 backend/config/crypto/csr.cnf diff --git a/backend/.gitignore b/backend/.gitignore index 44a7f827..24e2a7cf 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -31,6 +31,10 @@ build/ # Secrets *.pem *.p12 +*.key +*.crt +*.csr +*.srl # Java *.hprof diff --git a/backend/authik/src/main/resources/application.yml b/backend/authik/src/main/resources/application.yml index 0aeff112..1cbde315 100644 --- a/backend/authik/src/main/resources/application.yml +++ b/backend/authik/src/main/resources/application.yml @@ -1,6 +1,8 @@ spring: config: import: application-foundation.yml + application: + name: authik datasource: url: jdbc:postgresql://authik-database:5432/${POSTGRES_DB} username: ${POSTGRES_USER} diff --git a/backend/config/crypto/ca.cnf b/backend/config/crypto/ca.cnf new file mode 100644 index 00000000..a1c241bf --- /dev/null +++ b/backend/config/crypto/ca.cnf @@ -0,0 +1,14 @@ +[req] +default_bits = 4096 +prompt = no +default_md = sha256 +distinguished_name = dn +req_extensions = req_ext + +[dn] +C = RU +ST = Saint-Petersburg +L = Saint-Petersburg +O = ITMO Dating +OU = IT +CN = ITMO Dating Root CA diff --git a/backend/config/crypto/csr.cnf b/backend/config/crypto/csr.cnf new file mode 100644 index 00000000..69cf6ed0 --- /dev/null +++ b/backend/config/crypto/csr.cnf @@ -0,0 +1,23 @@ +[req] +default_bits = 4096 +prompt = no +default_md = sha256 +distinguished_name = dn +req_extensions = req_ext + +[dn] +C = RU +ST = Saint-Petersburg +L = Saint-Petersburg +O = ITMO Dating +OU = IT +CN = dating.se.ifmo.ru + +[req_ext] +subjectAltName = @alt_names + +[alt_names] +DNS.1 = localhost +DNS.2 = authik +DNS.3 = matchmaker +DNS.4 = people \ No newline at end of file diff --git a/backend/config/crypto/keys.bash b/backend/config/crypto/keys.bash index 7ff282d1..8604eeee 100755 --- a/backend/config/crypto/keys.bash +++ b/backend/config/crypto/keys.bash @@ -7,54 +7,97 @@ ENV="$2" ALIAS="itmo-dating" ALIAS_BACKEND="$ALIAS-backend" -KEYSTORE="keystore.p12" +VALIDITY=1 BACKEND_INSTALL_PATH="foundation/src/main/resources/keystore" +#CONSUL_INSTALL_PATH="consul/config" GATEWAY_INSTALL_PATH="gateway/src/main/resources/keystore" PASSWORD="$ITMO_DATING_KEY_STORE_PASSWORD" +function generate() { + echo "Phase: Generate" + + echo "Generating the private key for CA certificate..." + openssl genpkey \ + -algorithm RSA \ + -out "$ALIAS_BACKEND-ca.key" 2> /dev/null + + echo "Generating the self-signed CA certificate..." + openssl req -x509 -new \ + -nodes \ + -days "$VALIDITY" \ + -config ca.cnf \ + -key "$ALIAS_BACKEND-ca.key" \ + -out "$ALIAS_BACKEND-ca.crt" + + echo "Generating the private key for services..." + openssl genpkey \ + -algorithm RSA \ + -out "$ALIAS_BACKEND.key" 2> /dev/null + + echo "Generating the Certificate Signing Request (CSR)..." + openssl req -new \ + -config csr.cnf \ + -key "$ALIAS_BACKEND.key" \ + -out "$ALIAS_BACKEND.csr" + + echo "Signing the CSR with self-signed CA to create a certificate..." + openssl x509 -req \ + -sha256 \ + -days "$VALIDITY" \ + -extfile csr.cnf -extensions req_ext \ + -CA "$ALIAS_BACKEND-ca.crt" \ + -CAkey "$ALIAS_BACKEND-ca.key" \ + -CAcreateserial \ + -in "$ALIAS_BACKEND.csr" \ + -out "$ALIAS_BACKEND.crt" + + echo "Packaging keys and certificates..." + openssl pkcs12 -export \ + -password pass:"$PASSWORD" \ + -certfile "$ALIAS_BACKEND-ca.crt" \ + -inkey "$ALIAS_BACKEND.key" \ + -in "$ALIAS_BACKEND.crt" \ + -out "$ALIAS_BACKEND.p12" +} + function copy() { - mkdir -p "../../$BACKEND_INSTALL_PATH" - mkdir -p "../../$GATEWAY_INSTALL_PATH" - cp "$1" "../../$BACKEND_INSTALL_PATH/$1" - cp "$1" "../../$GATEWAY_INSTALL_PATH/$1" + DIR="$1" + FILE="$2" + + mkdir -p "../../$DIR" + cp "$FILE" "../../$DIR/$FILE" } -function remove() { - rm -f "$1" "../../$BACKEND_INSTALL_PATH/$1" "../../$GATEWAY_INSTALL_PATH/$1" +function distribute() { + echo "Phase: Distribute" + + echo "Copying package to the backend..." + copy "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.p12" + + echo "Copying package to the gateway..." + copy "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.p12" } -function generate() { - echo "Generating the backend key pair keystore..." - keytool \ - -genkeypair \ - -alias "$ALIAS" \ - -keyalg RSA \ - -keysize 4096 \ - -validity 1 \ - -dname "CN=localhost" \ - -ext "san=dns:localhost,dns:authik,dns:matchmaker,dns:people" \ - -keypass "$PASSWORD" \ - -keystore "$KEYSTORE" \ - -storeType PKCS12 \ - -storepass "$PASSWORD" - - echo "Exporting the backend private key..." - openssl pkcs12 -in "$KEYSTORE" -nocerts -out "$ALIAS_BACKEND-private.pem" \ - -passin pass:"$PASSWORD" -passout pass:"$PASSWORD" - - echo "Exporting the backend public key..." - openssl pkcs12 -in "$KEYSTORE" -nokeys -out "$ALIAS_BACKEND-public.pem" \ - -passin pass:"$PASSWORD" -passout pass:"$PASSWORD" - - copy "$KEYSTORE" - copy "$ALIAS_BACKEND-private.pem" - copy "$ALIAS_BACKEND-public.pem" +function remove() { + DIR="$1" + FILE="$2" + + rm -rf "../../$DIR/$FILE" + rm -rf "$FILE" } function clear() { - remove "$KEYSTORE" - remove "$ALIAS_BACKEND-private.pem" - remove "$ALIAS_BACKEND-public.pem" + echo "Phase: Clear" + + echo "Removing package from the backend..." + remove "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.p12" + + echo "Removing package from the gateway..." + remove "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.p12" + + rm -rf "$ALIAS_BACKEND-ca.key" + rm -rf "$ALIAS_BACKEND-ca.crt" + rm -rf "$ALIAS_BACKEND.key" } if [ "$ENV" = "test" ]; then @@ -63,6 +106,7 @@ fi if [ "$MODE" = "generate" ]; then generate + distribute elif [ "$MODE" = "clean" ]; then clear else diff --git a/backend/foundation/src/main/resources/application-foundation.yml b/backend/foundation/src/main/resources/application-foundation.yml index ff925999..37190a4e 100644 --- a/backend/foundation/src/main/resources/application-foundation.yml +++ b/backend/foundation/src/main/resources/application-foundation.yml @@ -2,7 +2,7 @@ server: ssl: enabled: true key-store-type: PKCS12 - key-store: classpath:keystore/keystore.p12 + key-store: classpath:keystore/itmo-dating-backend.p12 key-store-password: ${KEY_STORE_PASSWORD} protocol: TLSv1.3 enabled-protocols: TLSv1.3 diff --git a/backend/gateway/src/main/resources/application.yml b/backend/gateway/src/main/resources/application.yml index 53ed32e9..92175076 100644 --- a/backend/gateway/src/main/resources/application.yml +++ b/backend/gateway/src/main/resources/application.yml @@ -2,7 +2,7 @@ server: ssl: enabled: true key-store-type: PKCS12 - key-store: classpath:keystore/keystore.p12 + key-store: classpath:keystore/itmo-dating-backend.p12 key-store-password: ${KEY_STORE_PASSWORD} protocol: TLSv1.3 enabled-protocols: TLSv1.3 diff --git a/backend/matchmaker/src/main/resources/application.yml b/backend/matchmaker/src/main/resources/application.yml index 03dc6d28..03439aec 100644 --- a/backend/matchmaker/src/main/resources/application.yml +++ b/backend/matchmaker/src/main/resources/application.yml @@ -1,6 +1,8 @@ spring: config: import: application-foundation.yml + application: + name: matchmaker datasource: url: jdbc:postgresql://matchmaker-database:5432/${POSTGRES_DB} username: ${POSTGRES_USER} diff --git a/backend/people/src/main/resources/application.yml b/backend/people/src/main/resources/application.yml index b8f9cb38..97a506b5 100644 --- a/backend/people/src/main/resources/application.yml +++ b/backend/people/src/main/resources/application.yml @@ -1,6 +1,8 @@ spring: config: import: application-foundation.yml + application: + name: people datasource: url: jdbc:postgresql://people-database:5432/${POSTGRES_DB} username: ${POSTGRES_USER} From e57afa7f195b1429b289f1950cbfd2acaf81565d Mon Sep 17 00:00:00 2001 From: vityaman Date: Tue, 7 Jan 2025 19:44:35 +0300 Subject: [PATCH 2/5] #68 Consul discovers backend services --- backend/.gitignore | 1 + backend/config/crypto/ca.cnf | 7 ++++ backend/config/crypto/csr.cnf | 3 +- backend/config/crypto/keys.bash | 36 ++++++++++++++++--- backend/consul/Dockerfile | 14 ++++++++ backend/consul/config/consul.hcl | 27 ++++++++++++++ backend/foundation/build.gradle.kts | 3 ++ .../se/dating/spring/SpringConfiguration.kt | 2 ++ .../spring/security/ssl/KeyStoreExtractor.kt | 36 +++++++++++++++++++ .../main/resources/application-foundation.yml | 28 +++++++++++++++ backend/gradle/libs.versions.toml | 2 ++ compose.yml | 19 ++++++---- 12 files changed, 167 insertions(+), 11 deletions(-) create mode 100644 backend/consul/Dockerfile create mode 100644 backend/consul/config/consul.hcl create mode 100644 backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/security/ssl/KeyStoreExtractor.kt diff --git a/backend/.gitignore b/backend/.gitignore index 24e2a7cf..a6d69a58 100644 --- a/backend/.gitignore +++ b/backend/.gitignore @@ -35,6 +35,7 @@ build/ *.crt *.csr *.srl +*.jks # Java *.hprof diff --git a/backend/config/crypto/ca.cnf b/backend/config/crypto/ca.cnf index a1c241bf..2dac3cf1 100644 --- a/backend/config/crypto/ca.cnf +++ b/backend/config/crypto/ca.cnf @@ -4,6 +4,7 @@ prompt = no default_md = sha256 distinguished_name = dn req_extensions = req_ext +x509_extensions = v3_ca [dn] C = RU @@ -12,3 +13,9 @@ L = Saint-Petersburg O = ITMO Dating OU = IT CN = ITMO Dating Root CA + +[v3_ca] +subjectKeyIdentifier = hash +authorityKeyIdentifier = keyid:always,issuer +basicConstraints = critical, CA:true +keyUsage = critical, digitalSignature, cRLSign, keyCertSign diff --git a/backend/config/crypto/csr.cnf b/backend/config/crypto/csr.cnf index 69cf6ed0..d73b6b0c 100644 --- a/backend/config/crypto/csr.cnf +++ b/backend/config/crypto/csr.cnf @@ -20,4 +20,5 @@ subjectAltName = @alt_names DNS.1 = localhost DNS.2 = authik DNS.3 = matchmaker -DNS.4 = people \ No newline at end of file +DNS.4 = people +DNS.5 = server.dc1.consul diff --git a/backend/config/crypto/keys.bash b/backend/config/crypto/keys.bash index 8604eeee..d5c1ccee 100755 --- a/backend/config/crypto/keys.bash +++ b/backend/config/crypto/keys.bash @@ -9,7 +9,7 @@ ALIAS="itmo-dating" ALIAS_BACKEND="$ALIAS-backend" VALIDITY=1 BACKEND_INSTALL_PATH="foundation/src/main/resources/keystore" -#CONSUL_INSTALL_PATH="consul/config" +CONSUL_INSTALL_PATH="consul/config" GATEWAY_INSTALL_PATH="gateway/src/main/resources/keystore" PASSWORD="$ITMO_DATING_KEY_STORE_PASSWORD" @@ -45,19 +45,28 @@ function generate() { -sha256 \ -days "$VALIDITY" \ -extfile csr.cnf -extensions req_ext \ + -CAcreateserial \ -CA "$ALIAS_BACKEND-ca.crt" \ -CAkey "$ALIAS_BACKEND-ca.key" \ - -CAcreateserial \ -in "$ALIAS_BACKEND.csr" \ -out "$ALIAS_BACKEND.crt" echo "Packaging keys and certificates..." openssl pkcs12 -export \ -password pass:"$PASSWORD" \ - -certfile "$ALIAS_BACKEND-ca.crt" \ -inkey "$ALIAS_BACKEND.key" \ -in "$ALIAS_BACKEND.crt" \ + -certfile "$ALIAS_BACKEND-ca.crt" \ -out "$ALIAS_BACKEND.p12" + + echo "Converting PKCS12 to JKS..." + keytool -importkeystore \ + -srcstoretype PKCS12 \ + -srckeystore "$ALIAS_BACKEND.p12" \ + -srcstorepass "$PASSWORD" \ + -deststoretype JKS \ + -destkeystore "$ALIAS_BACKEND.jks" \ + -deststorepass "$PASSWORD" } function copy() { @@ -73,9 +82,16 @@ function distribute() { echo "Copying package to the backend..." copy "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.p12" + copy "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.jks" + copy "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.crt" echo "Copying package to the gateway..." copy "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.p12" + + echo "Copying keys to the consul..." + copy "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.key" + copy "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.crt" + copy "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND-ca.crt" } function remove() { @@ -91,13 +107,25 @@ function clear() { echo "Removing package from the backend..." remove "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.p12" + remove "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.jks" + remove "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.crt" echo "Removing package from the gateway..." remove "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.p12" + echo "Removing keys from the consul..." + remove "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.key" + remove "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.crt" + remove "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND-ca.crt" + + echo "Removing local outputs..." + rm -rf "$ALIAS_BACKEND.crt" + rm -rf "$ALIAS_BACKEND.csr" + rm -rf "$ALIAS_BACKEND.key" + rm -rf "$ALIAS_BACKEND.p12" rm -rf "$ALIAS_BACKEND-ca.key" rm -rf "$ALIAS_BACKEND-ca.crt" - rm -rf "$ALIAS_BACKEND.key" + rm -rf "$ALIAS_BACKEND-ca.srl" } if [ "$ENV" = "test" ]; then diff --git a/backend/consul/Dockerfile b/backend/consul/Dockerfile new file mode 100644 index 00000000..82b481c5 --- /dev/null +++ b/backend/consul/Dockerfile @@ -0,0 +1,14 @@ +FROM hashicorp/consul + +COPY ./config/itmo-dating-backend.key /consul/config/itmo-dating-backend.key +COPY ./config/itmo-dating-backend.crt /consul/config/itmo-dating-backend.crt +COPY ./config/itmo-dating-backend-ca.crt /consul/config/itmo-dating-backend-ca.crt +COPY ./config/consul.hcl /consul/config/consul.hcl + +COPY run.sh /consul/run.sh + +RUN chmod -R 755 /consul/config && \ + chmod 644 /consul/config/itmo-dating-backend.key && \ + chmod 644 /consul/config/itmo-dating-backend.crt && \ + chmod 644 /consul/config/itmo-dating-backend-ca.crt && \ + chmod 644 /consul/config/consul.hcl diff --git a/backend/consul/config/consul.hcl b/backend/consul/config/consul.hcl new file mode 100644 index 00000000..e8094fb1 --- /dev/null +++ b/backend/consul/config/consul.hcl @@ -0,0 +1,27 @@ +server = true +bind_addr = "0.0.0.0" +client_addr = "0.0.0.0" + +ports { + http = 8500 + https = 8501 + grpc = -1 +} + +acl { + enabled = true + default_policy = "allow" + enable_token_persistence = true +} + +data_dir = "/opt/consul/data" + +ui = true + +verify_incoming = true +verify_outgoing = true +verify_server_hostname = true +tls_min_version = "TLSv1_3" +key_file = "/consul/config/itmo-dating-backend.key" +cert_file = "/consul/config/itmo-dating-backend.crt" +ca_file = "/consul/config/itmo-dating-backend-ca.crt" diff --git a/backend/foundation/build.gradle.kts b/backend/foundation/build.gradle.kts index 655087c9..93597be9 100644 --- a/backend/foundation/build.gradle.kts +++ b/backend/foundation/build.gradle.kts @@ -9,10 +9,13 @@ dependencies { api(libs.org.springframework.boot.spring.boot.starter.webflux) api(libs.org.springframework.boot.spring.boot.starter.data.r2dbc) api(libs.org.springframework.boot.spring.boot.starter.security) + api(libs.org.springframework.boot.spring.boot.starter.actuator) api(libs.org.springframework.spring.web) api(libs.org.springframework.spring.context) + api(libs.org.springframework.cloud.spring.cloud.starter.consul.discovery) + api(libs.org.springdoc.springdoc.openapi.starter.webflux.ui) api(libs.io.swagger.core.v3.swagger.annotations) diff --git a/backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/SpringConfiguration.kt b/backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/SpringConfiguration.kt index 165d1ce4..6045f3ab 100644 --- a/backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/SpringConfiguration.kt +++ b/backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/SpringConfiguration.kt @@ -1,5 +1,6 @@ package ru.ifmo.se.dating.spring +import org.springframework.cloud.client.discovery.EnableDiscoveryClient import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration import org.springframework.scheduling.annotation.EnableScheduling @@ -7,6 +8,7 @@ import java.time.Clock @Configuration @EnableScheduling +@EnableDiscoveryClient class SpringConfiguration { @Bean fun clock(): Clock = Clock.systemDefaultZone() diff --git a/backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/security/ssl/KeyStoreExtractor.kt b/backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/security/ssl/KeyStoreExtractor.kt new file mode 100644 index 00000000..ffadc356 --- /dev/null +++ b/backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/security/ssl/KeyStoreExtractor.kt @@ -0,0 +1,36 @@ +package ru.ifmo.se.dating.spring.security.ssl + +import jakarta.annotation.PostConstruct +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component +import ru.ifmo.se.dating.logging.Log +import ru.ifmo.se.dating.logging.Log.Companion.autoLog +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardCopyOption + +@Component +class KeyStoreExtractor( + @Value("\${spring.cloud.consul.tls.certificate-path}") + private val certificatePath: String, + + @Value("\${spring.cloud.consul.tls.key-store-path}") + private val keystorePath: String, +) { + private val log = Log.autoLog() + + @PostConstruct + fun extractCertificate() { + extract(certificatePath) + extract(keystorePath) + } + + private fun extract(path: String) { + log.info("Extracting file '$path' from the jar...") + val inputStream = javaClass.classLoader.getResourceAsStream(certificatePath)!! + File(certificatePath).parentFile?.mkdirs() + Files.copy(inputStream, Paths.get(certificatePath), StandardCopyOption.REPLACE_EXISTING) + log.info("File '$path' extracted successfully!") + } +} diff --git a/backend/foundation/src/main/resources/application-foundation.yml b/backend/foundation/src/main/resources/application-foundation.yml index 37190a4e..6cba5ba8 100644 --- a/backend/foundation/src/main/resources/application-foundation.yml +++ b/backend/foundation/src/main/resources/application-foundation.yml @@ -7,10 +7,37 @@ server: protocol: TLSv1.3 enabled-protocols: TLSv1.3 spring: + cloud: + consul: + scheme: https + host: server.dc1.consul + port: 8501 + tls: + key-store-instance-type: JKS + certificate-path: keystore/itmo-dating-backend.jks + certificate-password: ${server.ssl.key-store-password} + key-store-path: keystore/itmo-dating-backend.jks + key-store-password: ${server.ssl.key-store-password} + discovery: + enabled: true + scheme: https + instance-id: ${spring.application.name}:${HOSTNAME} + heartbeat: + enabled: true datasource: driver-class-name: org.postgresql.Driver liquibase: change-log: database/changelog.sql +management: + health: + consul: + enabled: true + diskspace: + enabled: false + endpoints: + web: + exposure: + include: health springdoc: api-docs: path: /openapi @@ -25,3 +52,4 @@ security: logging: level: web: DEBUG + org.springframework.cloud.consul: DEBUG diff --git a/backend/gradle/libs.versions.toml b/backend/gradle/libs.versions.toml index c3cf4815..4e50688e 100644 --- a/backend/gradle/libs.versions.toml +++ b/backend/gradle/libs.versions.toml @@ -31,12 +31,14 @@ org-springframework-boot-spring-boot = { module = "org.springframework.boot:spri org-springframework-boot-spring-boot-starter-webflux = { module = "org.springframework.boot:spring-boot-starter-webflux", version.ref = "org-springframework-boot-spring-boot" } org-springframework-boot-spring-boot-starter-data-r2dbc = { module = "org.springframework.boot:spring-boot-starter-data-r2dbc", version.ref = "org-springframework-boot-spring-boot" } org-springframework-boot-spring-boot-starter-security = { module = "org.springframework.boot:spring-boot-starter-security", version.ref = "org-springframework-boot-spring-boot" } +org-springframework-boot-spring-boot-starter-actuator = { module = "org.springframework.boot:spring-boot-starter-actuator", version.ref = "org-springframework-boot-spring-boot" } org-springframework-boot-spring-boot-starter-test = { module = "org.springframework.boot:spring-boot-starter-test", version.ref = "org-springframework-boot-spring-boot" } org-springframework-spring-context = { module = "org.springframework:spring-context", version.ref = "org-springframework-spring" } org-springframework-spring-web = { module = "org.springframework:spring-web", version.ref = "org-springframework-spring" } org-springframework-cloud-spring-cloud-starter-gateway = { module = "org.springframework.cloud:spring-cloud-starter-gateway", version.ref = "org-springframework-cloud" } +org-springframework-cloud-spring-cloud-starter-consul-discovery = { module = "org.springframework.cloud:spring-cloud-starter-consul-discovery", version.ref = "org-springframework-cloud" } org-springdoc-springdoc-openapi-starter-webflux-ui = { module = "org.springdoc:springdoc-openapi-starter-webflux-ui", version.ref = "org-springdoc-springdoc-openapi-starter" } diff --git a/compose.yml b/compose.yml index 44f8ac1b..aade2abf 100644 --- a/compose.yml +++ b/compose.yml @@ -10,11 +10,11 @@ services: TOKEN_SIGN_KEY_PRIVATE: ${ITMO_DATING_AUTHIK_TOKEN_SIGN_KEY_PRIVATE?:err} TELEGRAM_BOT_TOKEN: ${ITMO_DATING_AUTHIK_TELEGRAM_BOT_TOKEN?:err} KEY_STORE_PASSWORD: ${ITMO_DATING_KEY_STORE_PASSWORD?:err} - ports: - - "127.0.0.1:8081:8080/tcp" depends_on: authik-database: condition: service_healthy + "server.dc1.consul": + condition: service_started authik-database: image: postgres environment: @@ -35,11 +35,11 @@ services: POSTGRES_PASSWORD: ${ITMO_DATING_MATCHMAKER_POSTGRES_PASSWORD?:err} TOKEN_SIGN_KEY_PUBLIC: ${ITMO_DATING_TOKEN_SIGN_KEY_PUBLIC?:err} KEY_STORE_PASSWORD: ${ITMO_DATING_KEY_STORE_PASSWORD?:err} - ports: - - "127.0.0.1:8082:8080/tcp" depends_on: matchmaker-database: condition: service_healthy + "server.dc1.consul": + condition: service_started matchmaker-database: image: postgres environment: @@ -60,11 +60,11 @@ services: POSTGRES_PASSWORD: ${ITMO_DATING_PEOPLE_POSTGRES_PASSWORD?:err} TOKEN_SIGN_KEY_PUBLIC: ${ITMO_DATING_TOKEN_SIGN_KEY_PUBLIC?:err} KEY_STORE_PASSWORD: ${ITMO_DATING_KEY_STORE_PASSWORD?:err} - ports: - - "127.0.0.1:8083:8080/tcp" depends_on: people-database: condition: service_healthy + "server.dc1.consul": + condition: service_started people-database: image: postgres environment: @@ -83,6 +83,13 @@ services: KEY_STORE_PASSWORD: ${ITMO_DATING_KEY_STORE_PASSWORD?:err} ports: - "444:8080" + "server.dc1.consul": + build: + context: ./backend/consul + command: sh ./consul/run.sh + ports: + - "127.0.0.1:8500:8500/tcp" + - "127.0.0.1:8501:8501/tcp" tg-miniapp: build: context: frontend From 36b2b91c4b0370c82056c0efdc3756e61dac15a3 Mon Sep 17 00:00:00 2001 From: vityaman Date: Fri, 10 Jan 2025 14:28:33 +0300 Subject: [PATCH 3/5] #68 Fix consul image --- backend/consul/run.sh | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 backend/consul/run.sh diff --git a/backend/consul/run.sh b/backend/consul/run.sh new file mode 100644 index 00000000..fe40436b --- /dev/null +++ b/backend/consul/run.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +consul agent -config-file=/consul/config/consul.hcl -bootstrap-expect=1 \ + | grep -v "This request used the token query parameter which is deprecated and will be removed" From d83076775de5fb034b1d0758e44e4972423b4296 Mon Sep 17 00:00:00 2001 From: vityaman Date: Fri, 10 Jan 2025 14:30:11 +0300 Subject: [PATCH 4/5] #68 Consul discovers the gateway --- backend/config/crypto/keys.bash | 4 +++ backend/gateway/build.gradle.kts | 2 ++ .../ru/ifmo/se/dating/gateway/Application.kt | 2 ++ .../se/dating/gateway/KeyStoreExtractor.kt | 31 +++++++++++++++++++ .../src/main/resources/application.yml | 28 +++++++++++++++++ 5 files changed, 67 insertions(+) create mode 100644 backend/gateway/src/main/kotlin/ru/ifmo/se/dating/gateway/KeyStoreExtractor.kt diff --git a/backend/config/crypto/keys.bash b/backend/config/crypto/keys.bash index d5c1ccee..d6ef1748 100755 --- a/backend/config/crypto/keys.bash +++ b/backend/config/crypto/keys.bash @@ -87,6 +87,8 @@ function distribute() { echo "Copying package to the gateway..." copy "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.p12" + copy "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.jks" + copy "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.crt" echo "Copying keys to the consul..." copy "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.key" @@ -112,6 +114,8 @@ function clear() { echo "Removing package from the gateway..." remove "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.p12" + remove "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.jks" + remove "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.crt" echo "Removing keys from the consul..." remove "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.key" diff --git a/backend/gateway/build.gradle.kts b/backend/gateway/build.gradle.kts index ed99d1b6..f3d4a11d 100644 --- a/backend/gateway/build.gradle.kts +++ b/backend/gateway/build.gradle.kts @@ -4,7 +4,9 @@ plugins { dependencies { implementation(libs.org.springframework.boot.spring.boot) + implementation(libs.org.springframework.boot.spring.boot.starter.actuator) implementation(libs.org.springframework.cloud.spring.cloud.starter.gateway) + implementation(libs.org.springframework.cloud.spring.cloud.starter.consul.discovery) implementation(libs.org.springdoc.springdoc.openapi.starter.webflux.ui) diff --git a/backend/gateway/src/main/kotlin/ru/ifmo/se/dating/gateway/Application.kt b/backend/gateway/src/main/kotlin/ru/ifmo/se/dating/gateway/Application.kt index 708d2713..60d8b202 100644 --- a/backend/gateway/src/main/kotlin/ru/ifmo/se/dating/gateway/Application.kt +++ b/backend/gateway/src/main/kotlin/ru/ifmo/se/dating/gateway/Application.kt @@ -2,8 +2,10 @@ package ru.ifmo.se.dating.gateway import org.springframework.boot.autoconfigure.SpringBootApplication import org.springframework.boot.runApplication +import org.springframework.cloud.client.discovery.EnableDiscoveryClient @SpringBootApplication +@EnableDiscoveryClient class Application fun main(args: Array) { diff --git a/backend/gateway/src/main/kotlin/ru/ifmo/se/dating/gateway/KeyStoreExtractor.kt b/backend/gateway/src/main/kotlin/ru/ifmo/se/dating/gateway/KeyStoreExtractor.kt new file mode 100644 index 00000000..9e0a8ea5 --- /dev/null +++ b/backend/gateway/src/main/kotlin/ru/ifmo/se/dating/gateway/KeyStoreExtractor.kt @@ -0,0 +1,31 @@ +package ru.ifmo.se.dating.gateway + +import jakarta.annotation.PostConstruct +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Component +import java.io.File +import java.nio.file.Files +import java.nio.file.Paths +import java.nio.file.StandardCopyOption + + +@Component +class KeyStoreExtractor( + @Value("\${spring.cloud.consul.tls.certificate-path}") + private val certificatePath: String, + + @Value("\${spring.cloud.consul.tls.key-store-path}") + private val keystorePath: String, +) { + @PostConstruct + fun extractCertificate() { + extract(certificatePath) + extract(keystorePath) + } + + private fun extract(path: String) { + val inputStream = javaClass.classLoader.getResourceAsStream(certificatePath)!! + File(certificatePath).parentFile?.mkdirs() + Files.copy(inputStream, Paths.get(certificatePath), StandardCopyOption.REPLACE_EXISTING) + } +} diff --git a/backend/gateway/src/main/resources/application.yml b/backend/gateway/src/main/resources/application.yml index 92175076..bb6fa705 100644 --- a/backend/gateway/src/main/resources/application.yml +++ b/backend/gateway/src/main/resources/application.yml @@ -7,6 +7,8 @@ server: protocol: TLSv1.3 enabled-protocols: TLSv1.3 spring: + application: + name: gateway cloud: gateway: routes: @@ -95,6 +97,32 @@ spring: - Method=GET - Path=/api/locations + consul: + scheme: https + host: server.dc1.consul + port: 8501 + tls: + key-store-instance-type: JKS + certificate-path: keystore/itmo-dating-backend.jks + certificate-password: ${server.ssl.key-store-password} + key-store-path: keystore/itmo-dating-backend.jks + key-store-password: ${server.ssl.key-store-password} + discovery: + enabled: true + scheme: https + instance-id: ${spring.application.name}:${HOSTNAME} + heartbeat: + enabled: true +management: + health: + consul: + enabled: true + diskspace: + enabled: false + endpoints: + web: + exposure: + include: health springdoc: swagger-ui: path: /swagger-ui.html From 86db05a942cdcca3ef96f304b2dda924f6af5a1c Mon Sep 17 00:00:00 2001 From: vityaman Date: Fri, 10 Jan 2025 14:49:30 +0300 Subject: [PATCH 5/5] #68 Consul discovers databases --- backend/consul/Dockerfile | 2 +- backend/consul/config/consul.hcl | 39 ++++++++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/backend/consul/Dockerfile b/backend/consul/Dockerfile index 82b481c5..2c31c6e3 100644 --- a/backend/consul/Dockerfile +++ b/backend/consul/Dockerfile @@ -5,7 +5,7 @@ COPY ./config/itmo-dating-backend.crt /consul/config/itmo-dating-backend.crt COPY ./config/itmo-dating-backend-ca.crt /consul/config/itmo-dating-backend-ca.crt COPY ./config/consul.hcl /consul/config/consul.hcl -COPY run.sh /consul/run.sh +COPY ./run.sh /consul/run.sh RUN chmod -R 755 /consul/config && \ chmod 644 /consul/config/itmo-dating-backend.key && \ diff --git a/backend/consul/config/consul.hcl b/backend/consul/config/consul.hcl index e8094fb1..f08d1d1c 100644 --- a/backend/consul/config/consul.hcl +++ b/backend/consul/config/consul.hcl @@ -25,3 +25,42 @@ tls_min_version = "TLSv1_3" key_file = "/consul/config/itmo-dating-backend.key" cert_file = "/consul/config/itmo-dating-backend.crt" ca_file = "/consul/config/itmo-dating-backend-ca.crt" + +services = [ + { + name = "authik-database" + address = "authik-database" + port = 5432 + check = { + id = "authik-database-check" + name = "Authik PostgreSQL Health Check" + tcp = "authik-database:5432" + interval = "10s" + timeout = "1s" + } + }, + { + name = "matchmaker-database" + address = "matchmaker-database" + port = 5432 + check = { + id = "matchmaker-database-check" + name = "Matchmaker PostgreSQL Health Check" + tcp = "matchmaker-database:5432" + interval = "10s" + timeout = "1s" + } + }, + { + name = "people-database" + address = "people-database" + port = 5432 + check = { + id = "people-database-check" + name = "People PostgreSQL Health Check" + tcp = "people-database:5432" + interval = "10s" + timeout = "1s" + } + }, +]