Skip to content

Commit f02a626

Browse files
authored
#91 Add Vault config server (#93)
Signed-off-by: vityaman <vityaman.dev@yandex.ru>
1 parent fb82e4e commit f02a626

File tree

56 files changed

+572
-289
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+572
-289
lines changed

.github/workflows/gradle.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ jobs:
2727
uses: stoplightio/spectral-action@latest
2828
with:
2929
file_glob: 'backend/*/src/main/resources/static/openapi/api.yml'
30-
spectral_ruleset: backend/config/.spectral.yaml
30+
spectral_ruleset: backend/script/.spectral.yaml
3131

3232
- name: Setup up JDK 22
3333
uses: actions/setup-java@v4
@@ -39,7 +39,7 @@ jobs:
3939
uses: gradle/actions/setup-gradle@v4
4040

4141
- name: Generate Key Pair
42-
run: bash config/crypto/keys.bash generate test
42+
run: bash script/crypto/keys.bash generate test
4343

4444
- name: Build
4545
run: gradle build

backend/README.md

+84-8
Original file line numberDiff line numberDiff line change
@@ -4,19 +4,95 @@
44

55
For building and testing a project check the [GitHub Workflow](../.github/workflows/gradle.yml).
66

7-
To run the backend you need to make Spring Boot jars and run docker compose, but before do not forget to prepare environment variables and secret keys.
8-
But before ensure that frontend is already built.
7+
### TLS Keys
8+
9+
Generate self-signed certificates for the backend.
10+
11+
```bash
12+
export ITMO_DATING_KEYSTORE_PASSWORD="<...>"
13+
bash ./script/crypto/keys.bash generate
14+
```
15+
16+
It will also duplicate backend keys to use at gateway edge.
17+
18+
On production replace `*-external.p12` with widely trusted certificates.
19+
20+
### Building
21+
22+
To build all backend services just do simple:
923

1024
```bash
11-
source config/env/local.sh
12-
bash config/crypto/keys.bash generate
1325
gradle bootJar
14-
docker compose up --build
1526
```
1627

17-
To connect to database you can enter the database container and login into `psql`. For example,
28+
### Running Vault & Consul
29+
30+
Firstly deploy `Consul` and `Vault`. Then initialize the new Vault or unseal it.
31+
32+
```bash
33+
docker compose up --build -d consul vault
34+
```
35+
36+
Create the `itmo-dating` Secret Engine with KV v1 (!!).
37+
38+
There are secrets you need for each service.
39+
40+
#### People
41+
42+
```json
43+
{
44+
"itmo-dating.auth.jwt.public-key": "<...>",
45+
"itmo-dating.matchmaker.url": "https://matchmaker/api",
46+
"itmo-dating.postgres.db": "postgres",
47+
"itmo-dating.postgres.host": "database-primary.dating.se.ifmo.ru",
48+
"itmo-dating.postgres.password": "<...>",
49+
"itmo-dating.postgres.username": "postgres",
50+
"itmo-dating.s3.bucket.profile-photos": "profile-photos",
51+
"itmo-dating.s3.host": "object-storage.dating.se.ifmo.ru",
52+
"itmo-dating.s3.password": "<...>",
53+
"itmo-dating.s3.port": "9000",
54+
"itmo-dating.s3.username": "<...>"
55+
}
56+
```
57+
58+
#### Matchmaker
59+
60+
```json
61+
{
62+
"itmo-dating.auth.jwt.public-key": "<...>",
63+
"itmo-dating.postgres.db": "postgres",
64+
"itmo-dating.postgres.host": "database-primary.dating.se.ifmo.ru",
65+
"itmo-dating.postgres.password": "<...>",
66+
"itmo-dating.postgres.username": "postgres"
67+
}
68+
```
69+
70+
#### Authik
71+
72+
```bash
73+
{
74+
"itmo-dating.auth.jwt.duration": "PT2H",
75+
"itmo-dating.auth.jwt.private-key": "<...>",
76+
"itmo-dating.auth.jwt.public-key": "<...>",
77+
"itmo-dating.postgres.db": "postgres",
78+
"itmo-dating.postgres.host": "database-primary.dating.se.ifmo.ru",
79+
"itmo-dating.postgres.password": "<...>",
80+
"itmo-dating.postgres.username": "postgres",
81+
"itmo-dating.telegram.bot-token": "<...>"
82+
}
83+
```
84+
85+
### Running the Config Server
86+
87+
When Vault is ready and unsealed, you can start Config Service.
88+
89+
```bash
90+
docker compose up --build -d config
91+
```
92+
93+
### Running other services
1894

1995
```bash
20-
docker exec -it itmo-dating-matchmaker-database-1 bash
21-
psql -h localhost -p 5432 -d $POSTGRES_DB -U $POSTGRES_USER
96+
export ITMO_DATING_VAULT_TOKEN="<...>"
97+
docker compose up --build -d
2298
```

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/external/telegram/TelegramInitDataParser.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import ru.ifmo.se.dating.validation.expect
1313

1414
@Component
1515
class TelegramInitDataParser(
16-
@Value("\${security.auth.telegram.token}")
16+
@Value("\${itmo-dating.telegram.bot-token}")
1717
telegramToken: String,
1818
) {
1919
private val jackson = jacksonObjectMapper().apply {

backend/authik/src/main/kotlin/ru/ifmo/se/dating/authik/security/auth/JwtTokenIssuerConfiguration.kt

+2-2
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,10 @@ class JwtTokenIssuerConfiguration {
1414
fun jwtTokenIssuer(
1515
clock: Clock,
1616

17-
@Value("\${security.auth.token.sign.private}")
17+
@Value("\${itmo-dating.auth.jwt.private-key}")
1818
privateSignKey: String,
1919

20-
@Value("\${security.auth.token.duration}")
20+
@Value("\${itmo-dating.auth.jwt.duration}")
2121
duration: Duration,
2222
) = LoggingTokenIssuer(
2323
JwtTokenIssuer(
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
spring:
22
config:
33
import: application-foundation-test.yml
4-
security:
4+
itmo-dating:
55
auth:
6-
token:
7-
sign:
8-
private: RSA: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
9-
telegram:
10-
token: fake-telegram-bot-token
6+
jwt:
7+
duration: PT2H
8+
private-key: RSA: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
9+
telegram:
10+
bot-token: fake-telegram-bot-token

backend/authik/src/main/resources/application.yml

-16
Original file line numberDiff line numberDiff line change
@@ -3,19 +3,3 @@ spring:
33
import: application-foundation.yml
44
application:
55
name: authik
6-
datasource:
7-
url: jdbc:postgresql://database-primary.dating.se.ifmo.ru:5432/${POSTGRES_DB}
8-
username: ${POSTGRES_USER}
9-
password: ${POSTGRES_PASSWORD}
10-
r2dbc:
11-
url: r2dbc:postgresql://database-primary.dating.se.ifmo.ru:5432/${POSTGRES_DB}
12-
username: ${POSTGRES_USER}
13-
password: ${POSTGRES_PASSWORD}
14-
security:
15-
auth:
16-
token:
17-
sign:
18-
private: ${TOKEN_SIGN_KEY_PRIVATE}
19-
duration: PT2H
20-
telegram:
21-
token: ${TELEGRAM_BOT_TOKEN}

backend/buildSrc/src/main/kotlin/buildlogic.detekt-conventions.gradle.kts

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,5 +10,5 @@ dependencies {
1010
detekt {
1111
buildUponDefaultConfig = true
1212
allRules = false
13-
config.setFrom(file("$rootDir/config/detekt.yml"))
13+
config.setFrom(file("$rootDir/script/detekt.yml"))
1414
}

backend/config/Dockerfile

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
FROM eclipse-temurin:23-jdk-alpine
2+
3+
WORKDIR /config
4+
5+
COPY ./build/libs/config-1.0.0.jar ./config.jar
6+
7+
EXPOSE 8080
8+
9+
CMD ["java", "-jar", "config.jar"]

backend/config/build.gradle.kts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
plugins {
2+
id("buildlogic.spring-conventions")
3+
}
4+
5+
dependencies {
6+
implementation(project(":starter-service-discovery"))
7+
implementation(project(":starter-tls"))
8+
9+
implementation(libs.org.springframework.boot.spring.boot)
10+
implementation(libs.org.springframework.boot.spring.boot.starter.web)
11+
implementation(libs.org.springframework.cloud.spring.cloud.config.server)
12+
13+
testImplementation(libs.org.springframework.boot.spring.boot.starter.test)
14+
testImplementation(libs.junit.junit)
15+
testImplementation(libs.org.testcontainers.vault)
16+
}

backend/config/crypto/csr.cnf

-30
This file was deleted.

backend/config/env/.env

-12
This file was deleted.

backend/config/env/local.sh

-1
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package ru.ifmo.se.dating.config
2+
3+
import org.springframework.boot.autoconfigure.SpringBootApplication
4+
import org.springframework.boot.runApplication
5+
import org.springframework.cloud.config.server.EnableConfigServer
6+
import org.springframework.context.annotation.ComponentScan
7+
8+
@SpringBootApplication
9+
@ComponentScan(basePackages = ["ru.ifmo.se.dating"])
10+
@EnableConfigServer
11+
class Application
12+
13+
fun main(args: Array<String>) {
14+
runApplication<Application>(args = args)
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
spring:
2+
config:
3+
import: application-service-discovery-test.yml,application-tls-test.yml
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
spring:
2+
config:
3+
import: application-service-discovery.yml,application-tls.yml
4+
application:
5+
name: config
6+
profiles:
7+
active: vault
8+
cloud:
9+
config:
10+
server:
11+
vault:
12+
scheme: https
13+
host: vault.dating.se.ifmo.ru
14+
port: 8200
15+
kv-version: 1
16+
backend: itmo-dating
17+
skip-ssl-validation: true
18+
ssl:
19+
key-store: ${server.ssl.key-store}
20+
key-store-password: ${server.ssl.key-store-password}
21+
trust-store: ${client.ssl.key-store}
22+
trust-store-password: ${client.ssl.key-store-password}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package ru.ifmo.se.dating
2+
3+
import org.springframework.boot.test.util.TestPropertyValues
4+
import org.springframework.context.ApplicationContextInitializer
5+
import org.springframework.context.ConfigurableApplicationContext
6+
import ru.ifmo.se.dating.container.Vault
7+
8+
class VaultInitializer :
9+
ApplicationContextInitializer<ConfigurableApplicationContext> {
10+
11+
private val token: String = "test-vault-token"
12+
13+
private lateinit var vault: Vault
14+
15+
override fun initialize(ctx: ConfigurableApplicationContext) {
16+
vault = Vault.start(token = token)
17+
18+
TestPropertyValues.of(
19+
"spring.cloud.config.server.vault.host=${vault.host}",
20+
"spring.cloud.config.server.vault.port=${vault.port}",
21+
"spring.cloud.config.server.vault.token=$token",
22+
).applyTo(ctx.environment)
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package ru.ifmo.se.dating.config
2+
3+
import org.junit.Test
4+
import org.junit.runner.RunWith
5+
import org.springframework.boot.test.context.SpringBootTest
6+
import org.springframework.test.context.ActiveProfiles
7+
import org.springframework.test.context.ContextConfiguration
8+
import org.springframework.test.context.junit4.SpringRunner
9+
import ru.ifmo.se.dating.VaultInitializer
10+
11+
@RunWith(SpringRunner::class)
12+
@ActiveProfiles(profiles = ["test", "vault"])
13+
@SpringBootTest(
14+
classes = [Application::class],
15+
webEnvironment = SpringBootTest.WebEnvironment.DEFINED_PORT,
16+
useMainMethod = SpringBootTest.UseMainMethod.ALWAYS,
17+
)
18+
@ContextConfiguration(initializers = [VaultInitializer::class])
19+
class ConfigStartupTest {
20+
@Test
21+
fun contextLoads() {
22+
// Okay
23+
}
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package ru.ifmo.se.dating.container
2+
3+
import org.testcontainers.vault.VaultContainer
4+
5+
class Vault private constructor(val token: String) : AutoCloseable {
6+
private val container = VaultContainer(DOCKER_IMAGE_NAME)
7+
8+
val host: String get() = container.host
9+
10+
val port: Int get() = container.firstMappedPort
11+
12+
override fun close() {
13+
container.stop()
14+
container.close()
15+
}
16+
17+
companion object {
18+
private const val DOCKER_IMAGE_NAME = "hashicorp/vault"
19+
20+
fun start(token: String): Vault {
21+
val vault = Vault(token)
22+
vault.container.start()
23+
return vault
24+
}
25+
}
26+
}

0 commit comments

Comments
 (0)