Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
801fff9
feat(init): run Sentry with mTLS enabled in self-hosted init
ep0ll Oct 30, 2026
d05160c
feat(uninstall): remove Sentry container if present
ep0ll Oct 30, 2026
92d253a
chore(status-msg): show status message when dapr sentry is running
ep0ll Oct 31, 2026
6a39d78
Inject self-hosted mTLS env vars into daprd and apps in dapr run
ep0ll Oct 31, 2026
648953f
fix(sentry): prevent port conflicts and use absolute volume paths
ep0ll Oct 31, 2026
4a23216
ensure certs exists when MTLS enabled
ep0ll Nov 1, 2026
8f838d2
test(mtls): characterize existing self-hosted mTLS behavior
ep0ll Nov 1, 2026
c4a8538
Self-hosted: opt-in, and refactor code
ep0ll Nov 1, 2026
5b88bc8
llm generated tests
ep0ll Nov 1, 2026
ea4395f
fix tests excet in run_test.go file
ep0ll Nov 1, 2026
15ec68e
tests: add e2e tests
ep0ll Nov 3, 2026
ef7b472
DRY run_file_config_mtls_test.go code
ep0ll Nov 4, 2026
06ac61e
tests: DRY tests and remove duplicate code and unwanted tests
ep0ll Nov 4, 2026
8d489d4
e2e: verify --enable-mtls starts sentry with certs written
ep0ll Nov 4, 2026
1fbec77
e2e: add missing import
ep0ll Nov 4, 2026
a2e303f
gha: just a commit to run gha on my fork
ep0ll Nov 4, 2026
f61a281
make /Users/sai/.dapr/certs world writable
ep0ll Nov 4, 2026
d629a39
make /Users/sai/.dapr/certs world writable
ep0ll Nov 4, 2026
5035df9
feat(init): run Sentry with mTLS enabled in self-hosted init
ep0ll Oct 30, 2026
493708d
feat(uninstall): remove Sentry container if present
ep0ll Oct 30, 2026
cc8bc0e
chore(status-msg): show status message when dapr sentry is running
ep0ll Oct 31, 2026
7261627
Inject self-hosted mTLS env vars into daprd and apps in dapr run
ep0ll Oct 31, 2026
85b0fd1
fix(sentry): prevent port conflicts and use absolute volume paths
ep0ll Oct 31, 2026
f4b074d
ensure certs exists when MTLS enabled
ep0ll Nov 1, 2026
5a17c96
Self-hosted: opt-in, and refactor code
ep0ll Nov 1, 2026
3e808c4
test(mtls): characterize existing self-hosted mTLS behavior
ep0ll Nov 1, 2026
4d54e7e
fix tests excet in run_test.go file
ep0ll Nov 1, 2026
d517f0b
tests: add e2e tests
ep0ll Nov 3, 2026
1667249
DRY run_file_config_mtls_test.go code
ep0ll Nov 4, 2026
7ec4b63
tests: DRY tests and remove duplicate code and unwanted tests
ep0ll Nov 4, 2026
f770ee1
e2e: verify --enable-mtls starts sentry with certs written
ep0ll Nov 4, 2026
57a350e
e2e: add missing import
ep0ll Nov 4, 2026
aa400c4
gha: just a commit to run gha on my fork
ep0ll Nov 4, 2026
5c97ee0
make /Users/sai/.dapr/certs world writable
ep0ll Nov 4, 2026
1b1ca77
make /Users/sai/.dapr/certs world writable
ep0ll Nov 4, 2026
0bcfafe
docs: document opt-in self-hosted mTLS
ep0ll Nov 5, 2026
5789cc6
use uid:gid to start container with writable volume
ep0ll Nov 7, 2026
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
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,18 @@ This step creates the following defaults:
2. component files in the components folder called `pubsub.yaml` and `statestore.yaml`.
3. default config file `$HOME/.dapr/config.yaml` for Linux/MacOS or for Windows at `%USERPROFILE%\.dapr\config.yaml` to enable tracing on `dapr init` call. Can be overridden with the `--config` flag on `dapr run`.

#### Enable mTLS for self-hosted Dapr

mTLS is disabled by default in self-hosted mode. To enable mTLS, pass the `--enable-mtls` flag when initializing Dapr:

```bash
dapr init --enable-mtls
```

When enabled, the CLI starts the local Sentry service, generates the required certificates, and enables mTLS in the default Dapr configuration. The CLI also makes the generated certificates available automatically when you run an app with `dapr run`, including apps launched through a run file. You do not need to configure the mTLS environment variables manually.

Without `--enable-mtls`, self-hosted initialization behaves as before and mTLS remains disabled.

#### Slim Init

Alternatively to the above, to have the CLI not install any default configuration files or run Docker containers, use the `--slim` flag with the init command. Only Dapr binaries will be installed.
Expand Down
6 changes: 5 additions & 1 deletion cmd/init.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,9 @@ dapr init --runtime-path <path-to-install-directory>
# Initialize Dapr in self-hosted mode with Redis Stack for RediSearch/vector store support
dapr init --redis-stack

# Initialize Dapr in self-hosted mode with mTLS enabled for sentry service
dapr init --enable-mtls

# See more at: https://docs.dapr.io/getting-started/
`,
Run: func(cmd *cobra.Command, args []string) {
Expand Down Expand Up @@ -206,6 +209,7 @@ dapr init --redis-stack
SchedulerOverrideBroadcastHostPort: schedulerHostPort,
SchedulerPlacement: schedulerPlacement,
RedisStack: redisStack,
EnableMTLS: enableMTLS && cmd.Flags().Changed("enable-mtls"),
})
if err != nil {
print.FailureStatusEvent(os.Stderr, "%s", err.Error())
Expand Down Expand Up @@ -248,7 +252,7 @@ func init() {
InitCmd.Flags().BoolVarP(&slimMode, "slim", "s", false, "Exclude placement service, scheduler service, Redis and Zipkin containers from self-hosted installation")
InitCmd.Flags().StringVarP(&runtimeVersion, "runtime-version", "", defaultRuntimeVersion, "The version of the Dapr runtime to install, for example: 1.0.0")
InitCmd.Flags().StringVarP(&initNamespace, "namespace", "n", "dapr-system", "The Kubernetes namespace to install Dapr in")
InitCmd.Flags().BoolVarP(&enableMTLS, "enable-mtls", "", true, "Enable mTLS in your cluster")
InitCmd.Flags().BoolVarP(&enableMTLS, "enable-mtls", "", true, "Enable mTLS. Kubernetes: on by default. Self-hosted: opt-in, installs and runs the Sentry service")
InitCmd.Flags().BoolVarP(&enableHA, "enable-ha", "", false, "Enable high availability (HA) mode")
InitCmd.Flags().String("network", "", "The Docker network on which to deploy the Dapr runtime")
InitCmd.Flags().StringVarP(&fromDir, "from-dir", "", "", "Use Dapr artifacts from local directory for self-hosted installation")
Expand Down
17 changes: 17 additions & 0 deletions cmd/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,22 @@ dapr run --run-file /path/to/directory -k
}
}

var mTLSEnv = make(map[string]string, 4)
if standalone.IsMTLSEnabled(configFile) {
certsDir := standalone.GetDaprSentryCertsPath(daprDirPath)
if !standalone.EnsureCerts(certsDir) {
print.FailureStatusEvent(os.Stderr, "MTLS enabled, but no certs foudn in dir %s", certsDir)
os.Exit(1)
}

var err error
mTLSEnv, err = standalone.MTLSEnv(daprDirPath)
if err != nil {
print.FailureStatusEvent(os.Stderr, "Failed to load mTLS environment: %v", err)
os.Exit(1)
}
}

sharedRunConfig := &standalone.SharedRunConfig{
ConfigFile: configFile,
EnableProfiling: enableProfiling,
Expand All @@ -210,6 +226,7 @@ dapr run --run-file /path/to/directory -k
EnableAPILogging: enableAPILogging,
APIListenAddresses: apiListenAddresses,
DaprdInstallPath: cmdruntime.GetDaprRuntimePath(),
Env: mTLSEnv,
}

// placement-host-address flag handling: only set pointer if flag was explicitly changed
Expand Down
184 changes: 184 additions & 0 deletions pkg/runfileconfig/run_file_config_mtls_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
/*
Copyright 2026 The Dapr Authors
Licensed under the Apache License, Version 2.0
(the "License");
you may not use this file except in compliance with
the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed
on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
OF ANY KIND, either express or implied.
See the License for the specific language governing
permissions and limitations under the License.
*/

package runfileconfig

import (
"fmt"
"os"
"path/filepath"
"testing"

"github.com/dapr/cli/pkg/standalone"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"gopkg.in/yaml.v2"
)

func setupRunFileMTLSTest(
t *testing.T,
mtlsEnabled bool,
createCerts bool,
) (string, string) {
t.Helper()

runtimePath := t.TempDir()
t.Setenv("DAPR_RUNTIME_PATH", runtimePath)

daprDir, err := standalone.GetDaprRuntimePath(runtimePath)
require.NoError(t, err)

require.NoError(t, os.MkdirAll(daprDir, 0o755))

config := fmt.Sprintf(`apiVersion: dapr.io/v1alpha1
kind: Configuration
metadata:
name: daprConfig
spec:
mtls:
enabled: %t
`, mtlsEnabled)

configFile := standalone.GetDaprConfigPath(daprDir)

require.NoError(t, os.WriteFile(
configFile,
[]byte(config),
0o600,
))

if createCerts {
certsDir := standalone.GetDaprSentryCertsPath(daprDir)
require.NoError(t, os.MkdirAll(certsDir, 0o755))

for _, cert := range []struct {
name string
content string
}{
{"ca.crt", "CA"},
{"issuer.crt", "CHAIN"},
{"issuer.key", "KEY"},
} {
require.NoError(t, os.WriteFile(
filepath.Join(certsDir, cert.name),
[]byte(cert.content),
0o600,
))
}
}

return daprDir, configFile
}

func createMTLSRunFile(
t *testing.T,
configFile string,
appDir string,
appEnv map[string]string,
) string {
t.Helper()

config := RunFileConfig{
Version: 1,
Common: Common{
SharedRunConfig: standalone.SharedRunConfig{
ConfigFile: configFile,
},
},
Apps: []App{
{
AppDirPath: appDir,
RunConfig: standalone.RunConfig{
SharedRunConfig: standalone.SharedRunConfig{
Env: appEnv,
},
AppID: "mtls-test-app",
},
},
},
}

content, err := yaml.Marshal(&config)
require.NoError(t, err)

runFile := filepath.Join(t.TempDir(), "dapr.yaml")
require.NoError(t, os.WriteFile(runFile, content, 0o600))

return runFile
}

func TestGetAppsMTLSEnv(t *testing.T) {
t.Run("loads generated certificates and preserves app environment", func(t *testing.T) {
_, configFile := setupRunFileMTLSTest(t, true, true)

appDir := t.TempDir()
runFile := createMTLSRunFile(
t,
configFile,
appDir,
map[string]string{
"DAPR_CERT_KEY": "user-defined-key",
"CUSTOM_ENV": "application-value",
},
)

cfg := &RunFileConfig{}

apps, err := cfg.GetApps(runFile)

require.NoError(t, err)
require.Len(t, apps, 1)

assert.Equal(t, "CA", apps[0].Env[standalone.EnvDaprTrustAnchors])
assert.Equal(t, "CHAIN", apps[0].Env[standalone.EnvDaprCertChain])

// Application-defined environment takes precedence.
assert.Equal(t, "user-defined-key", apps[0].Env[standalone.EnvDaprCertKey])
assert.Equal(t, "application-value", apps[0].Env["CUSTOM_ENV"])
})

t.Run("disabled mTLS does not inject existing certificates", func(t *testing.T) {
_, configFile := setupRunFileMTLSTest(t, false, true)

appDir := t.TempDir()
runFile := createMTLSRunFile(t, configFile, appDir, nil)

cfg := &RunFileConfig{}

apps, err := cfg.GetApps(runFile)

require.NoError(t, err)
require.Len(t, apps, 1)

assert.NotContains(t, apps[0].Env, standalone.EnvDaprTrustAnchors)
assert.NotContains(t, apps[0].Env, standalone.EnvDaprCertChain)
assert.NotContains(t, apps[0].Env, standalone.EnvDaprCertKey)
})

t.Run("missing certificates return an error when mTLS is enabled", func(t *testing.T) {
_, configFile := setupRunFileMTLSTest(t, true, false)

appDir := t.TempDir()
runFile := createMTLSRunFile(t, configFile, appDir, nil)

cfg := &RunFileConfig{}

_, err := cfg.GetApps(runFile)

require.Error(t, err)
assert.Contains(t, err.Error(), "sentry certs")
})
}
45 changes: 45 additions & 0 deletions pkg/runfileconfig/run_file_config_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"reflect"
"strings"

cmdruntime "github.com/dapr/cli/cmd/runtime"
"github.com/dapr/cli/pkg/standalone"
"github.com/dapr/cli/utils"

Expand All @@ -28,6 +29,44 @@ import (

var imagePullPolicyValuesAllowed = []string{"Always", "Never", "IfNotPresent"}

func (a *RunFileConfig) addMTLSEnvToCommon() error {
daprDir, err := standalone.GetDaprRuntimePath(cmdruntime.GetDaprRuntimePath())
if err != nil {
return fmt.Errorf("error getting dapr install path: %w", err)
}

configFile := a.Common.ConfigFile
if configFile == "" {
configFile = standalone.GetDaprConfigPath(daprDir)
}

if !standalone.IsMTLSEnabled(configFile) {
return nil
}

certsDir := standalone.GetDaprSentryCertsPath(daprDir)
if !standalone.EnsureCerts(certsDir) {
return fmt.Errorf("error getting sentry certs")
}

env, err := standalone.MTLSEnv(daprDir)
if err != nil {
return err
}

if a.Common.Env == nil {
a.Common.Env = make(map[string]string, len(env))
}

for key, value := range env {
if _, exists := a.Common.Env[key]; !exists {
a.Common.Env[key] = value
}
}

return nil
}

// Parse the provided run file into a RunFileConfig struct.
func (a *RunFileConfig) parseAppsConfig(runFilePath string) error {
var err error
Expand Down Expand Up @@ -128,6 +167,9 @@ func (a *RunFileConfig) GetApps(runFilePath string) ([]App, error) {
return nil, err
}
a.mergeCommonAndAppsSharedRunConfig()
if err = a.addMTLSEnvToCommon(); err != nil {
return nil, err
}
a.mergeCommonAndAppsEnv()

// Set and validates default fields in the run file.
Expand Down Expand Up @@ -257,6 +299,9 @@ func (a *RunFileConfig) resolveResourcesAndConfigFilePaths() error {
// Precedence order for envs -> apps[i].envs > common.envs.
func (a *RunFileConfig) mergeCommonAndAppsEnv() {
for i := range a.Apps {
if a.Apps[i].Env == nil && len(a.Common.Env) > 0 {
a.Apps[i].Env = make(map[string]string, len(a.Common.Env))
}
for k, v := range a.Common.Env {
if _, ok := a.Apps[i].Env[k]; !ok {
a.Apps[i].Env[k] = v
Expand Down
43 changes: 43 additions & 0 deletions pkg/standalone/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ import (
path_filepath "path/filepath"
"runtime"
"strings"

"gopkg.in/yaml.v2"
)

const (
Expand All @@ -29,6 +31,7 @@ const (
defaultComponentsDirName = "components"
defaultSchedulerDirName = "scheduler"
defaultSchedulerDataDirName = "data"
defaultSentryCertsDirName = "certs"
)

// GetDaprRuntimePath returns the dapr runtime installation path.
Expand Down Expand Up @@ -84,3 +87,43 @@ func GetDaprComponentsPath(daprDir string) string {
func GetDaprConfigPath(daprDir string) string {
return path_filepath.Join(daprDir, DefaultConfigFileName)
}

func GetDaprSentryCertsPath(daprDir string) string {
return path_filepath.Join(daprDir, defaultSentryCertsDirName)
}

func GetMTLSConfig(configFile string) (mtlsConfig, error) {
b, err := os.ReadFile(configFile)
if err != nil {
return mtlsConfig{}, err
}

return parseMTLSConfig(b)
}

func parseMTLSConfig(configBytes []byte) (mtlsConfig, error) {
var config mtlsConfig
err := yaml.Unmarshal(configBytes, &config)
if err != nil {
return mtlsConfig{}, err
}

return config, nil
}

func IsMTLSEnabled(configFile string) bool {
if configFile == "" {
daprDir, err := GetDaprRuntimePath("")
if err != nil {
return false
}
configFile = GetDaprConfigPath(daprDir)
}

mtlsConfig, err := GetMTLSConfig(configFile)
if err != nil {
return false
}

return mtlsConfig.Spec.MTLS.Enabled
}
Loading