diff --git a/.github/workflows/iac.yml b/.github/workflows/iac.yml
deleted file mode 100644
index 7fc7e45..0000000
--- a/.github/workflows/iac.yml
+++ /dev/null
@@ -1,4 +0,0 @@
-name: iac.yml
-on:
-
-jobs:
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6e2f653
--- /dev/null
+++ b/README.md
@@ -0,0 +1 @@
+
diff --git a/infrastructure/dev/docker-compose.yaml b/infrastructure/dev/docker-compose.yaml
index bc16bb3..df9c523 100644
--- a/infrastructure/dev/docker-compose.yaml
+++ b/infrastructure/dev/docker-compose.yaml
@@ -1,34 +1,9 @@
services:
- # API GATEWAY
-
- kong-cp:
- image: '${GW_IMAGE:-kong/kong-gateway:3.11.0.2}'
- restart: on-failure
- environment:
- KONG_DATABASE: off
- KONG_DECLARATIVE_CONFIG: /kong/declarative/kong.yaml
- KONG_ADMIN_LISTEN: 0.0.0.0:8001, 0.0.0.0:8444 ssl
- KONG_ADMIN_GUI_LISTEN: 0.0.0.0:8002, 0.0.0.0:8445 ssl
- KONG_ADMIN_GUI_URL: http://${GW_HOST:-localhost}:8002
- KONG_PASSWORD: handyshake
- ports:
- - "8000:8000" # Proxy HTTP
- - "8443:8443" # Proxy HTTPS
- - "8001:8001" # Admin API HTTP
- - "8444:8444" # Admin API HTTPS
- - "8002:8002" # Kong Manager HTTP
- - "8445:8445" # Kong Manager HTTPS
- networks:
- - backend-network
- volumes:
- - ./kong/kong.yaml:/kong/declarative/kong.yaml
- command: kong start
-
# MICROSERVICES
storage-service:
- image: storage-service:dev
+ image: acrdevopsprojectprod.azurecr.io/storage-service:v1.0
build:
context: ../../storage-service
dockerfile: Dockerfile
@@ -38,6 +13,8 @@ services:
RABBITMQ_HOST: rabbitmq
BUCKET_ENDPOINT: http://minio:9000
AWS_BUCKET_NAME : devops
+ ports:
+ - "8080:8080"
networks:
- backend-network
- db-network
@@ -51,7 +28,7 @@ services:
start_interval: 5s
user-service:
- image: user-service:dev
+ image: acrdevopsprojectprod.azurecr.io/user-service:v1.0
build:
context: ../../user-service
dockerfile: Dockerfile
@@ -59,7 +36,9 @@ services:
environment:
SERVER_PORT: 8080
RABBITMQ_HOST: rabbitmq
- POSTGRESQL_URL: jdbc:postgresql://postgresql/devops
+ POSTGRESQL_URI: jdbc:postgresql://postgresql/devops
+ ports:
+ - "8081:8080"
networks:
- backend-network
- db-network
@@ -151,7 +130,7 @@ services:
PGADMIN_DEFAULT_EMAIL: kacper@kacper.pl
PGADMIN_DEFAULT_PASSWORD: kacper
ports:
- - "8080:80"
+ - "8880:80"
networks:
- db-network
volumes:
@@ -161,7 +140,7 @@ services:
image: mongo-express:latest
restart: on-failure
ports:
- - "8081:8081"
+ - "8881:8081"
environment:
ME_CONFIG_MONGODB_ADMINUSERNAME: kacper
ME_CONFIG_MONGODB_ADMINPASSWORD: kacper
diff --git a/infrastructure/prod/kubernetes/argocd-app-of-apps/config-rabbitmq.yaml b/infrastructure/prod/kubernetes/argocd-app-of-apps/config-rabbitmq.yaml
index 7250ae4..672536c 100644
--- a/infrastructure/prod/kubernetes/argocd-app-of-apps/config-rabbitmq.yaml
+++ b/infrastructure/prod/kubernetes/argocd-app-of-apps/config-rabbitmq.yaml
@@ -25,6 +25,5 @@ spec:
app.kubernetes.io/component: rabbitmq-operator
app.kubernetes.io/name: rabbitmq-system
app.kubernetes.io/part-of: rabbitmq
- istio-injection: enabled # To inject sidecar proxy automatically we need set this label
syncOptions:
- CreateNamespace=true
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-base.yaml b/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-base.yaml
index 2dd69fb..1a01777 100644
--- a/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-base.yaml
+++ b/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-base.yaml
@@ -10,7 +10,7 @@ spec:
source:
chart: base
repoURL: https://istio-release.storage.googleapis.com/charts
- targetRevision: 1.28.1
+ targetRevision: 1.28.2
helm:
releaseName: istio-base
parameters:
diff --git a/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-d.yaml b/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-d.yaml
index 033e714..4bbfb47 100644
--- a/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-d.yaml
+++ b/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-d.yaml
@@ -10,7 +10,7 @@ spec:
source:
chart: istiod
repoURL: https://istio-release.storage.googleapis.com/charts
- targetRevision: 1.28.1
+ targetRevision: 1.28.2
helm:
releaseName: istiod
destination:
diff --git a/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-egress-controller.yaml b/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-egress-controller.yaml
index 863745d..010d07d 100644
--- a/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-egress-controller.yaml
+++ b/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-egress-controller.yaml
@@ -12,7 +12,7 @@ spec:
source:
chart: gateway
repoURL: https://istio-release.storage.googleapis.com/charts
- targetRevision: 1.28.1
+ targetRevision: 1.28.2
helm:
releaseName: istio-egressgateway
values: |
diff --git a/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-ingress-controller.yaml b/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-ingress-controller.yaml
index 32b53f1..629cb32 100644
--- a/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-ingress-controller.yaml
+++ b/infrastructure/prod/kubernetes/argocd-app-of-apps/helm-istio-ingress-controller.yaml
@@ -12,7 +12,7 @@ spec:
source:
chart: gateway
repoURL: https://istio-release.storage.googleapis.com/charts
- targetRevision: 1.28.1
+ targetRevision: 1.28.2
helm:
releaseName: istio-ingressgateway
values: |
diff --git a/infrastructure/prod/kubernetes/external-secrets-manifests/key-vault-external-secrets.yaml b/infrastructure/prod/kubernetes/external-secrets-manifests/key-vault-external-secrets.yaml
index 7c1fecd..ea6ff8f 100644
--- a/infrastructure/prod/kubernetes/external-secrets-manifests/key-vault-external-secrets.yaml
+++ b/infrastructure/prod/kubernetes/external-secrets-manifests/key-vault-external-secrets.yaml
@@ -11,7 +11,7 @@ spec:
refreshInterval: 0h5m0s
target:
name: cloudflare-api-token-secret
- creationPolicy: Owner # Creates Kubernetes secret if ExternalSecret created
+ creationPolicy: Owner # Creates Kubernetes secret if ExternalSecret created
data:
- secretKey: api-token
remoteRef:
@@ -53,19 +53,78 @@ spec:
template:
type: kubernetes.io/dockerconfigjson
data:
- .dockerconfigjson: |
+ .dockerconfigjson: | # Token username is the same as ACR name
{
"auths": {
- "acrdevopsprojectprod.azurecr.io": {
+ "{{ .acrName }}.azurecr.io": {
"username": "{{ .token }}",
"password": "{{ .password }}"
}
}
}
data:
+ - secretKey: acrName
+ remoteRef:
+ key: secret/acr-name
- secretKey: token
remoteRef:
key: secret/aks-acr-token
- secretKey: password
remoteRef:
- key: secret/aks-acr-password
\ No newline at end of file
+ key: secret/aks-acr-password
+---
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+ name: postgresql-credentials
+ namespace: prod
+spec:
+ secretStoreRef:
+ kind: ClusterSecretStore
+ name: azure-cluster-secret-store
+ refreshPolicy: Periodic
+ refreshInterval: 0h5m0s
+ target:
+ name: postgresql-credentials
+ creationPolicy: Owner
+ template: # Here we are using templating engine to create secret ready to be used in Spring application
+ engineVersion: v2
+ data: # Complete URI for JDBC with TLS required
+ uri: "jdbc:postgresql://{{ .uri }}/devops?&sslmode=require"
+ username: "{{ .username }}"
+ password: "{{ .password }}"
+ data:
+ - secretKey: uri
+ remoteRef:
+ key: secret/postgresql-uri
+ - secretKey: username
+ remoteRef:
+ key: secret/postgresql-username
+ - secretKey: password
+ remoteRef:
+ key: secret/postgresql-password
+---
+apiVersion: external-secrets.io/v1
+kind: ExternalSecret
+metadata:
+ name: cloudflare-r2-credentials
+ namespace: prod
+spec:
+ secretStoreRef:
+ kind: ClusterSecretStore
+ name: azure-cluster-secret-store
+ refreshPolicy: Periodic
+ refreshInterval: 0h5m0s
+ target:
+ name: cloudflare-r2-credentials
+ creationPolicy: Owner
+ data:
+ - secretKey: apiUri
+ remoteRef:
+ key: secret/cloudflare-r2-api-uri
+ - secretKey: accessKeyId
+ remoteRef:
+ key: secret/cloudflare-r2-account-id
+ - secretKey: secretAccessKey
+ remoteRef:
+ key: secret/cloudflare-api-token
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/istio-manifests/mtls-lockdown-peer-authentication.yaml b/infrastructure/prod/kubernetes/istio-manifests/mtls-lockdown-peer-authentication.yaml
index 131bc99..e86cadf 100644
--- a/infrastructure/prod/kubernetes/istio-manifests/mtls-lockdown-peer-authentication.yaml
+++ b/infrastructure/prod/kubernetes/istio-manifests/mtls-lockdown-peer-authentication.yaml
@@ -5,4 +5,4 @@ metadata:
namespace: istio-system
spec:
mtls:
- mode: PERMISSIVE # Lock down workloads in all namespaces to prioritise accept mTLS traffic
\ No newline at end of file
+ mode: STRICT # Lock down workloads in all namespaces to prioritise accept mTLS traffic
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/istio-manifests/rabbitmq-virtual-service.yaml b/infrastructure/prod/kubernetes/istio-manifests/rabbitmq-virtual-service.yaml
index 0721ddd..16bff9e 100644
--- a/infrastructure/prod/kubernetes/istio-manifests/rabbitmq-virtual-service.yaml
+++ b/infrastructure/prod/kubernetes/istio-manifests/rabbitmq-virtual-service.yaml
@@ -28,9 +28,16 @@ spec:
- devops-project-cluster.rabbitmq-system.svc.cluster.local
tcp:
- match:
- - port: 5672
+ - port: 5672 # TCP AMQP
route:
- destination:
host: devops-project-cluster.rabbitmq-system.svc.cluster.local
port:
- number: 5672
\ No newline at end of file
+ number: 5672
+ - match:
+ - port: 15692 # TCP Prometheus port
+ route:
+ - destination:
+ host: devops-project-cluster.rabbitmq-system.svc.cluster.local
+ port:
+ number: 15692
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/istio-manifests/storage-service-virtual-service.yaml b/infrastructure/prod/kubernetes/istio-manifests/storage-service-virtual-service.yaml
index 12cc455..e77b174 100644
--- a/infrastructure/prod/kubernetes/istio-manifests/storage-service-virtual-service.yaml
+++ b/infrastructure/prod/kubernetes/istio-manifests/storage-service-virtual-service.yaml
@@ -15,13 +15,19 @@ spec:
route:
- destination:
host: storage-service.prod.svc.cluster.local
+ port:
+ number: 8080
subset: v1
weight: 100 # We declare that all traffic is routed to v1.0 version
- destination:
host: storage-service.prod.svc.cluster.local
+ port:
+ number: 8080
subset: v2
weight: 0
- destination:
host: storage-service.prod.svc.cluster.local
+ port:
+ number: 8080
subset: v3
weight: 0
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/istio-manifests/user-service-virtual-service.yaml b/infrastructure/prod/kubernetes/istio-manifests/user-service-virtual-service.yaml
index bfab57a..2c6c446 100644
--- a/infrastructure/prod/kubernetes/istio-manifests/user-service-virtual-service.yaml
+++ b/infrastructure/prod/kubernetes/istio-manifests/user-service-virtual-service.yaml
@@ -15,13 +15,19 @@ spec:
route:
- destination:
host: user-service.prod.svc.cluster.local
+ port:
+ number: 8080
subset: v1
weight: 100 # We declare that all traffic is routed to v1.0 version
- destination:
host: user-service.prod.svc.cluster.local
+ port:
+ number: 8080
subset: v2
weight: 0
- destination:
host: user-service.prod.svc.cluster.local
+ port:
+ number: 8080
subset: v3
weight: 0
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/rabbitmq-manifests/rabbitmq-cluster-config.yaml b/infrastructure/prod/kubernetes/rabbitmq-manifests/rabbitmq-cluster-config.yaml
index d30da2f..14b8572 100644
--- a/infrastructure/prod/kubernetes/rabbitmq-manifests/rabbitmq-cluster-config.yaml
+++ b/infrastructure/prod/kubernetes/rabbitmq-manifests/rabbitmq-cluster-config.yaml
@@ -3,14 +3,15 @@ kind: RabbitmqCluster
metadata:
name: devops-project-cluster
namespace: rabbitmq-system
- labels:
- istio-injection: enabled # To inject sidecar proxy automatically we need to set this label
spec:
replicas: 1
override:
statefulSet:
spec:
template:
+ metadata:
+ labels: # To inject sidecar proxy automatically we need to set this label (Pod level) That way we prevent injecting to deployments where there is no need
+ sidecar.istio.io/inject: "true" # Same problem as with ArgoCD but I found solution :)
spec:
containers:
- name: rabbitmq
diff --git a/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-deployment-v1.0.yaml b/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-deployment-v1.0.yaml
index c89c60c..04e7d22 100644
--- a/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-deployment-v1.0.yaml
+++ b/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-deployment-v1.0.yaml
@@ -18,12 +18,30 @@ spec:
app: storage-service
version: v1.0
spec:
- imagePullSecrets: # Setting credentials to Azure Container Registry, synchronized with Key Vault
- - name: azurecr-credentials
containers:
- name: storage-service
- image: storage-service:dev
+ image: acrdevopsprojectprod.azurecr.io/storage-service:v1.0
imagePullPolicy: IfNotPresent
+ env:
+ - name: SERVER_PORT
+ value: "8080"
+ - name: RABBITMQ_HOST
+ value: rabbitmq.rabbitmq-system.svc.cluster.local
+ - name: BUCKET_ENDPOINT
+ valueFrom:
+ secretKeyRef:
+ name: cloudflare-r2-credentials
+ key: apiUri
+ - name: BUCKET_ACCESS_KEY
+ valueFrom:
+ secretKeyRef:
+ name: cloudflare-r2-credentials
+ key: accessKeyId
+ - name: BUCKET_SECRET_KEY
+ valueFrom:
+ secretKeyRef:
+ name: cloudflare-r2-credentials
+ key: secretAccessKey
ports:
- containerPort: 8080
protocol: TCP
@@ -45,5 +63,7 @@ spec:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
- serviceAccountName: storage-service
- restartPolicy: Always
\ No newline at end of file
+ imagePullSecrets: # Setting credentials to Azure Container Registry, synchronized with Key Vault
+ - name: azurecr-credentials
+ restartPolicy: Always
+ serviceAccountName: storage-service
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-network-policy-v1.0.yaml b/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-network-policy-v1.0.yaml
deleted file mode 100644
index 31c750d..0000000
--- a/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-network-policy-v1.0.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
-metadata:
- name: storage-service-v1.0
- namespace: prod
-spec:
- podSelector:
- matchLabels:
- app: storage-service
- version: v1.0
- policyTypes:
- - Ingress
- ingress:
- - from:
- - namespaceSelector:
- matchLabels:
- kubernetes.io/metadata.name: prod
- ports:
- - protocol: TCP
- port: 8080
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-rbac.yaml b/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-rbac.yaml
new file mode 100644
index 0000000..17ab0a3
--- /dev/null
+++ b/infrastructure/prod/kubernetes/storage-service-manifests/storage-service-rbac.yaml
@@ -0,0 +1,22 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: storage-service-read-secrets
+ namespace: prod
+rules:
+ - apiGroups: [ "" ]
+ resources: [ "secrets" ]
+ verbs: [get, list]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: storage-service-read-secrets
+ namespace: prod
+subjects:
+ - kind: ServiceAccount
+ name: storage-service
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: storage-service-read-secrets
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/user-service-manifests/user-service-deployment-v1.0.yaml b/infrastructure/prod/kubernetes/user-service-manifests/user-service-deployment-v1.0.yaml
index 7d017f6..11c6eb6 100644
--- a/infrastructure/prod/kubernetes/user-service-manifests/user-service-deployment-v1.0.yaml
+++ b/infrastructure/prod/kubernetes/user-service-manifests/user-service-deployment-v1.0.yaml
@@ -18,12 +18,30 @@ spec:
app: user-service
version: v1.0
spec:
- imagePullSecrets: # Setting credentials to Azure Container Registry, synchronized with Key Vault
- - name: azurecr-credentials
containers:
- name: user-service
- image: user-service:dev
+ image: acrdevopsprojectprod.azurecr.io/user-service:v1.0
imagePullPolicy: IfNotPresent
+ env:
+ - name: SERVER_PORT
+ value: "8080"
+ - name: RABBITMQ_HOST
+ value: rabbitmq.rabbitmq-system.svc.cluster.local
+ - name: POSTGRESQL_URI
+ valueFrom:
+ secretKeyRef:
+ name: postgresql-credentials
+ key: uri
+ - name: POSTGRESQL_USERNAME
+ valueFrom:
+ secretKeyRef:
+ name: postgresql-credentials
+ key: username
+ - name: POSTGRESQL_PASSWORD
+ valueFrom:
+ secretKeyRef:
+ name: postgresql-credentials
+ key: password
ports:
- containerPort: 8080
protocol: TCP
@@ -45,5 +63,7 @@ spec:
port: 8080
initialDelaySeconds: 15
periodSeconds: 10
- serviceAccountName: user-service
- restartPolicy: Always
\ No newline at end of file
+ imagePullSecrets: # Setting credentials to Azure Container Registry, synchronized with Key Vault
+ - name: azurecr-credentials
+ restartPolicy: Always
+ serviceAccountName: user-service
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/user-service-manifests/user-service-network-policy-v1.0.yaml b/infrastructure/prod/kubernetes/user-service-manifests/user-service-network-policy-v1.0.yaml
deleted file mode 100644
index 04308f4..0000000
--- a/infrastructure/prod/kubernetes/user-service-manifests/user-service-network-policy-v1.0.yaml
+++ /dev/null
@@ -1,20 +0,0 @@
-apiVersion: networking.k8s.io/v1
-kind: NetworkPolicy
-metadata:
- name: user-service-v1.0
- namespace: prod
-spec:
- podSelector:
- matchLabels:
- app: user-service
- version: v1.0
- policyTypes:
- - Ingress
- ingress:
- - from:
- - namespaceSelector:
- matchLabels:
- kubernetes.io/metadata.name: prod
- ports:
- - protocol: TCP
- port: 8080
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/user-service-manifests/user-service-rbac.yaml b/infrastructure/prod/kubernetes/user-service-manifests/user-service-rbac.yaml
new file mode 100644
index 0000000..ae53689
--- /dev/null
+++ b/infrastructure/prod/kubernetes/user-service-manifests/user-service-rbac.yaml
@@ -0,0 +1,22 @@
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+ name: user-service-read-secrets
+ namespace: prod
+rules:
+ - apiGroups: [ "" ]
+ resources: [ "secrets" ]
+ verbs: [get, list]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+ name: user-service-read-secrets
+ namespace: prod
+subjects:
+ - kind: ServiceAccount
+ name: user-service
+roleRef:
+ apiGroup: rbac.authorization.k8s.io
+ kind: Role
+ name: user-service-read-secrets
\ No newline at end of file
diff --git a/infrastructure/prod/kubernetes/values.yaml b/infrastructure/prod/kubernetes/values.yaml
index b8f2b33..91310c0 100644
--- a/infrastructure/prod/kubernetes/values.yaml
+++ b/infrastructure/prod/kubernetes/values.yaml
@@ -4,6 +4,8 @@ configs:
cm:
statusbadge.enabled: true # -- Enable Status Badge
server: # We need to apply additional config to ArgoCD helm chart to proxy traffic via Istio gateway
+ podLabels: # We are injecting Istio Envoy Proxy Sidecar here using annotation for pods. Only for ArgoCD Server because is used with Ingress Gateway for now
+ sidecar.istio.io/inject: "true" # If you don't want a problem with installation remember: If you need to pass some bool value as string use quotation marks "" :)
extraArgs:
- --staticassets
- /shared/app
diff --git a/infrastructure/prod/terraform/main.tf b/infrastructure/prod/terraform/main.tf
index 47f6bb3..b8fa9a6 100644
--- a/infrastructure/prod/terraform/main.tf
+++ b/infrastructure/prod/terraform/main.tf
@@ -35,7 +35,7 @@ resource "cloudflare_r2_custom_domain" "devops_r2_custom_domain" {
domain = "r2storage-${local.env}.kacperklimas.com"
enabled = true
zone_id = var.cloudflare_dns_zone_id
- min_tls = "1.2"
+ min_tls = "1.2" # What's interesting R2 resource block use TLS 1.0 as default minimum version which is not recommended and can cause major security issues, so we need to change to TLS 1.2
}
/* AZURE */
@@ -155,7 +155,7 @@ resource "azurerm_virtual_network_gateway" "vpn_gateway" {
resource_group_name = module.azure_resource_group.name
type = "Vpn"
generation = "Generation1"
- sku = "VpnGw2AZ"
+ sku = "VpnGw2AZ" # Zone-redundant gateway
ip_configuration {
public_ip_address_id = azurerm_public_ip.vpn_public_ip.id
subnet_id = module.azure_management_vnet.subnets["vpnsubnet"].resource_id
@@ -165,7 +165,7 @@ resource "azurerm_virtual_network_gateway" "vpn_gateway" {
vpn_client_protocols = ["IkeV2", "OpenVPN"]
address_space = ["172.16.0.0/24"]
root_certificate {
- name = "devopsCA"
+ name = "devopsCA" # Azure documentation says if we want to use self generated certificate we need to take part without --BEGIN CERTIFICATE-- and --END CERTIFICATE-- lines, so we use regular expressions to do that.
public_cert_data = replace(file(var.azure_vpn_path_to_cert), "/-.*-/", "")
}
}
@@ -200,13 +200,6 @@ module "azure_management_vnet" {
id = module.azure_management_vnet_nsg.resource_id
}
}
- # "dbsubnet" = {
- # name = "DatabaseInstancesSubnet"
- # address_prefixes = ["10.1.2.0/24"]
- # network_security_group = {
- # id = module.azure_management_vnet_nsg.resource_id
- # }
- # }
}
tags = var.azure_application_tags
}
@@ -464,6 +457,10 @@ module "azure_aks" {
tags = var.azure_application_tags
}
+locals { # Local variable to pass in ACR config and Secret config
+ aks_acr_token = "aks-token"
+}
+
# Azure Container Registry
module "azure_container_registry" {
source = "Azure/avm-res-containerregistry-registry/azurerm"
@@ -485,10 +482,10 @@ module "azure_container_registry" {
aksscope = {
name = "aks-scope" # Authorization read only (Pulling images, reading statuses etc)
actions = ["repositories/*/content/read", "repositories/*/metadata/read"]
- description = "Read only all repositories"
+ description = "Read-only all repositories"
registry_tokens = {
akstoken = {
- name = "aks-token"
+ name = local.aks_acr_token
passwords = {
password1 = { # Expiration date for token password
expiry = "2026-12-31T00:00:00Z"
@@ -520,28 +517,10 @@ data "azurerm_container_registry" "azure_container_registry" {
}
locals {
- keyvault_private_endpoint_ip = data.azurerm_private_endpoint_connection.key_vault_private_endpoint.private_service_connection[0].private_ip_address
- containerregistry_private_endpoint_ip = data.azurerm_private_endpoint_connection.container_registry_private_endpoint.private_service_connection[0].private_ip_address
- postgresql_private_endpoint_ip = data.azurerm_private_endpoint_connection.postgresql_private_endpoint.private_service_connection[0].private_ip_address
- container_registry_aks_password = module.azure_container_registry.scope_maps["aksscope"].registry_token_passwords["akstoken"].password1[0].value
-}
-
-data "azurerm_private_endpoint_connection" "key_vault_private_endpoint" {
- name = "KeyVaultPrivateEndpoint"
- resource_group_name = module.azure_resource_group.name
- depends_on = [module.devops_key_vault]
-}
-
-data "azurerm_private_endpoint_connection" "container_registry_private_endpoint" {
- name = "ContainerRegistryPrivateEndpoint"
- resource_group_name = module.azure_resource_group.name
- depends_on = [module.azure_container_registry]
-}
-
-data "azurerm_private_endpoint_connection" "postgresql_private_endpoint" {
- name = "PostgresqlPrivateEndpoint"
- resource_group_name = module.azure_resource_group.name
- depends_on = [module.devops_postgresql]
+ keyvault_private_endpoint_ip = data.azurerm_private_endpoint_connection.key_vault_private_endpoint.private_service_connection[0].private_ip_address
+ postgresql_private_endpoint_ip = data.azurerm_private_endpoint_connection.postgresql_private_endpoint.private_service_connection[0].private_ip_address
+ container_registry_main_endpoint_ip = data.azurerm_network_interface.container_registry_main_private_endpoint.private_ip_addresses[1] # This is a list, fist address is matching .data subdomain so if we need to retrieve address for main subdomain it must select second address in a list
+ container_registry_aks_password = module.azure_container_registry.scope_maps["aksscope"].registry_token_passwords["akstoken"].password1[0].value
}
# Azure Key Vault
@@ -584,12 +563,24 @@ module "devops_key_vault" {
description = "KeyVaultDefaultClient"
}
}
- secrets = { # Create secret for Kubernetes external dns and cert manager
+ secrets = { # Create secret for Kubernetes external dns and cert manager whole workload
+ cloudflare_r2_api_uri = {
+ name = "cloudflare-r2-api-uri"
+ tags = var.azure_application_tags
+ }
+ cloudflare_r2_account_id = {
+ name = "cloudflare-r2-account-id"
+ tags = var.azure_application_tags
+ }
cloudflare_api_token = {
name = "cloudflare-api-token"
tags = var.azure_application_tags
}
- aks_registry_login = {
+ acr_name = {
+ name = "acr-name"
+ tags = var.azure_application_tags
+ }
+ aks_registry_token = {
name = "aks-acr-token"
tags = var.azure_application_tags
}
@@ -601,8 +592,8 @@ module "devops_key_vault" {
name = "postgresql-uri"
tags = var.azure_application_tags
}
- postgresql_user = {
- name = "postgresql-user"
+ postgresql_username = {
+ name = "postgresql-username"
tags = var.azure_application_tags
}
postgresql_password = {
@@ -611,12 +602,15 @@ module "devops_key_vault" {
}
}
secrets_value = {
- cloudflare_api_token = var.cloudflare_api_token
- aks_registry_login = module.azure_container_registry.name
- aks_registry_password = local.container_registry_aks_password
- postgresql_uri = data.azurerm_postgresql_flexible_server.devops_postgresql.fqdn
- postgresql_user = data.azurerm_postgresql_flexible_server.devops_postgresql.administrator_login
- postgresql_password = random_password.postgresql_admin_password.result
+ cloudflare_r2_api_uri = "https://${cloudflare_r2_custom_domain.devops_r2_custom_domain.domain}/${cloudflare_r2_bucket.devops_r2_bucket.name}"
+ cloudflare_r2_account_id = var.cloudflare_account_id
+ cloudflare_api_token = var.cloudflare_api_token
+ acr_name = module.azure_container_registry.name
+ aks_registry_token = local.aks_acr_token # Here we need to pass ACR token name
+ aks_registry_password = local.container_registry_aks_password
+ postgresql_uri = data.azurerm_postgresql_flexible_server.devops_postgresql.fqdn
+ postgresql_username = data.azurerm_postgresql_flexible_server.devops_postgresql.administrator_login
+ postgresql_password = random_password.postgresql_admin_password.result
}
tags = var.azure_application_tags
}
@@ -628,7 +622,7 @@ module "devops_postgresql" {
name = "${module.azure_naming.postgresql_server.name}-${local.env}"
resource_group_name = module.azure_resource_group.name
location = var.azure_region
- server_version = "16" # Postgresql 18
+ server_version = "16" # Postgresql 16
sku_name = "GP_Standard_D2ds_v4" # 2 vCores, 8 GiB memory, 3750 max iops for Server VM. Depends on Region and current availability
storage_mb = "32768" # 32 GB on SSD Disk
authentication = {
@@ -641,7 +635,7 @@ module "devops_postgresql" {
high_availability = {
mode = "SameZone"
}
- zone = "1" # Need to choose AZ for correct provisioning
+ zone = "1" # Need to choose AZ for correct provisioning
databases = {
devops = {
name = "devops"
@@ -663,12 +657,31 @@ module "devops_postgresql" {
tags = var.azure_application_tags
}
+# In this section we need to retrieve IPv4 addresses from Private Endpoint configurations
data "azurerm_postgresql_flexible_server" "devops_postgresql" {
name = module.devops_postgresql.name
resource_group_name = module.azure_resource_group.name
depends_on = [module.devops_postgresql]
}
+data "azurerm_private_endpoint_connection" "key_vault_private_endpoint" {
+ name = "KeyVaultPrivateEndpoint"
+ resource_group_name = module.azure_resource_group.name
+ depends_on = [module.devops_key_vault]
+}
+
+data "azurerm_private_endpoint_connection" "postgresql_private_endpoint" {
+ name = "PostgresqlPrivateEndpoint"
+ resource_group_name = module.azure_resource_group.name
+ depends_on = [module.devops_postgresql]
+}
+
+data "azurerm_network_interface" "container_registry_main_private_endpoint" {
+ name = "containerregistry-${module.azure_naming.network_interface.name}${local.env}"
+ resource_group_name = module.azure_resource_group.name
+ depends_on = [module.azure_container_registry]
+}
+
# Private DNS Zones (These are very important because we can use TLS protocol in isolated private Azure network without exposing endpoints outside. Azure usually provides TLS wildcard certs that we can use with resource name as subdomain)
module "devops_key_vault_private_dns_zone" {
source = "Azure/avm-res-network-privatednszone/azurerm"
@@ -700,13 +713,13 @@ module "devops_container_registry_private_dns_zone" {
version = "0.4.3"
domain_name = "azurecr.io"
parent_id = module.azure_resource_group.resource_id
- a_records = {
- acr = {
- name = module.azure_container_registry.name
- ttl = 5
- ip_addresses = [local.containerregistry_private_endpoint_ip]
- }
- }
+ # a_records = { # I don't know why ACR module enter primary (data) Private DNS record automatically, I had some bugs with that because methods used in other resources (DB, Key Vault) are using one IPv4 address and one domain per resource. I couldn't pull images from registry because DNS records weren't configured correctly
+ # acr_io = {
+ # name = module.azure_container_registry.name
+ # ttl = 5
+ # ip_addresses = [local.container_registry_main_endpoint_ip]
+ # }
+ # }
virtual_network_links = {
aks = {
vnetlinkname = "aksvnetlink"
@@ -725,13 +738,13 @@ module "devops_postgresql_private_dns_zone" {
version = "0.4.3"
domain_name = "postgres.database.azure.com"
parent_id = module.azure_resource_group.resource_id
- a_records = {
- db = {
- name = module.devops_postgresql.name
- ttl = 5
- ip_addresses = [local.postgresql_private_endpoint_ip]
- }
- }
+ # a_records = { # Same problem as with ACR
+ # db = {
+ # name = module.devops_postgresql.name
+ # ttl = 5
+ # ip_addresses = [local.postgresql_private_endpoint_ip]
+ # }
+ # }
virtual_network_links = {
aks = {
vnetlinkname = "aksvnetlink"
diff --git a/pom.xml b/pom.xml
index 4bcff74..f7a4336 100644
--- a/pom.xml
+++ b/pom.xml
@@ -36,10 +36,6 @@
org.springframework.boot
spring-boot-starter
-
- org.springframework
- spring-webmvc
-
org.springframework.boot
spring-boot-starter-web
diff --git a/storage-service/src/main/java/com/devops/storageservice/config/SecurityConfig.java b/storage-service/src/main/java/com/devops/storageservice/config/SecurityConfig.java
index 41ce17a..8bd4068 100644
--- a/storage-service/src/main/java/com/devops/storageservice/config/SecurityConfig.java
+++ b/storage-service/src/main/java/com/devops/storageservice/config/SecurityConfig.java
@@ -2,10 +2,16 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
@Configuration
@EnableWebSecurity
@@ -17,11 +23,26 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.sessionManagement((sessionManagement) -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
+ .cors(Customizer.withDefaults())
.authorizeHttpRequests((requests) -> requests
- .requestMatchers("/**")
+ .requestMatchers("/actuator/**", "/api/**")
.permitAll()
.anyRequest().authenticated()
).csrf((csrf) -> csrf.disable())
.build();
}
-}
+
+ @Bean
+ public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowedOriginPatterns(List.of("http://localhost:*", "https://devops.kacperklimas.com"));
+ config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
+ config.setAllowedHeaders(List.of("*"));
+ config.setAllowCredentials(true);
+ config.setMaxAge(3600L);
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", config);
+ return source;
+ }
+}
\ No newline at end of file
diff --git a/storage-service/src/main/java/com/devops/storageservice/config/WebConfig.java b/storage-service/src/main/java/com/devops/storageservice/config/WebConfig.java
deleted file mode 100644
index e0a38d0..0000000
--- a/storage-service/src/main/java/com/devops/storageservice/config/WebConfig.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.devops.storageservice.config;
-
-import org.springframework.stereotype.Component;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-@Component
-@EnableWebMvc
-public class WebConfig implements WebMvcConfigurer {
-
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**")
- .allowedOrigins("http://localhost:8501")
- .allowedMethods("GET", "POST")
- .allowedHeaders("*")
- .allowCredentials(true);
- }
-}
diff --git a/storage-service/src/main/java/com/devops/storageservice/controller/StorageController.java b/storage-service/src/main/java/com/devops/storageservice/controller/StorageController.java
index b5eb62f..62d7ef5 100644
--- a/storage-service/src/main/java/com/devops/storageservice/controller/StorageController.java
+++ b/storage-service/src/main/java/com/devops/storageservice/controller/StorageController.java
@@ -5,10 +5,7 @@
import lombok.RequiredArgsConstructor;
import com.devops.storageservice.service.StorageService;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
import java.net.URISyntaxException;
@@ -19,6 +16,13 @@ public class StorageController {
private final StorageService storageService;
+ @GetMapping
+ public ResponseEntity helloStorage() {
+ return ResponseEntity
+ .ok()
+ .body("Hello from storage-service v1 :)");
+ }
+
@PostMapping("/file")
ResponseEntity fileUploadRequest(@RequestBody FileUploadRequestDto fileUploadRequestDto) throws URISyntaxException {
FileUploadResponseDto fileUploadResponseDto = storageService.fileUploadRequest(fileUploadRequestDto);
diff --git a/storage-service/src/main/java/com/devops/storageservice/service/StorageService.java b/storage-service/src/main/java/com/devops/storageservice/service/StorageService.java
index 6d86aa2..6e71dbf 100644
--- a/storage-service/src/main/java/com/devops/storageservice/service/StorageService.java
+++ b/storage-service/src/main/java/com/devops/storageservice/service/StorageService.java
@@ -13,7 +13,7 @@
@RequiredArgsConstructor
public class StorageService {
- @Qualifier("BlobStorage")
+ @Qualifier("R2Storage")
private final StorageRepository storageRepository;
public FileUploadResponseDto fileUploadRequest(FileUploadRequestDto fileUploadRequestDto) {
diff --git a/storage-service/src/main/resources/application.yaml b/storage-service/src/main/resources/application.yaml
index 87eefed..d7a9597 100644
--- a/storage-service/src/main/resources/application.yaml
+++ b/storage-service/src/main/resources/application.yaml
@@ -3,15 +3,18 @@ spring:
name: "storage-service"
cloud:
aws:
+ credentials:
+ access-key: "${BUCKET_ACCESS_KEY}:kacperkacper"
+ secret-key: "${BUCKET_SECRET_KEY}:kacperkacper"
s3:
endpoint: "${BUCKET_ENDPOINT:http://localhost:9000}"
- region: "us-east-1" # Auto region for Minio and R2
+ region: "${BUCKET_REGION}:us-east-1" # Auto region for Minio and R2
rabbitmq:
username: "${RABBITMQ_USERNAME:kacper}"
password: "${RABBITMQ_PASSWORD:kacper}"
+ virtual-host: "${RABBITMQ_VHOST:rabbitmq}"
host: "${RABBITMQ_HOST:localhost}"
port: "${RABBITMQ_PORT:5672}"
- virtual-host: "${RABBITMQ_VHOST:rabbitmq}"
template:
retry:
enabled: true
diff --git a/user-service/src/main/java/com/devops/userservice/config/SecurityConfig.java b/user-service/src/main/java/com/devops/userservice/config/SecurityConfig.java
index 5987d21..8a47020 100644
--- a/user-service/src/main/java/com/devops/userservice/config/SecurityConfig.java
+++ b/user-service/src/main/java/com/devops/userservice/config/SecurityConfig.java
@@ -2,10 +2,16 @@
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.Customizer;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+
+import java.util.List;
@Configuration
@EnableWebSecurity
@@ -17,11 +23,26 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.sessionManagement((sessionManagement) -> sessionManagement
.sessionCreationPolicy(SessionCreationPolicy.STATELESS)
)
+ .cors(Customizer.withDefaults())
.authorizeHttpRequests((requests) -> requests
- .requestMatchers("/**")
+ .requestMatchers("/actuator/**", "/api/**")
.permitAll()
.anyRequest().authenticated()
).csrf((csrf) -> csrf.disable())
.build();
}
+
+ @Bean
+ public CorsConfigurationSource corsConfigurationSource() {
+ CorsConfiguration config = new CorsConfiguration();
+ config.setAllowedOriginPatterns(List.of("http://localhost:*", "https://devops.kacperklimas.com"));
+ config.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "OPTIONS"));
+ config.setAllowedHeaders(List.of("*"));
+ config.setAllowCredentials(true);
+ config.setMaxAge(3600L);
+
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+ source.registerCorsConfiguration("/**", config);
+ return source;
+ }
}
diff --git a/user-service/src/main/java/com/devops/userservice/config/WebConfig.java b/user-service/src/main/java/com/devops/userservice/config/WebConfig.java
deleted file mode 100644
index 50a4879..0000000
--- a/user-service/src/main/java/com/devops/userservice/config/WebConfig.java
+++ /dev/null
@@ -1,20 +0,0 @@
-package com.devops.userservice.config;
-
-import org.springframework.stereotype.Component;
-import org.springframework.web.servlet.config.annotation.CorsRegistry;
-import org.springframework.web.servlet.config.annotation.EnableWebMvc;
-import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
-
-@Component
-@EnableWebMvc
-public class WebConfig implements WebMvcConfigurer {
-
- @Override
- public void addCorsMappings(CorsRegistry registry) {
- registry.addMapping("/**")
- .allowedOrigins("http://localhost:8500")
- .allowedMethods("GET", "POST")
- .allowedHeaders("*")
- .allowCredentials(true);
- }
-}
diff --git a/user-service/src/main/java/com/devops/userservice/controller/UserController.java b/user-service/src/main/java/com/devops/userservice/controller/UserController.java
index 23ac0d9..324d77d 100644
--- a/user-service/src/main/java/com/devops/userservice/controller/UserController.java
+++ b/user-service/src/main/java/com/devops/userservice/controller/UserController.java
@@ -4,10 +4,7 @@
import com.devops.sharedresources.dto.UserDto;
import com.devops.userservice.service.UserService;
import org.springframework.http.ResponseEntity;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api/user")
@@ -16,6 +13,13 @@ public class UserController {
private final UserService userService;
+ @GetMapping
+ public ResponseEntity helloUser() {
+ return ResponseEntity
+ .ok()
+ .body("Hello from user-service v1 :)");
+ }
+
@PostMapping
public ResponseEntity createUser(@RequestBody UserDto userDto) {
userService.createUser(userDto.getUsername(), userDto.getEmail());
diff --git a/user-service/src/main/resources/application.yaml b/user-service/src/main/resources/application.yaml
index 82ef30b..37d9cff 100644
--- a/user-service/src/main/resources/application.yaml
+++ b/user-service/src/main/resources/application.yaml
@@ -4,7 +4,7 @@ spring:
datasource:
username: "${POSTGRESQL_USERNAME:kacper}"
password: "${POSTGRESQL_PASSWORD:kacper}"
- url: "${POSTGRESQL_URL:jdbc:postgresql://localhost/devops}"
+ url: "${POSTGRESQL_URI:jdbc:postgresql://localhost/devops}"
driver-class-name: org.postgresql.Driver
jpa:
hibernate: