Skip to content

Commit d81fa94

Browse files
authored
#68 Register all backend instances at Consul (#69)
1 parent 2a54ecd commit d81fa94

File tree

20 files changed

+401
-44
lines changed

20 files changed

+401
-44
lines changed

backend/.gitignore

+5
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ build/
3131
# Secrets
3232
*.pem
3333
*.p12
34+
*.key
35+
*.crt
36+
*.csr
37+
*.srl
38+
*.jks
3439

3540
# Java
3641
*.hprof

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

+2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
spring:
22
config:
33
import: application-foundation.yml
4+
application:
5+
name: authik
46
datasource:
57
url: jdbc:postgresql://authik-database:5432/${POSTGRES_DB}
68
username: ${POSTGRES_USER}

backend/config/crypto/ca.cnf

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
[req]
2+
default_bits = 4096
3+
prompt = no
4+
default_md = sha256
5+
distinguished_name = dn
6+
req_extensions = req_ext
7+
x509_extensions = v3_ca
8+
9+
[dn]
10+
C = RU
11+
ST = Saint-Petersburg
12+
L = Saint-Petersburg
13+
O = ITMO Dating
14+
OU = IT
15+
CN = ITMO Dating Root CA
16+
17+
[v3_ca]
18+
subjectKeyIdentifier = hash
19+
authorityKeyIdentifier = keyid:always,issuer
20+
basicConstraints = critical, CA:true
21+
keyUsage = critical, digitalSignature, cRLSign, keyCertSign

backend/config/crypto/csr.cnf

+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
[req]
2+
default_bits = 4096
3+
prompt = no
4+
default_md = sha256
5+
distinguished_name = dn
6+
req_extensions = req_ext
7+
8+
[dn]
9+
C = RU
10+
ST = Saint-Petersburg
11+
L = Saint-Petersburg
12+
O = ITMO Dating
13+
OU = IT
14+
CN = dating.se.ifmo.ru
15+
16+
[req_ext]
17+
subjectAltName = @alt_names
18+
19+
[alt_names]
20+
DNS.1 = localhost
21+
DNS.2 = authik
22+
DNS.3 = matchmaker
23+
DNS.4 = people
24+
DNS.5 = server.dc1.consul

backend/config/crypto/keys.bash

+112-36
Original file line numberDiff line numberDiff line change
@@ -7,54 +7,129 @@ ENV="$2"
77

88
ALIAS="itmo-dating"
99
ALIAS_BACKEND="$ALIAS-backend"
10-
KEYSTORE="keystore.p12"
10+
VALIDITY=1
1111
BACKEND_INSTALL_PATH="foundation/src/main/resources/keystore"
12+
CONSUL_INSTALL_PATH="consul/config"
1213
GATEWAY_INSTALL_PATH="gateway/src/main/resources/keystore"
1314
PASSWORD="$ITMO_DATING_KEY_STORE_PASSWORD"
1415

16+
function generate() {
17+
echo "Phase: Generate"
18+
19+
echo "Generating the private key for CA certificate..."
20+
openssl genpkey \
21+
-algorithm RSA \
22+
-out "$ALIAS_BACKEND-ca.key" 2> /dev/null
23+
24+
echo "Generating the self-signed CA certificate..."
25+
openssl req -x509 -new \
26+
-nodes \
27+
-days "$VALIDITY" \
28+
-config ca.cnf \
29+
-key "$ALIAS_BACKEND-ca.key" \
30+
-out "$ALIAS_BACKEND-ca.crt"
31+
32+
echo "Generating the private key for services..."
33+
openssl genpkey \
34+
-algorithm RSA \
35+
-out "$ALIAS_BACKEND.key" 2> /dev/null
36+
37+
echo "Generating the Certificate Signing Request (CSR)..."
38+
openssl req -new \
39+
-config csr.cnf \
40+
-key "$ALIAS_BACKEND.key" \
41+
-out "$ALIAS_BACKEND.csr"
42+
43+
echo "Signing the CSR with self-signed CA to create a certificate..."
44+
openssl x509 -req \
45+
-sha256 \
46+
-days "$VALIDITY" \
47+
-extfile csr.cnf -extensions req_ext \
48+
-CAcreateserial \
49+
-CA "$ALIAS_BACKEND-ca.crt" \
50+
-CAkey "$ALIAS_BACKEND-ca.key" \
51+
-in "$ALIAS_BACKEND.csr" \
52+
-out "$ALIAS_BACKEND.crt"
53+
54+
echo "Packaging keys and certificates..."
55+
openssl pkcs12 -export \
56+
-password pass:"$PASSWORD" \
57+
-inkey "$ALIAS_BACKEND.key" \
58+
-in "$ALIAS_BACKEND.crt" \
59+
-certfile "$ALIAS_BACKEND-ca.crt" \
60+
-out "$ALIAS_BACKEND.p12"
61+
62+
echo "Converting PKCS12 to JKS..."
63+
keytool -importkeystore \
64+
-srcstoretype PKCS12 \
65+
-srckeystore "$ALIAS_BACKEND.p12" \
66+
-srcstorepass "$PASSWORD" \
67+
-deststoretype JKS \
68+
-destkeystore "$ALIAS_BACKEND.jks" \
69+
-deststorepass "$PASSWORD"
70+
}
71+
1572
function copy() {
16-
mkdir -p "../../$BACKEND_INSTALL_PATH"
17-
mkdir -p "../../$GATEWAY_INSTALL_PATH"
18-
cp "$1" "../../$BACKEND_INSTALL_PATH/$1"
19-
cp "$1" "../../$GATEWAY_INSTALL_PATH/$1"
73+
DIR="$1"
74+
FILE="$2"
75+
76+
mkdir -p "../../$DIR"
77+
cp "$FILE" "../../$DIR/$FILE"
2078
}
2179

22-
function remove() {
23-
rm -f "$1" "../../$BACKEND_INSTALL_PATH/$1" "../../$GATEWAY_INSTALL_PATH/$1"
80+
function distribute() {
81+
echo "Phase: Distribute"
82+
83+
echo "Copying package to the backend..."
84+
copy "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.p12"
85+
copy "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.jks"
86+
copy "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.crt"
87+
88+
echo "Copying package to the gateway..."
89+
copy "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.p12"
90+
copy "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.jks"
91+
copy "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.crt"
92+
93+
echo "Copying keys to the consul..."
94+
copy "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.key"
95+
copy "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.crt"
96+
copy "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND-ca.crt"
2497
}
2598

26-
function generate() {
27-
echo "Generating the backend key pair keystore..."
28-
keytool \
29-
-genkeypair \
30-
-alias "$ALIAS" \
31-
-keyalg RSA \
32-
-keysize 4096 \
33-
-validity 1 \
34-
-dname "CN=localhost" \
35-
-ext "san=dns:localhost,dns:authik,dns:matchmaker,dns:people" \
36-
-keypass "$PASSWORD" \
37-
-keystore "$KEYSTORE" \
38-
-storeType PKCS12 \
39-
-storepass "$PASSWORD"
40-
41-
echo "Exporting the backend private key..."
42-
openssl pkcs12 -in "$KEYSTORE" -nocerts -out "$ALIAS_BACKEND-private.pem" \
43-
-passin pass:"$PASSWORD" -passout pass:"$PASSWORD"
44-
45-
echo "Exporting the backend public key..."
46-
openssl pkcs12 -in "$KEYSTORE" -nokeys -out "$ALIAS_BACKEND-public.pem" \
47-
-passin pass:"$PASSWORD" -passout pass:"$PASSWORD"
48-
49-
copy "$KEYSTORE"
50-
copy "$ALIAS_BACKEND-private.pem"
51-
copy "$ALIAS_BACKEND-public.pem"
99+
function remove() {
100+
DIR="$1"
101+
FILE="$2"
102+
103+
rm -rf "../../$DIR/$FILE"
104+
rm -rf "$FILE"
52105
}
53106

54107
function clear() {
55-
remove "$KEYSTORE"
56-
remove "$ALIAS_BACKEND-private.pem"
57-
remove "$ALIAS_BACKEND-public.pem"
108+
echo "Phase: Clear"
109+
110+
echo "Removing package from the backend..."
111+
remove "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.p12"
112+
remove "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.jks"
113+
remove "$BACKEND_INSTALL_PATH" "$ALIAS_BACKEND.crt"
114+
115+
echo "Removing package from the gateway..."
116+
remove "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.p12"
117+
remove "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.jks"
118+
remove "$GATEWAY_INSTALL_PATH" "$ALIAS_BACKEND.crt"
119+
120+
echo "Removing keys from the consul..."
121+
remove "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.key"
122+
remove "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND.crt"
123+
remove "$CONSUL_INSTALL_PATH" "$ALIAS_BACKEND-ca.crt"
124+
125+
echo "Removing local outputs..."
126+
rm -rf "$ALIAS_BACKEND.crt"
127+
rm -rf "$ALIAS_BACKEND.csr"
128+
rm -rf "$ALIAS_BACKEND.key"
129+
rm -rf "$ALIAS_BACKEND.p12"
130+
rm -rf "$ALIAS_BACKEND-ca.key"
131+
rm -rf "$ALIAS_BACKEND-ca.crt"
132+
rm -rf "$ALIAS_BACKEND-ca.srl"
58133
}
59134

60135
if [ "$ENV" = "test" ]; then
@@ -63,6 +138,7 @@ fi
63138

64139
if [ "$MODE" = "generate" ]; then
65140
generate
141+
distribute
66142
elif [ "$MODE" = "clean" ]; then
67143
clear
68144
else

backend/consul/Dockerfile

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
FROM hashicorp/consul
2+
3+
COPY ./config/itmo-dating-backend.key /consul/config/itmo-dating-backend.key
4+
COPY ./config/itmo-dating-backend.crt /consul/config/itmo-dating-backend.crt
5+
COPY ./config/itmo-dating-backend-ca.crt /consul/config/itmo-dating-backend-ca.crt
6+
COPY ./config/consul.hcl /consul/config/consul.hcl
7+
8+
COPY ./run.sh /consul/run.sh
9+
10+
RUN chmod -R 755 /consul/config && \
11+
chmod 644 /consul/config/itmo-dating-backend.key && \
12+
chmod 644 /consul/config/itmo-dating-backend.crt && \
13+
chmod 644 /consul/config/itmo-dating-backend-ca.crt && \
14+
chmod 644 /consul/config/consul.hcl

backend/consul/config/consul.hcl

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
server = true
2+
bind_addr = "0.0.0.0"
3+
client_addr = "0.0.0.0"
4+
5+
ports {
6+
http = 8500
7+
https = 8501
8+
grpc = -1
9+
}
10+
11+
acl {
12+
enabled = true
13+
default_policy = "allow"
14+
enable_token_persistence = true
15+
}
16+
17+
data_dir = "/opt/consul/data"
18+
19+
ui = true
20+
21+
verify_incoming = true
22+
verify_outgoing = true
23+
verify_server_hostname = true
24+
tls_min_version = "TLSv1_3"
25+
key_file = "/consul/config/itmo-dating-backend.key"
26+
cert_file = "/consul/config/itmo-dating-backend.crt"
27+
ca_file = "/consul/config/itmo-dating-backend-ca.crt"
28+
29+
services = [
30+
{
31+
name = "authik-database"
32+
address = "authik-database"
33+
port = 5432
34+
check = {
35+
id = "authik-database-check"
36+
name = "Authik PostgreSQL Health Check"
37+
tcp = "authik-database:5432"
38+
interval = "10s"
39+
timeout = "1s"
40+
}
41+
},
42+
{
43+
name = "matchmaker-database"
44+
address = "matchmaker-database"
45+
port = 5432
46+
check = {
47+
id = "matchmaker-database-check"
48+
name = "Matchmaker PostgreSQL Health Check"
49+
tcp = "matchmaker-database:5432"
50+
interval = "10s"
51+
timeout = "1s"
52+
}
53+
},
54+
{
55+
name = "people-database"
56+
address = "people-database"
57+
port = 5432
58+
check = {
59+
id = "people-database-check"
60+
name = "People PostgreSQL Health Check"
61+
tcp = "people-database:5432"
62+
interval = "10s"
63+
timeout = "1s"
64+
}
65+
},
66+
]

backend/consul/run.sh

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
#!/bin/sh
2+
3+
consul agent -config-file=/consul/config/consul.hcl -bootstrap-expect=1 \
4+
| grep -v "This request used the token query parameter which is deprecated and will be removed"

backend/foundation/build.gradle.kts

+3
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,13 @@ dependencies {
99
api(libs.org.springframework.boot.spring.boot.starter.webflux)
1010
api(libs.org.springframework.boot.spring.boot.starter.data.r2dbc)
1111
api(libs.org.springframework.boot.spring.boot.starter.security)
12+
api(libs.org.springframework.boot.spring.boot.starter.actuator)
1213

1314
api(libs.org.springframework.spring.web)
1415
api(libs.org.springframework.spring.context)
1516

17+
api(libs.org.springframework.cloud.spring.cloud.starter.consul.discovery)
18+
1619
api(libs.org.springdoc.springdoc.openapi.starter.webflux.ui)
1720

1821
api(libs.io.swagger.core.v3.swagger.annotations)

backend/foundation/src/main/kotlin/ru/ifmo/se/dating/spring/SpringConfiguration.kt

+2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
package ru.ifmo.se.dating.spring
22

3+
import org.springframework.cloud.client.discovery.EnableDiscoveryClient
34
import org.springframework.context.annotation.Bean
45
import org.springframework.context.annotation.Configuration
56
import org.springframework.scheduling.annotation.EnableScheduling
67
import java.time.Clock
78

89
@Configuration
910
@EnableScheduling
11+
@EnableDiscoveryClient
1012
class SpringConfiguration {
1113
@Bean
1214
fun clock(): Clock = Clock.systemDefaultZone()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package ru.ifmo.se.dating.spring.security.ssl
2+
3+
import jakarta.annotation.PostConstruct
4+
import org.springframework.beans.factory.annotation.Value
5+
import org.springframework.stereotype.Component
6+
import ru.ifmo.se.dating.logging.Log
7+
import ru.ifmo.se.dating.logging.Log.Companion.autoLog
8+
import java.io.File
9+
import java.nio.file.Files
10+
import java.nio.file.Paths
11+
import java.nio.file.StandardCopyOption
12+
13+
@Component
14+
class KeyStoreExtractor(
15+
@Value("\${spring.cloud.consul.tls.certificate-path}")
16+
private val certificatePath: String,
17+
18+
@Value("\${spring.cloud.consul.tls.key-store-path}")
19+
private val keystorePath: String,
20+
) {
21+
private val log = Log.autoLog()
22+
23+
@PostConstruct
24+
fun extractCertificate() {
25+
extract(certificatePath)
26+
extract(keystorePath)
27+
}
28+
29+
private fun extract(path: String) {
30+
log.info("Extracting file '$path' from the jar...")
31+
val inputStream = javaClass.classLoader.getResourceAsStream(certificatePath)!!
32+
File(certificatePath).parentFile?.mkdirs()
33+
Files.copy(inputStream, Paths.get(certificatePath), StandardCopyOption.REPLACE_EXISTING)
34+
log.info("File '$path' extracted successfully!")
35+
}
36+
}

0 commit comments

Comments
 (0)