mirror of
https://github.com/juanfont/headscale.git
synced 2024-11-26 08:53:05 +00:00
Compare commits
7 commits
0c98d09783
...
ca9938f4e9
Author | SHA1 | Date | |
---|---|---|---|
|
ca9938f4e9 | ||
|
1fc14790f9 | ||
|
6d9e5b0022 | ||
|
9163edcf50 | ||
|
50a7315226 | ||
|
bbe1327785 | ||
|
e72bd1cc8c |
8 changed files with 170 additions and 76 deletions
|
@ -60,9 +60,9 @@ func (*Suite) TestConfigFileLoading(c *check.C) {
|
|||
c.Assert(viper.GetString("metrics_listen_addr"), check.Equals, "127.0.0.1:9090")
|
||||
c.Assert(viper.GetString("database.type"), check.Equals, "sqlite")
|
||||
c.Assert(viper.GetString("database.sqlite.path"), check.Equals, "/var/lib/headscale/db.sqlite")
|
||||
c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "")
|
||||
c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http")
|
||||
c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01")
|
||||
c.Assert(viper.GetString("tls.letsencrypt_hostname"), check.Equals, "")
|
||||
c.Assert(viper.GetString("tls.letsencrypt_listen"), check.Equals, ":http")
|
||||
c.Assert(viper.GetString("tls.letsencrypt_challenge_type"), check.Equals, "HTTP-01")
|
||||
c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1")
|
||||
c.Assert(
|
||||
util.GetFileMode("unix_socket_permission"),
|
||||
|
@ -103,9 +103,9 @@ func (*Suite) TestConfigLoading(c *check.C) {
|
|||
c.Assert(viper.GetString("metrics_listen_addr"), check.Equals, "127.0.0.1:9090")
|
||||
c.Assert(viper.GetString("database.type"), check.Equals, "sqlite")
|
||||
c.Assert(viper.GetString("database.sqlite.path"), check.Equals, "/var/lib/headscale/db.sqlite")
|
||||
c.Assert(viper.GetString("tls_letsencrypt_hostname"), check.Equals, "")
|
||||
c.Assert(viper.GetString("tls_letsencrypt_listen"), check.Equals, ":http")
|
||||
c.Assert(viper.GetString("tls_letsencrypt_challenge_type"), check.Equals, "HTTP-01")
|
||||
c.Assert(viper.GetString("tls.letsencrypt_hostname"), check.Equals, "")
|
||||
c.Assert(viper.GetString("tls.letsencrypt_listen"), check.Equals, ":http")
|
||||
c.Assert(viper.GetString("tls.letsencrypt_challenge_type"), check.Equals, "HTTP-01")
|
||||
c.Assert(viper.GetStringSlice("dns_config.nameservers")[0], check.Equals, "1.1.1.1")
|
||||
c.Assert(
|
||||
util.GetFileMode("unix_socket_permission"),
|
||||
|
@ -180,12 +180,12 @@ noise:
|
|||
c.Assert(
|
||||
tmp,
|
||||
check.Matches,
|
||||
".*Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both.*",
|
||||
".*Fatal config error: set either tls.letsencrypt_hostname or tls.cert_path/tls.key_path, not both.*",
|
||||
)
|
||||
c.Assert(
|
||||
tmp,
|
||||
check.Matches,
|
||||
".*Fatal config error: the only supported values for tls_letsencrypt_challenge_type are.*",
|
||||
".*Fatal config error: the only supported values for tls.letsencrypt_challenge_type are.*",
|
||||
)
|
||||
c.Assert(
|
||||
tmp,
|
||||
|
@ -193,6 +193,8 @@ noise:
|
|||
".*Fatal config error: server_url must start with https:// or http://.*",
|
||||
)
|
||||
|
||||
viper.Reset()
|
||||
|
||||
// Check configuration validation errors (2)
|
||||
configYaml = []byte(`---
|
||||
noise:
|
||||
|
|
|
@ -24,21 +24,22 @@ listen_addr: 127.0.0.1:8080
|
|||
#
|
||||
metrics_listen_addr: 127.0.0.1:9090
|
||||
|
||||
# Address to listen for gRPC.
|
||||
# gRPC is used for controlling a headscale server
|
||||
# remotely with the CLI
|
||||
# Note: Remote access _only_ works if you have
|
||||
# valid certificates.
|
||||
#
|
||||
# For production:
|
||||
# grpc_listen_addr: 0.0.0.0:50443
|
||||
grpc_listen_addr: 127.0.0.1:50443
|
||||
grpc:
|
||||
# Address to listen for gRPC.
|
||||
# Note: Remote access _only_ works if you have
|
||||
# valid certificates.
|
||||
#
|
||||
# For production:
|
||||
# listen_addr: 0.0.0.0:50443
|
||||
listen_addr: 127.0.0.1:50443
|
||||
|
||||
# Allow the gRPC admin interface to run in INSECURE
|
||||
# mode. This is not recommended as the traffic will
|
||||
# be unencrypted. Only enable if you know what you
|
||||
# are doing.
|
||||
grpc_allow_insecure: false
|
||||
# Allow the gRPC admin interface to run in INSECURE
|
||||
# mode. This is not recommended as the traffic will
|
||||
# be unencrypted. Only enable if you know what you
|
||||
# are doing.
|
||||
allow_insecure: false
|
||||
|
||||
# The Noise section includes specific configuration for the
|
||||
# TS2021 Noise protocol
|
||||
|
@ -165,38 +166,44 @@ database:
|
|||
# ssl: false
|
||||
|
||||
### TLS configuration
|
||||
#
|
||||
## Let's encrypt / ACME
|
||||
#
|
||||
# headscale supports automatically requesting and setting up
|
||||
# TLS for a domain with Let's Encrypt.
|
||||
#
|
||||
# URL to ACME directory
|
||||
acme_url: https://acme-v02.api.letsencrypt.org/directory
|
||||
tls:
|
||||
## Let's encrypt / ACME
|
||||
#
|
||||
# headscale supports automatically requesting and setting up
|
||||
# TLS for a domain with Let's Encrypt.
|
||||
#
|
||||
# URL to ACME directory
|
||||
acme_url: https://acme-v02.api.letsencrypt.org/directory
|
||||
|
||||
# Email to register with ACME provider
|
||||
acme_email: ""
|
||||
# Email to register with ACME provider
|
||||
acme_email: ""
|
||||
|
||||
# Domain name to request a TLS certificate for:
|
||||
tls_letsencrypt_hostname: ""
|
||||
# Domain name to request a TLS certificate for:
|
||||
letsencrypt_hostname: ""
|
||||
|
||||
# Path to store certificates and metadata needed by
|
||||
# letsencrypt
|
||||
# For production:
|
||||
tls_letsencrypt_cache_dir: /var/lib/headscale/cache
|
||||
# Path to store certificates and metadata needed by
|
||||
# letsencrypt
|
||||
# For production:
|
||||
letsencrypt_cache_dir: /var/lib/headscale/cache
|
||||
|
||||
# Type of ACME challenge to use, currently supported types:
|
||||
# HTTP-01 or TLS-ALPN-01
|
||||
# See [docs/tls.md](docs/tls.md) for more information
|
||||
tls_letsencrypt_challenge_type: HTTP-01
|
||||
# When HTTP-01 challenge is chosen, letsencrypt must set up a
|
||||
# verification endpoint, and it will be listening on:
|
||||
# :http = port 80
|
||||
tls_letsencrypt_listen: ":http"
|
||||
# Type of ACME challenge to use, currently supported types:
|
||||
# HTTP-01 or TLS-ALPN-01
|
||||
# See [docs/tls.md](docs/tls.md) for more information
|
||||
letsencrypt_challenge_type: HTTP-01
|
||||
# When HTTP-01 challenge is chosen, letsencrypt must set up a
|
||||
# verification endpoint, and it will be listening on:
|
||||
# :http = port 80
|
||||
letsencrypt_listen: ":http"
|
||||
|
||||
## Use already defined certificates:
|
||||
tls_cert_path: ""
|
||||
tls_key_path: ""
|
||||
## Use already defined certificates:
|
||||
cert_path: ""
|
||||
key_path: ""
|
||||
|
||||
# Use a separate x509 certificate for gRPC, this is used
|
||||
# instead of the global certificate.
|
||||
grpc:
|
||||
cert_path: ""
|
||||
key_path: ""
|
||||
|
||||
log:
|
||||
# Output formatting for logs: text or json
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
- Access to create API keys (local access to the `headscale` server)
|
||||
- `headscale` _must_ be served over TLS/HTTPS
|
||||
- Remote access does _not_ support unencrypted traffic.
|
||||
- Port `50443` must be open in the firewall (or port overridden by `grpc_listen_addr` option)
|
||||
- Port `50443` must be open in the firewall (or port overridden by `grpc.listen_addr` option)
|
||||
|
||||
## Goal
|
||||
|
||||
|
|
|
@ -653,9 +653,27 @@ func (h *Headscale) Serve() error {
|
|||
// https://github.com/soheilhy/cmux/issues/68
|
||||
// https://github.com/soheilhy/cmux/issues/91
|
||||
|
||||
grpcTlsConfig := &tls.Config{
|
||||
NextProtos: []string{"http/1.1"},
|
||||
Certificates: make([]tls.Certificate, 1),
|
||||
MinVersion: tls.VersionTLS12,
|
||||
}
|
||||
|
||||
if h.cfg.TLS.GRPCCertPath == "" && h.cfg.TLS.GRPCKeyPath == "" {
|
||||
grpcTlsConfig = tlsConfig
|
||||
} else {
|
||||
grpcTlsConfig.Certificates[0], err = tls.LoadX509KeyPair(h.cfg.TLS.GRPCCertPath, h.cfg.TLS.GRPCKeyPath)
|
||||
|
||||
if err != nil {
|
||||
log.Error().Err(err).Msg("Failed to set up gRPC TLS configuration")
|
||||
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
var grpcServer *grpc.Server
|
||||
var grpcListener net.Listener
|
||||
if tlsConfig != nil || h.cfg.GRPCAllowInsecure {
|
||||
if grpcTlsConfig != nil || h.cfg.GRPCAllowInsecure {
|
||||
log.Info().Msgf("Enabling remote gRPC at %s", h.cfg.GRPCAddr)
|
||||
|
||||
grpcOptions := []grpc.ServerOption{
|
||||
|
@ -668,9 +686,9 @@ func (h *Headscale) Serve() error {
|
|||
),
|
||||
}
|
||||
|
||||
if tlsConfig != nil {
|
||||
if grpcTlsConfig != nil {
|
||||
grpcOptions = append(grpcOptions,
|
||||
grpc.Creds(credentials.NewTLS(tlsConfig)),
|
||||
grpc.Creds(credentials.NewTLS(grpcTlsConfig)),
|
||||
)
|
||||
} else {
|
||||
log.Warn().Msg("gRPC is running without security")
|
||||
|
|
|
@ -108,8 +108,10 @@ type DatabaseConfig struct {
|
|||
}
|
||||
|
||||
type TLSConfig struct {
|
||||
CertPath string
|
||||
KeyPath string
|
||||
CertPath string
|
||||
KeyPath string
|
||||
GRPCCertPath string
|
||||
GRPCKeyPath string
|
||||
|
||||
LetsEncrypt LetsEncryptConfig
|
||||
}
|
||||
|
@ -178,6 +180,13 @@ type Tuning struct {
|
|||
NodeMapSessionBufferedChanSize int
|
||||
}
|
||||
|
||||
func RegisterDeprecatedAlias(old, new string) {
|
||||
if viper.IsSet(old) {
|
||||
log.Warn().Msgf("%s is deprecated and may be removed in future versions, please use %s instead.", old, new)
|
||||
viper.Set(new, viper.GetString(old))
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfig(path string, isFile bool) error {
|
||||
if isFile {
|
||||
viper.SetConfigFile(path)
|
||||
|
@ -197,8 +206,8 @@ func LoadConfig(path string, isFile bool) error {
|
|||
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
|
||||
viper.AutomaticEnv()
|
||||
|
||||
viper.SetDefault("tls_letsencrypt_cache_dir", "/var/www/.cache")
|
||||
viper.SetDefault("tls_letsencrypt_challenge_type", HTTP01ChallengeType)
|
||||
viper.SetDefault("tls.letsencrypt_cache_dir", "/var/www/.cache")
|
||||
viper.SetDefault("tls.letsencrypt_challenge_type", HTTP01ChallengeType)
|
||||
|
||||
viper.SetDefault("log.level", "info")
|
||||
viper.SetDefault("log.format", TextLogFormat)
|
||||
|
@ -214,8 +223,8 @@ func LoadConfig(path string, isFile bool) error {
|
|||
viper.SetDefault("unix_socket", "/var/run/headscale/headscale.sock")
|
||||
viper.SetDefault("unix_socket_permission", "0o770")
|
||||
|
||||
viper.SetDefault("grpc_listen_addr", ":50443")
|
||||
viper.SetDefault("grpc_allow_insecure", false)
|
||||
viper.SetDefault("grpc.listen_addr", ":50443")
|
||||
viper.SetDefault("grpc.allow_insecure", false)
|
||||
|
||||
viper.SetDefault("cli.timeout", "5s")
|
||||
viper.SetDefault("cli.insecure", false)
|
||||
|
@ -254,28 +263,39 @@ func LoadConfig(path string, isFile bool) error {
|
|||
return fmt.Errorf("fatal error reading config file: %w", err)
|
||||
}
|
||||
|
||||
RegisterDeprecatedAlias("grpc_listen_addr", "grpc.listen_addr")
|
||||
RegisterDeprecatedAlias("grpc_allow_insecure", "grpc.allow_insecure")
|
||||
RegisterDeprecatedAlias("acme_url", "tls.acme_url")
|
||||
RegisterDeprecatedAlias("acme_email", "tls.acme_email")
|
||||
RegisterDeprecatedAlias("tls_letsencrypt_hostname", "tls.letsencrypt_hostname")
|
||||
RegisterDeprecatedAlias("tls_letsencrypt_cache_dir", "tls.letsencrypt_cache_dir")
|
||||
RegisterDeprecatedAlias("tls_letsencrypt_challenge_type", "tls.letsencrypt_challenge_type")
|
||||
RegisterDeprecatedAlias("tls_letsencrypt_listen", "tls.letsencrypt_listen")
|
||||
RegisterDeprecatedAlias("tls_cert_path", "tls.cert_path")
|
||||
RegisterDeprecatedAlias("tls_key_path", "tls.key_path")
|
||||
|
||||
// Collect any validation errors and return them all at once
|
||||
var errorText string
|
||||
if (viper.GetString("tls_letsencrypt_hostname") != "") &&
|
||||
((viper.GetString("tls_cert_path") != "") || (viper.GetString("tls_key_path") != "")) {
|
||||
errorText += "Fatal config error: set either tls_letsencrypt_hostname or tls_cert_path/tls_key_path, not both\n"
|
||||
if (viper.GetString("tls.letsencrypt_hostname") != "") &&
|
||||
((viper.GetString("tls.cert_path") != "") || (viper.GetString("tls.key_path") != "")) {
|
||||
errorText += "Fatal config error: set either tls.letsencrypt_hostname or tls.cert_path/tls.key_path, not both\n"
|
||||
}
|
||||
|
||||
if !viper.IsSet("noise") || viper.GetString("noise.private_key_path") == "" {
|
||||
errorText += "Fatal config error: headscale now requires a new `noise.private_key_path` field in the config file for the Tailscale v2 protocol\n"
|
||||
}
|
||||
|
||||
if (viper.GetString("tls_letsencrypt_hostname") != "") &&
|
||||
(viper.GetString("tls_letsencrypt_challenge_type") == TLSALPN01ChallengeType) &&
|
||||
if (viper.GetString("tls.letsencrypt_hostname") != "") &&
|
||||
(viper.GetString("tls.letsencrypt_challenge_type") == TLSALPN01ChallengeType) &&
|
||||
(!strings.HasSuffix(viper.GetString("listen_addr"), ":443")) {
|
||||
// this is only a warning because there could be something sitting in front of headscale that redirects the traffic (e.g. an iptables rule)
|
||||
log.Warn().
|
||||
Msg("Warning: when using tls_letsencrypt_hostname with TLS-ALPN-01 as challenge type, headscale must be reachable on port 443, i.e. listen_addr should probably end in :443")
|
||||
Msg("Warning: when using tls.letsencrypt_hostname with TLS-ALPN-01 as challenge type, headscale must be reachable on port 443, i.e. listen_addr should probably end in :443")
|
||||
}
|
||||
|
||||
if (viper.GetString("tls_letsencrypt_challenge_type") != HTTP01ChallengeType) &&
|
||||
(viper.GetString("tls_letsencrypt_challenge_type") != TLSALPN01ChallengeType) {
|
||||
errorText += "Fatal config error: the only supported values for tls_letsencrypt_challenge_type are HTTP-01 and TLS-ALPN-01\n"
|
||||
if (viper.GetString("tls.letsencrypt_challenge_type") != HTTP01ChallengeType) &&
|
||||
(viper.GetString("tls.letsencrypt_challenge_type") != TLSALPN01ChallengeType) {
|
||||
errorText += "Fatal config error: the only supported values for tls.letsencrypt_challenge_type are HTTP-01 and TLS-ALPN-01\n"
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(viper.GetString("server_url"), "http://") &&
|
||||
|
@ -305,18 +325,24 @@ func LoadConfig(path string, isFile bool) error {
|
|||
func GetTLSConfig() TLSConfig {
|
||||
return TLSConfig{
|
||||
LetsEncrypt: LetsEncryptConfig{
|
||||
Hostname: viper.GetString("tls_letsencrypt_hostname"),
|
||||
Listen: viper.GetString("tls_letsencrypt_listen"),
|
||||
Hostname: viper.GetString("tls.letsencrypt_hostname"),
|
||||
Listen: viper.GetString("tls.letsencrypt_listen"),
|
||||
CacheDir: util.AbsolutePathFromConfigPath(
|
||||
viper.GetString("tls_letsencrypt_cache_dir"),
|
||||
viper.GetString("tls.letsencrypt_cache_dir"),
|
||||
),
|
||||
ChallengeType: viper.GetString("tls_letsencrypt_challenge_type"),
|
||||
ChallengeType: viper.GetString("tls.letsencrypt_challenge_type"),
|
||||
},
|
||||
CertPath: util.AbsolutePathFromConfigPath(
|
||||
viper.GetString("tls_cert_path"),
|
||||
viper.GetString("tls.cert_path"),
|
||||
),
|
||||
KeyPath: util.AbsolutePathFromConfigPath(
|
||||
viper.GetString("tls_key_path"),
|
||||
viper.GetString("tls.key_path"),
|
||||
),
|
||||
GRPCCertPath: util.AbsolutePathFromConfigPath(
|
||||
viper.GetString("tls.grpc.cert_path"),
|
||||
),
|
||||
GRPCKeyPath: util.AbsolutePathFromConfigPath(
|
||||
viper.GetString("tls.grpc.key_path"),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -698,8 +724,8 @@ func GetHeadscaleConfig() (*Config, error) {
|
|||
ServerURL: viper.GetString("server_url"),
|
||||
Addr: viper.GetString("listen_addr"),
|
||||
MetricsAddr: viper.GetString("metrics_listen_addr"),
|
||||
GRPCAddr: viper.GetString("grpc_listen_addr"),
|
||||
GRPCAllowInsecure: viper.GetBool("grpc_allow_insecure"),
|
||||
GRPCAddr: viper.GetString("grpc.listen_addr"),
|
||||
GRPCAllowInsecure: viper.GetBool("grpc.allow_insecure"),
|
||||
DisableUpdateCheck: viper.GetBool("disable_check_updates"),
|
||||
|
||||
PrefixV4: prefix4,
|
||||
|
@ -724,8 +750,8 @@ func GetHeadscaleConfig() (*Config, error) {
|
|||
DNSConfig: dnsConfig,
|
||||
DNSUserNameInMagicDNS: viper.GetBool("dns_config.use_username_in_magic_dns"),
|
||||
|
||||
ACMEEmail: viper.GetString("acme_email"),
|
||||
ACMEURL: viper.GetString("acme_url"),
|
||||
ACMEEmail: viper.GetString("tls.acme_email"),
|
||||
ACMEURL: viper.GetString("tls.acme_url"),
|
||||
|
||||
UnixSocket: viper.GetString("unix_socket"),
|
||||
UnixSocketPermission: util.GetFileMode("unix_socket_permission"),
|
||||
|
|
|
@ -43,6 +43,7 @@ func TestDERPServerScenario(t *testing.T) {
|
|||
hsic.WithExtraPorts([]string{"3478/udp"}),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithGrpcTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
|
|
@ -41,6 +41,7 @@ func TestPingAllByIP(t *testing.T) {
|
|||
hsic.WithTestName("pingallbyip"),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithGrpcTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
hsic.WithIPAllocationStrategy(types.IPAllocationStrategyRandom),
|
||||
)
|
||||
|
@ -836,6 +837,7 @@ func TestPingAllByIPManyUpDown(t *testing.T) {
|
|||
hsic.WithTestName("pingallbyipmany"),
|
||||
hsic.WithEmbeddedDERPServerOnly(),
|
||||
hsic.WithTLS(),
|
||||
hsic.WithGrpcTLS(),
|
||||
hsic.WithHostnameAsServerURL(),
|
||||
)
|
||||
assertNoErrHeadscaleEnv(t, err)
|
||||
|
|
|
@ -40,6 +40,8 @@ const (
|
|||
aclPolicyPath = "/etc/headscale/acl.hujson"
|
||||
tlsCertPath = "/etc/headscale/tls.cert"
|
||||
tlsKeyPath = "/etc/headscale/tls.key"
|
||||
grpcTlsCertPath = "/etc/headscale/grpc_tls.cert"
|
||||
grpcTlsKeyPath = "/etc/headscale/grpc_tls.key"
|
||||
headscaleDefaultPort = 8080
|
||||
)
|
||||
|
||||
|
@ -69,6 +71,8 @@ type HeadscaleInContainer struct {
|
|||
env map[string]string
|
||||
tlsCert []byte
|
||||
tlsKey []byte
|
||||
grpcTlsCert []byte
|
||||
grpcTlsKey []byte
|
||||
filesInContainer []fileInContainer
|
||||
postgres bool
|
||||
}
|
||||
|
@ -105,6 +109,19 @@ func WithTLS() Option {
|
|||
}
|
||||
}
|
||||
|
||||
// WithGrpcTLS creates gRPC certificates and enables them.
|
||||
func WithGrpcTLS() Option {
|
||||
return func(hsic *HeadscaleInContainer) {
|
||||
cert, key, err := createCertificate(hsic.hostname)
|
||||
if err != nil {
|
||||
log.Fatalf("failed to create grpc certificates for headscale test: %s", err)
|
||||
}
|
||||
|
||||
hsic.grpcTlsCert = cert
|
||||
hsic.grpcTlsKey = key
|
||||
}
|
||||
}
|
||||
|
||||
// WithConfigEnv takes a map of environment variables that
|
||||
// can be used to override Headscale configuration.
|
||||
func WithConfigEnv(configEnv map[string]string) Option {
|
||||
|
@ -253,6 +270,11 @@ func New(
|
|||
hsic.env["HEADSCALE_SERVER_URL"] = serverURL.String()
|
||||
}
|
||||
|
||||
if hsic.hasGrpcTLS() {
|
||||
hsic.env["HEADSCALE_GRPC_TLS_CERT_PATH"] = grpcTlsCertPath
|
||||
hsic.env["HEADSCALE_GRPC_TLS_KEY_PATH"] = grpcTlsKeyPath
|
||||
}
|
||||
|
||||
headscaleBuildOptions := &dockertest.BuildOptions{
|
||||
Dockerfile: "Dockerfile.debug",
|
||||
ContextDir: dockerContextPath,
|
||||
|
@ -374,6 +396,18 @@ func New(
|
|||
}
|
||||
}
|
||||
|
||||
if hsic.hasGrpcTLS() {
|
||||
err = hsic.WriteFile(grpcTlsCertPath, hsic.grpcTlsCert)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write TLS certificate to container: %w", err)
|
||||
}
|
||||
|
||||
err = hsic.WriteFile(grpcTlsKeyPath, hsic.grpcTlsKey)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to write TLS key to container: %w", err)
|
||||
}
|
||||
}
|
||||
|
||||
for _, f := range hsic.filesInContainer {
|
||||
if err := hsic.WriteFile(f.path, f.contents); err != nil {
|
||||
return nil, fmt.Errorf("failed to write %q: %w", f.path, err)
|
||||
|
@ -391,6 +425,10 @@ func (t *HeadscaleInContainer) hasTLS() bool {
|
|||
return len(t.tlsCert) != 0 && len(t.tlsKey) != 0
|
||||
}
|
||||
|
||||
func (t *HeadscaleInContainer) hasGrpcTLS() bool {
|
||||
return len(t.grpcTlsCert) != 0 && len(t.grpcTlsKey) != 0
|
||||
}
|
||||
|
||||
// Shutdown stops and cleans up the Headscale container.
|
||||
func (t *HeadscaleInContainer) Shutdown() error {
|
||||
err := t.SaveLog("/tmp/control")
|
||||
|
|
Loading…
Reference in a new issue