mirror of
https://github.com/juanfont/headscale.git
synced 2025-06-01 01:15:56 +02:00
Merge 42ff11a133
into 109989005d
This commit is contained in:
commit
397c0db2f1
@ -238,6 +238,15 @@ policy:
|
||||
# HuJSON file containing ACL policies.
|
||||
path: ""
|
||||
|
||||
|
||||
certificates:
|
||||
enabled: false
|
||||
# Path to an executable that will be called when dns01 challenge is raised by tailscale client.
|
||||
# Command will be called with 3 arguments: <domain>,<type>,<value>
|
||||
# Eg: /path/to/set-dns-command "_acme-challenge.node1.example.com" "TXT" "jYhsfThsdf_Lo3shgdBRY7hNxe"
|
||||
set_dns_command: ""
|
||||
|
||||
|
||||
## DNS
|
||||
#
|
||||
# headscale supports Tailscale's DNS configuration and MagicDNS.
|
||||
|
@ -134,6 +134,13 @@ func generateDNSConfig(
|
||||
|
||||
addNextDNSMetadata(dnsConfig.Resolvers, node)
|
||||
|
||||
hostname, err := node.GetFQDN(cfg.BaseDomain)
|
||||
if err != nil {
|
||||
log.Warn().Msgf("failed to get FQDN of node %s for certDomains: %s", node.ID, err)
|
||||
} else {
|
||||
dnsConfig.CertDomains = append(dnsConfig.CertDomains, hostname)
|
||||
}
|
||||
|
||||
return dnsConfig
|
||||
}
|
||||
|
||||
|
@ -121,6 +121,10 @@ func tailNode(
|
||||
tailcfg.CapabilitySSH: []tailcfg.RawMessage{},
|
||||
}
|
||||
|
||||
if cfg.CertificatesFeatureConfig.Enabled {
|
||||
tNode.CapMap[tailcfg.CapabilityHTTPS] = []tailcfg.RawMessage{}
|
||||
}
|
||||
|
||||
if cfg.RandomizeClientPort {
|
||||
tNode.CapMap[tailcfg.NodeAttrRandomizeClientPort] = []tailcfg.RawMessage{}
|
||||
}
|
||||
|
@ -7,6 +7,8 @@ import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"os/exec"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/juanfont/headscale/hscontrol/capver"
|
||||
@ -101,6 +103,7 @@ func (h *Headscale) NoiseUpgradeHandler(
|
||||
router.HandleFunc("/machine/register", noiseServer.NoiseRegistrationHandler).
|
||||
Methods(http.MethodPost)
|
||||
router.HandleFunc("/machine/map", noiseServer.NoisePollNetMapHandler)
|
||||
router.HandleFunc("/machine/set-dns", noiseServer.SetDNSHandler).Methods(http.MethodPost)
|
||||
|
||||
noiseServer.httpBaseConfig = &http.Server{
|
||||
Handler: router,
|
||||
@ -230,6 +233,73 @@ func (ns *noiseServer) NoisePollNetMapHandler(
|
||||
}
|
||||
}
|
||||
|
||||
func (ns *noiseServer) SetDNSHandler(
|
||||
writer http.ResponseWriter,
|
||||
req *http.Request,
|
||||
) {
|
||||
body, _ := io.ReadAll(req.Body)
|
||||
|
||||
setDnsRequest := tailcfg.SetDNSRequest{}
|
||||
if err := json.Unmarshal(body, &setDnsRequest); err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Cannot parse MapRequest")
|
||||
http.Error(writer, "Internal error", http.StatusInternalServerError)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
log.Info().
|
||||
Caller().
|
||||
Str("handler", "NoisePollNetMap").
|
||||
Any("headers", req.Header).
|
||||
Str("NodeKey", setDnsRequest.NodeKey.ShortString()).
|
||||
Str("Name", setDnsRequest.Name).
|
||||
Str("Type", setDnsRequest.Type).
|
||||
Str("Value", setDnsRequest.Value).
|
||||
Msg("SetDNSHandler called")
|
||||
|
||||
if !ns.headscale.cfg.CertificatesFeatureConfig.Enabled {
|
||||
http.Error(writer, "certificates feature is not enabled in headscale", http.StatusForbidden)
|
||||
return
|
||||
}
|
||||
cmd := exec.Command(ns.headscale.cfg.CertificatesFeatureConfig.SetDNSCommand, setDnsRequest.Name, setDnsRequest.Type, setDnsRequest.Value)
|
||||
cmd.Stdout = os.Stderr
|
||||
cmd.Stderr = os.Stderr
|
||||
err := cmd.Run()
|
||||
|
||||
if err != nil {
|
||||
log.Error().AnErr("error", err).
|
||||
Strs("args", cmd.Args).
|
||||
Str("NodeKey", setDnsRequest.NodeKey.ShortString()).
|
||||
Str("DnsName", setDnsRequest.Name).
|
||||
Msg("Error running set_dns_command")
|
||||
http.Error(writer, "Failed to execute SetDNSCommand", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
resp := tailcfg.SetDNSResponse{}
|
||||
respBody, err := json.Marshal(resp)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Msg("Cannot encode message")
|
||||
http.Error(writer, "Internal server error", http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
|
||||
writer.Header().Set("Content-Type", "application/json; charset=utf-8")
|
||||
_, err = writer.Write(respBody)
|
||||
if err != nil {
|
||||
log.Error().
|
||||
Caller().
|
||||
Err(err).
|
||||
Msg("Failed to write response")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func regErr(err error) *tailcfg.RegisterResponse {
|
||||
return &tailcfg.RegisterResponse{Error: err.Error()}
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ type Config struct {
|
||||
// it can be used directly when sending Netmaps to clients.
|
||||
TailcfgDNSConfig *tailcfg.DNSConfig
|
||||
|
||||
CertificatesFeatureConfig CertificatesFeatureConfig
|
||||
|
||||
UnixSocket string
|
||||
UnixSocketPermission fs.FileMode
|
||||
|
||||
@ -165,6 +167,11 @@ type LetsEncryptConfig struct {
|
||||
ChallengeType string
|
||||
}
|
||||
|
||||
type CertificatesFeatureConfig struct {
|
||||
Enabled bool
|
||||
SetDNSCommand string
|
||||
}
|
||||
|
||||
type PKCEConfig struct {
|
||||
Enabled bool
|
||||
Method string
|
||||
@ -282,6 +289,9 @@ func LoadConfig(path string, isFile bool) error {
|
||||
viper.SetDefault("tls_letsencrypt_cache_dir", "/var/www/.cache")
|
||||
viper.SetDefault("tls_letsencrypt_challenge_type", HTTP01ChallengeType)
|
||||
|
||||
viper.SetDefault("certificates.enabled", false)
|
||||
viper.SetDefault("certificates.set_dns_command", "")
|
||||
|
||||
viper.SetDefault("log.level", "info")
|
||||
viper.SetDefault("log.format", TextLogFormat)
|
||||
|
||||
@ -401,6 +411,10 @@ func validateServerConfig() error {
|
||||
errorText += "Fatal config error: the only supported values for tls_letsencrypt_challenge_type are HTTP-01 and TLS-ALPN-01\n"
|
||||
}
|
||||
|
||||
if (viper.GetBool("certificates.enabled") == true) && viper.GetString("certificates.set_dns_command") == "" {
|
||||
errorText += "Fatal config error: certificates.enabled is set to true, but certificates.set_dns_command is not set\n"
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(viper.GetString("server_url"), "http://") &&
|
||||
!strings.HasPrefix(viper.GetString("server_url"), "https://") {
|
||||
errorText += "Fatal config error: server_url must start with https:// or http://\n"
|
||||
@ -911,6 +925,11 @@ func LoadServerConfig() (*Config, error) {
|
||||
ACMEEmail: viper.GetString("acme_email"),
|
||||
ACMEURL: viper.GetString("acme_url"),
|
||||
|
||||
CertificatesFeatureConfig: CertificatesFeatureConfig{
|
||||
Enabled: viper.GetBool("certificates.enabled"),
|
||||
SetDNSCommand: os.ExpandEnv(viper.GetString("certificates.set_dns_command")),
|
||||
},
|
||||
|
||||
UnixSocket: viper.GetString("unix_socket"),
|
||||
UnixSocketPermission: util.GetFileMode("unix_socket_permission"),
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user