Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ stellar-disbursement-platform-backend
.idea
.cursor

# Local HTTPS certificates
dev/certs/

# Rust & Soroban
target/
test_snapshots/
test_snapshots/
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/).

### Added

- Added Launch Wizard through `make setup` command to simplify initial setup and mainnet configuration for docker compose [#875](https://github.com/stellar/stellar-disbursement-platform-backend/pull/875)
- Add Launch Wizard through `make setup` command to simplify initial setup and mainnet configuration for docker compose [#875](https://github.com/stellar/stellar-disbursement-platform-backend/pull/875)
- Add HTTPS mode to setup wizard [#957](https://github.com/stellar/stellar-disbursement-platform-backend/pull/957)
- Support for SEP-10 and SEP-24 endpoints in the SDP [#834](https://github.com/stellar/stellar-disbursement-platform-backend/pull/834)
- Add SEP10 /auth endpoints
- Add SEP24 /info endpoints
Expand Down
18 changes: 18 additions & 0 deletions dev/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Pre-requisites](#pre-requisites)
- [Clone the repository:](#clone-the-repository)
- [Update local DNS](#update-local-dns)
- [Set up local HTTPS (optional)](#set-up-local-https-optional)
- [Automated Stellar Account Creation and .env Configuration](#automated-stellar-account-creation-and-env-configuration)
- [Start/Stop Local Environment](#startstop-local-environment)
- [Login to the SDP and send a Disbursement](#login-to-the-sdp-and-send-a-disbursement)
Expand Down Expand Up @@ -62,6 +63,23 @@ To include them, you can run command `sudo nano /etc/hosts` and insert the lines
127.0.0.1 pinkcorp.stellar.local
```

### Set up local HTTPS (optional)

HTTPS is required for working with WebAuthn/passkeys. If you want the wizard to launch the dashboard over HTTPS (`https://<tenant>.stellar.local:3443`):

1. Install [mkcert](https://web.dev/articles/how-to-use-local-https):
```sh
brew install mkcert
mkcert -install
```
2. Generate local TLS certs (run from the repo root once):
```sh
mkdir -p dev/certs
mkcert -key-file dev/certs/stellar.local-key.pem -cert-file dev/certs/stellar.local.pem \
"*.stellar.local" localhost 127.0.0.1 ::1
```
3. When the setup wizard asks, choose HTTPS.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great instructions!


### Automated Stellar Account Creation and .env Configuration

Use the unified setup wizard to generate accounts and a ready-to-use `.env`:
Expand Down
17 changes: 17 additions & 0 deletions dev/docker-compose-https-frontend.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
services:
sdp-frontend:
extends:
file: docker-compose-frontend.yml
service: sdp-frontend
sdp-frontend-proxy:
image: nginx:1.25-alpine
depends_on:
- sdp-frontend
ports:
- "3443:443"
extra_hosts:
- "host.docker.internal:host-gateway"
volumes:
- ./nginx-https.conf:/etc/nginx/conf.d/default.conf:ro
- ./certs:/etc/nginx/ssl:ro
- ./env-config-${NETWORK_TYPE:-testnet}.js:/etc/nginx/html-env/env-config.js:ro
62 changes: 62 additions & 0 deletions dev/nginx-https.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
map $http_upgrade $connection_upgrade {
default upgrade;
'' close;
}

upstream frontend_dev {
server host.docker.internal:3000;
keepalive 16;
}

upstream frontend_static {
server sdp-frontend:80;
keepalive 16;
}

server {
listen 80;
server_name localhost *.stellar.local;
return 301 https://$host$request_uri;
}

server {
listen 443 ssl;
server_name localhost *.stellar.local;

ssl_certificate /etc/nginx/ssl/stellar.local.pem;
ssl_certificate_key /etc/nginx/ssl/stellar.local-key.pem;
ssl_protocols TLSv1.2 TLSv1.3;

gzip on;
gzip_types application/javascript application/rss+xml application/x-font-opentype application/x-font-truetype application/x-font-ttf application/xhtml+xml application/xml font/opentype font/otf font/ttf image/svg+xml image/x-icon text/css text/javascript text/plain text/xml;

location = /settings/env-config.js {
alias /etc/nginx/html-env/env-config.js;
add_header Cache-Control "no-store";
}

location / {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_cache_bypass $http_upgrade;
proxy_read_timeout 300s;
proxy_connect_timeout 5s;
proxy_pass http://frontend_dev;
proxy_intercept_errors on;
error_page 502 503 504 = @frontend_static;
}

location @frontend_static {
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto https;
proxy_pass http://frontend_static;
}
}
76 changes: 76 additions & 0 deletions tools/sdp-setup/internal/config/env.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package config

import (
"fmt"
"net/url"
"os"
"path/filepath"
"strconv"
Expand All @@ -21,6 +22,8 @@ const (
DefaultAdminKey = "api_key_1234567890"
DefaultAdminURL = "http://localhost:8003"
DefaultWorkDir = "dev"
DefaultHTTPSPort = "3443"
DefaultHTTPPort = "3000"
)

// Config represents the environment configuration for SDP
Expand All @@ -37,6 +40,11 @@ type Config struct {
SetupName string // Optional setup name for multi-config support
SingleTenantMode bool // Whether to use single-tenant mode

// Frontend HTTPS settings
UseHTTPS bool
FrontendProtocol string // http or https
FrontendPort string // exposed port for the UI (3000/3443)

// Following config doesn't get written to env file
WorkDir string
EnvFilePath string
Expand Down Expand Up @@ -125,6 +133,7 @@ type ConfigOpts struct {
Network utils.NetworkType
SingleTenantMode bool
Accounts accounts.Info
EnableHTTPS bool
}

// NewConfig creates a new Config
Expand All @@ -134,6 +143,9 @@ func NewConfig(opts ConfigOpts) Config {
cfg.SingleTenantMode = opts.SingleTenantMode
cfg.EnvFilePath = opts.EnvPath
cfg.DockerProject = ComposeProjectName(opts.SetupName)
if opts.EnableHTTPS {
cfg.EnableHTTPS()
}
return cfg
}

Expand Down Expand Up @@ -163,6 +175,11 @@ func Load(path string) (Config, error) {
AdminKey: DefaultAdminKey,
AdminURL: DefaultAdminURL,
WorkDir: DefaultWorkDir,

// Frontend defaults
UseHTTPS: false,
FrontendProtocol: "http",
FrontendPort: DefaultHTTPPort,
}

// Parse SINGLE_TENANT_MODE
Expand All @@ -173,6 +190,29 @@ func Load(path string) (Config, error) {
}
}

// Parse USE_HTTPS (optional)
if useHTTPS := strings.TrimSpace(envMap["USE_HTTPS"]); useHTTPS != "" {
if cfg.UseHTTPS, err = strconv.ParseBool(useHTTPS); err != nil {
return Config{}, fmt.Errorf("parsing USE_HTTPS: %w", err)
}
}

// Parse SDP_UI_BASE_URL (optional)
if base := strings.TrimSpace(envMap["SDP_UI_BASE_URL"]); base != "" {
if u, parseErr := url.Parse(base); parseErr == nil && u.Scheme != "" {
cfg.FrontendProtocol = u.Scheme
if port := u.Port(); port != "" {
cfg.FrontendPort = port
}
}
}

if cfg.UseHTTPS {
cfg.EnableHTTPS()
} else {
cfg.DisableHTTPS()
}

return cfg, nil
}

Expand Down Expand Up @@ -201,6 +241,9 @@ func Write(cfg Config, path string) error {
"DISTRIBUTION_SEED": cfg.DistributionSeed,
"CHANNEL_ACCOUNT_ENCRYPTION_PASSPHRASE": cfg.DistributionSeed,
"DISTRIBUTION_ACCOUNT_ENCRYPTION_PASSPHRASE": cfg.DistributionSeed,
"USE_HTTPS": strconv.FormatBool(cfg.UseHTTPS),
"SDP_UI_BASE_URL": cfg.FrontendBaseURL("localhost"),
"BASE_URL": "http://localhost:8000",
}

if cfg.NetworkType == "pubnet" {
Expand Down Expand Up @@ -228,6 +271,10 @@ func fromAccounts(networkType utils.NetworkType, acc accounts.Info) Config {
AdminKey: DefaultAdminKey,
AdminURL: DefaultAdminURL,
WorkDir: DefaultWorkDir,

UseHTTPS: false,
FrontendProtocol: "http",
FrontendPort: DefaultHTTPPort,
}

switch networkType {
Expand Down Expand Up @@ -259,3 +306,32 @@ func ComposeProjectName(setupName string) string {
}
return fmt.Sprintf("%s-%s", DefaultProject, setupName)
}

// EnableHTTPS toggles HTTPS related defaults on the config.
func (cfg *Config) EnableHTTPS() {
cfg.UseHTTPS = true
cfg.FrontendProtocol = "https"
if cfg.FrontendPort == "" || cfg.FrontendPort == DefaultHTTPPort {
cfg.FrontendPort = DefaultHTTPSPort
}
}

// DisableHTTPS forces HTTP defaults on the config.
func (cfg *Config) DisableHTTPS() {
cfg.UseHTTPS = false
cfg.FrontendProtocol = "http"
cfg.FrontendPort = DefaultHTTPPort
}

// FrontendBaseURL builds a UI base URL for the provided host (e.g., localhost or bluecorp.stellar.local)
func (cfg Config) FrontendBaseURL(host string) string {
protocol := cfg.FrontendProtocol
if protocol == "" {
protocol = "http"
}
port := cfg.FrontendPort
if port == "" {
port = DefaultHTTPPort
}
return fmt.Sprintf("%s://%s:%s", protocol, host, port)
}
43 changes: 43 additions & 0 deletions tools/sdp-setup/internal/docker/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"context"
"errors"
"fmt"
"os"
"os/exec"
"path/filepath"
"strings"
Expand All @@ -19,20 +20,33 @@ var ErrUserDeclined = errors.New("user declined to proceed")
type Service struct {
cfg config.Config
executor CommandExecutor
files []string
}

// NewService creates a new Docker service.
func NewService(cfg config.Config) (*Service, error) {
if cfg.WorkDir != "" {
if abs, err := filepath.Abs(cfg.WorkDir); err == nil {
cfg.WorkDir = abs
}
}
if cfg.EnvFilePath != "" {
if abs, err := filepath.Abs(cfg.EnvFilePath); err == nil {
cfg.EnvFilePath = abs
}
}

files := []string{"docker-compose.yml"}
if cfg.UseHTTPS {
files = append(files, "docker-compose-https-frontend.yml")
}

return &Service{
cfg: cfg,
executor: &DefaultExecutor{
cfg: cfg,
},
files: files,
}, nil
}

Expand Down Expand Up @@ -65,6 +79,12 @@ func (s *Service) StartDockerStack(ctx context.Context) error {
return err
}

if s.cfg.UseHTTPS {
if err := s.prepareHTTPS(); err != nil {
return err
}
}

if err := s.composeDown(ctx); err != nil {
return err
}
Expand All @@ -75,13 +95,33 @@ func (s *Service) StartDockerStack(ctx context.Context) error {
return nil
}

// prepareHTTPS validates that HTTPS certs already exist.
func (s *Service) prepareHTTPS() error {
certsDir := filepath.Join(s.cfg.WorkDir, "certs")
certPath := filepath.Join(certsDir, "stellar.local.pem")
keyPath := filepath.Join(certsDir, "stellar.local-key.pem")

if _, err := os.Stat(certPath); err == nil {
if _, err := os.Stat(keyPath); err == nil {
fmt.Println("Using TLS certs from dev/certs (HTTPS enabled)")
return nil
}
}

return fmt.Errorf("HTTPS selected but TLS certs are missing; expected dev/certs/stellar.local.pem and dev/certs/stellar.local-key.pem (generate with mkcert; see dev/README.md)")
}

// composeDown runs 'docker compose down' with the given config.
func (s *Service) composeDown(ctx context.Context) error {
fmt.Println("====> docker compose down")

args := []string{"compose", "-p", s.cfg.DockerProject}
args = append(args, "--env-file", s.cfg.EnvFilePath)
for _, file := range s.files {
args = append(args, "-f", file)
}
args = append(args, "down")
args = append(args, "--remove-orphans")

if err := s.executor.Execute(ctx, "docker", args...); err != nil {
return fmt.Errorf("running docker compose down: %w", err)
Expand All @@ -95,6 +135,9 @@ func (s *Service) composeUp(ctx context.Context) error {

args := []string{"compose", "-p", s.cfg.DockerProject}
args = append(args, "--env-file", s.cfg.EnvFilePath)
for _, file := range s.files {
args = append(args, "-f", file)
}
args = append(args, "up", "-d")

if err := s.executor.Execute(ctx, "docker", args...); err != nil {
Expand Down
Loading