Compare commits
2 Commits
dev
...
6037788dbd
| Author | SHA1 | Date | |
|---|---|---|---|
| 6037788dbd | |||
| 706631c57b |
62
README.md
62
README.md
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
A Spring Boot (Java 21) application acting as a secure bridge between Infisical and Dokploy, enabling automated synchronization and deployment of secrets through APIs and webhooks.
|
A Spring Boot (Java 21) application acting as a secure bridge between Infisical and Dokploy, enabling automated synchronization and deployment of secrets through APIs and webhooks.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Features
|
## Features
|
||||||
|
|
||||||
- Secure integration with Infisical
|
- Secure integration with Infisical
|
||||||
@@ -9,6 +11,8 @@ A Spring Boot (Java 21) application acting as a secure bridge between Infisical
|
|||||||
- Webhook-driven synchronization
|
- Webhook-driven synchronization
|
||||||
- Docker and Docker Compose ready
|
- Docker and Docker Compose ready
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Architecture Overview
|
## Architecture Overview
|
||||||
|
|
||||||
Infisical
|
Infisical
|
||||||
@@ -17,6 +21,8 @@ Infisical–Dokploy Bridge (Spring Boot)
|
|||||||
↓ (Dokploy API)
|
↓ (Dokploy API)
|
||||||
Dokploy
|
Dokploy
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Requirements
|
## Requirements
|
||||||
|
|
||||||
- Java 21
|
- Java 21
|
||||||
@@ -24,6 +30,8 @@ Dokploy
|
|||||||
- Infisical account
|
- Infisical account
|
||||||
- Dokploy instance with API access
|
- Dokploy instance with API access
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Environment Variables
|
## Environment Variables
|
||||||
|
|
||||||
### Infisical
|
### Infisical
|
||||||
@@ -38,9 +46,10 @@ Dokploy
|
|||||||
- DOKPLOY_API_URL: Base URL of Dokploy API
|
- DOKPLOY_API_URL: Base URL of Dokploy API
|
||||||
- DOKPLOY_API_KEY: Dokploy API key
|
- DOKPLOY_API_KEY: Dokploy API key
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Docker Compose
|
## Docker Compose
|
||||||
|
|
||||||
```txt
|
|
||||||
services:
|
services:
|
||||||
infisical-bridge:
|
infisical-bridge:
|
||||||
build: .
|
build: .
|
||||||
@@ -52,65 +61,46 @@ services:
|
|||||||
INFISICAL_WEBHOOK_SECRET: ${INFISICAL_WEBHOOK_SECRET}
|
INFISICAL_WEBHOOK_SECRET: ${INFISICAL_WEBHOOK_SECRET}
|
||||||
DOKPLOY_API_URL: ${DOKPLOY_API_URL}
|
DOKPLOY_API_URL: ${DOKPLOY_API_URL}
|
||||||
DOKPLOY_API_KEY: ${DOKPLOY_API_KEY}
|
DOKPLOY_API_KEY: ${DOKPLOY_API_KEY}
|
||||||
```
|
|
||||||
|
---
|
||||||
|
|
||||||
## Running
|
## Running
|
||||||
|
|
||||||
With Docker Compose:
|
With Docker Compose:
|
||||||
|
|
||||||
```sh
|
|
||||||
docker compose up -d --build
|
docker compose up -d --build
|
||||||
```
|
|
||||||
|
|
||||||
Local development:
|
Local development:
|
||||||
|
|
||||||
```sh
|
|
||||||
./gradlew bootRun
|
./gradlew bootRun
|
||||||
```
|
|
||||||
|
|
||||||
Application runs on http://localhost:8080
|
Application runs on http://localhost:8080
|
||||||
|
|
||||||
Use a service like ngrok.
|
Use a service like ngrok.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Infisical Webhook Configuration
|
## Infisical Webhook Configuration
|
||||||
|
|
||||||
When creating a webhook in Infisical, the following rules must be respected.
|
When creating a webhook in Infisical, the following rules must be respected.
|
||||||
|
|
||||||
### Webhook URL Formats
|
### Webhook URL format
|
||||||
|
|
||||||
Infisical bridge supports two webhook URL formats, depending on the Dokploy resource you want to update.
|
${INFISICAL_API_URL}/webhook?dokployComposeId=${DOKPLOY_COMPOSE_ID}
|
||||||
|
|
||||||
#### Dokploy Compose Webhook
|
- `dokployComposeId` must be the target Dokploy compose identifier
|
||||||
|
- This value is required and used to determine which Dokploy service is updated
|
||||||
`${INFISICAL_API_URL}/webhook?dokployComposeId=${DOKPLOY_COMPOSE_ID}`
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
- dokployComposeId (required):
|
|
||||||
The identifier of the target Dokploy Compose.
|
|
||||||
This value is used to determine which Dokploy compose service should be updated when the webhook is triggered.
|
|
||||||
|
|
||||||
#### Dokploy Application Webhook
|
|
||||||
|
|
||||||
`${INFISICAL_API_URL}/webhook?dokployApplicationId=${DOKPLOY_APPLICATION_ID}`
|
|
||||||
|
|
||||||
Parameters:
|
|
||||||
- dokployApplicationId (required):
|
|
||||||
The identifier of the target Dokploy Application.
|
|
||||||
This value is used to determine which Dokploy application should be updated when the webhook is triggered.
|
|
||||||
|
|
||||||
#### Notes
|
|
||||||
- Exactly one identifier must be provided per webhook URL.
|
|
||||||
- If no identifier or multiple identifiers are provided, the webhook request will be rejected.
|
|
||||||
- Ensure the provided ID matches an existing Dokploy resource.
|
|
||||||
|
|
||||||
### Webhook Secret
|
### Webhook Secret
|
||||||
|
|
||||||
The webhook secret **must exactly match**:
|
The webhook secret **must exactly match**:
|
||||||
|
|
||||||
`${INFISICAL_WEBHOOK_SECRET}`
|
${INFISICAL_WEBHOOK_SECRET}
|
||||||
|
|
||||||
Requests with an invalid or missing secret will be rejected.
|
Requests with an invalid or missing secret will be rejected.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Webhooks Behavior
|
## Webhooks Behavior
|
||||||
|
|
||||||
- Incoming webhook signatures are validated
|
- Incoming webhook signatures are validated
|
||||||
@@ -118,6 +108,8 @@ Requests with an invalid or missing secret will be rejected.
|
|||||||
- Dokploy is updated using its API
|
- Dokploy is updated using its API
|
||||||
- Invalid or unsigned requests are ignored
|
- Invalid or unsigned requests are ignored
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Security Notes
|
## Security Notes
|
||||||
|
|
||||||
- Secrets are never persisted
|
- Secrets are never persisted
|
||||||
@@ -125,11 +117,13 @@ Requests with an invalid or missing secret will be rejected.
|
|||||||
- HTTPS is recommended in production
|
- HTTPS is recommended in production
|
||||||
- Restrict network access to trusted sources only
|
- Restrict network access to trusted sources only
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
```sh
|
|
||||||
./gradlew test
|
./gradlew test
|
||||||
```
|
|
||||||
|
---
|
||||||
|
|
||||||
## Tech Stack
|
## Tech Stack
|
||||||
|
|
||||||
@@ -138,6 +132,8 @@ Requests with an invalid or missing secret will be rejected.
|
|||||||
- Gradle (Kotlin DSL)
|
- Gradle (Kotlin DSL)
|
||||||
- Docker / Docker Compose
|
- Docker / Docker Compose
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
MIT License
|
MIT License
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ import org.springframework.cloud.openfeign.FeignClient;
|
|||||||
import org.springframework.web.bind.annotation.PostMapping;
|
import org.springframework.web.bind.annotation.PostMapping;
|
||||||
import org.springframework.web.bind.annotation.RequestBody;
|
import org.springframework.web.bind.annotation.RequestBody;
|
||||||
|
|
||||||
import com.abnov.infisicalbridge.dto.DokployApplicationUpdateRequest;
|
|
||||||
import com.abnov.infisicalbridge.dto.DokployComposeUpdateRequest;
|
import com.abnov.infisicalbridge.dto.DokployComposeUpdateRequest;
|
||||||
|
|
||||||
@FeignClient(name = "dokployClient", url = "${dokploy.api-url}", configuration = DokployFeignConfig.class)
|
@FeignClient(name = "dokployClient", url = "${dokploy.api-url}", configuration = DokployFeignConfig.class)
|
||||||
@@ -12,7 +11,4 @@ public interface DokployClient {
|
|||||||
|
|
||||||
@PostMapping("/compose.update")
|
@PostMapping("/compose.update")
|
||||||
void updateCompose(@RequestBody DokployComposeUpdateRequest request);
|
void updateCompose(@RequestBody DokployComposeUpdateRequest request);
|
||||||
|
|
||||||
@PostMapping("/application.update")
|
|
||||||
void updateApplication(@RequestBody DokployApplicationUpdateRequest request);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package com.abnov.infisicalbridge.dto;
|
|
||||||
|
|
||||||
public record DokployApplicationUpdateRequest(
|
|
||||||
String applicationId,
|
|
||||||
String env) {
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,6 @@ import org.springframework.web.bind.annotation.RequestParam;
|
|||||||
import org.springframework.web.bind.annotation.RestController;
|
import org.springframework.web.bind.annotation.RestController;
|
||||||
|
|
||||||
import com.abnov.infisicalbridge.dokploy.DokployClient;
|
import com.abnov.infisicalbridge.dokploy.DokployClient;
|
||||||
import com.abnov.infisicalbridge.dto.DokployApplicationUpdateRequest;
|
|
||||||
import com.abnov.infisicalbridge.dto.DokployComposeUpdateRequest;
|
import com.abnov.infisicalbridge.dto.DokployComposeUpdateRequest;
|
||||||
import com.abnov.infisicalbridge.dto.InfisicalWebhookEventResponse;
|
import com.abnov.infisicalbridge.dto.InfisicalWebhookEventResponse;
|
||||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||||
@@ -39,8 +38,7 @@ public class InfisicalWebhookController {
|
|||||||
@PostMapping
|
@PostMapping
|
||||||
public ResponseEntity<Void> handleWebhook(
|
public ResponseEntity<Void> handleWebhook(
|
||||||
@RequestBody String payload,
|
@RequestBody String payload,
|
||||||
@RequestParam(required = false) String dokployComposeId,
|
@RequestParam String dokployComposeId,
|
||||||
@RequestParam(required = false) String dokployApplicationId,
|
|
||||||
@RequestHeader(value = "X-Infisical-Signature", required = false) String signature)
|
@RequestHeader(value = "X-Infisical-Signature", required = false) String signature)
|
||||||
throws InfisicalException {
|
throws InfisicalException {
|
||||||
|
|
||||||
@@ -82,24 +80,12 @@ public class InfisicalWebhookController {
|
|||||||
.map(s -> s.getSecretKey() + "=" + s.getSecretValue())
|
.map(s -> s.getSecretKey() + "=" + s.getSecretValue())
|
||||||
.collect(Collectors.joining("\n"));
|
.collect(Collectors.joining("\n"));
|
||||||
|
|
||||||
if (dokployComposeId != null) {
|
try {
|
||||||
try {
|
dokployClient.updateCompose(
|
||||||
dokployClient.updateCompose(
|
new DokployComposeUpdateRequest(dokployComposeId, envContent));
|
||||||
new DokployComposeUpdateRequest(dokployComposeId, envContent));
|
} catch (Exception e) {
|
||||||
} catch (Exception e) {
|
log.error("Failed to update Dokploy compose {}", dokployComposeId, e);
|
||||||
log.error("Failed to update Dokploy compose {}", dokployComposeId, e);
|
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
|
||||||
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dokployApplicationId != null) {
|
|
||||||
try {
|
|
||||||
dokployClient.updateApplication(
|
|
||||||
new DokployApplicationUpdateRequest(dokployApplicationId, envContent));
|
|
||||||
} catch (Exception e) {
|
|
||||||
log.error("Failed to update Dokploy application {}", dokployApplicationId, e);
|
|
||||||
return ResponseEntity.status(HttpStatus.BAD_GATEWAY).build();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ResponseEntity.ok().build();
|
return ResponseEntity.ok().build();
|
||||||
|
|||||||
Reference in New Issue
Block a user