Support requiring TLS for API access
Signed-off-by: Jonathon Anderson <janderson@ciq.com>
This commit is contained in:
@@ -117,7 +117,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS13}
|
tlsConfig := &tls.Config{MinVersion: tls.VersionTLS13}
|
||||||
if conf.Warewulf.EnableTLS() {
|
if conf.Warewulf.TLSEnabled() {
|
||||||
caCert, err := os.ReadFile("/warewulf/tls/warewulf.crt")
|
caCert, err := os.ReadFile("/warewulf/tls/warewulf.crt")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
wwlog.Error("failed to read ca cert: %s", err)
|
wwlog.Error("failed to read ca cert: %s", err)
|
||||||
@@ -260,7 +260,7 @@ func CobraRunE(cmd *cobra.Command, args []string) (err error) {
|
|||||||
|
|
||||||
port := conf.Warewulf.Port
|
port := conf.Warewulf.Port
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
if conf.Warewulf.EnableTLS() {
|
if conf.Warewulf.TLSEnabled() {
|
||||||
port = conf.Warewulf.TlsPort
|
port = conf.Warewulf.TlsPort
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ func CobraRunE(cmd *cobra.Command, args []string) error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if !conf.Warewulf.EnableTLS() {
|
if !conf.Warewulf.TLSEnabled() {
|
||||||
fmt.Fprintf(cmd.OutOrStdout(), "TLS is not enabled in warewulf.conf\n")
|
fmt.Fprintf(cmd.OutOrStdout(), "TLS is not enabled in warewulf.conf\n")
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ func (n IPNet) IPNet() net.IPNet {
|
|||||||
|
|
||||||
type APIConf struct {
|
type APIConf struct {
|
||||||
EnabledP *bool `yaml:"enabled,omitempty" default:"false"`
|
EnabledP *bool `yaml:"enabled,omitempty" default:"false"`
|
||||||
|
TLSEnabledP *bool `yaml:"tls,omitempty"`
|
||||||
AllowedNets []IPNet `yaml:"allowed subnets,omitempty" default:"[\"127.0.0.0/8\", \"::1/128\"]"`
|
AllowedNets []IPNet `yaml:"allowed subnets,omitempty" default:"[\"127.0.0.0/8\", \"::1/128\"]"`
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,3 +50,7 @@ func (conf *APIConf) Unmarshal(unmarshal func(interface{}) error) error {
|
|||||||
func (conf APIConf) Enabled() bool {
|
func (conf APIConf) Enabled() bool {
|
||||||
return util.BoolP(conf.EnabledP)
|
return util.BoolP(conf.EnabledP)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (conf APIConf) TLSEnabled() bool {
|
||||||
|
return util.BoolP(conf.TLSEnabledP)
|
||||||
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ type WarewulfConf struct {
|
|||||||
Port int `yaml:"port,omitempty" default:"9873"`
|
Port int `yaml:"port,omitempty" default:"9873"`
|
||||||
TlsPort int `yaml:"tls port,omitempty" default:"9874"`
|
TlsPort int `yaml:"tls port,omitempty" default:"9874"`
|
||||||
SecureP *bool `yaml:"secure,omitempty" default:"true"`
|
SecureP *bool `yaml:"secure,omitempty" default:"true"`
|
||||||
EnableTLSP *bool `yaml:"tls,omitempty" default:"false"`
|
TLSEnabledP *bool `yaml:"tls,omitempty"`
|
||||||
UpdateInterval int `yaml:"update interval,omitempty" default:"60"`
|
UpdateInterval int `yaml:"update interval,omitempty" default:"60"`
|
||||||
AutobuildOverlaysP *bool `yaml:"autobuild overlays,omitempty" default:"true"`
|
AutobuildOverlaysP *bool `yaml:"autobuild overlays,omitempty" default:"true"`
|
||||||
EnableHostOverlayP *bool `yaml:"host overlay,omitempty" default:"true"`
|
EnableHostOverlayP *bool `yaml:"host overlay,omitempty" default:"true"`
|
||||||
@@ -56,8 +56,8 @@ func (conf WarewulfConf) Secure() bool {
|
|||||||
return util.BoolP(conf.SecureP)
|
return util.BoolP(conf.SecureP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conf WarewulfConf) EnableTLS() bool {
|
func (conf WarewulfConf) TLSEnabled() bool {
|
||||||
return util.BoolP(conf.EnableTLSP)
|
return util.BoolP(conf.TLSEnabledP)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (conf WarewulfConf) AutobuildOverlays() bool {
|
func (conf WarewulfConf) AutobuildOverlays() bool {
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ func TestParse(t *testing.T) {
|
|||||||
result: `
|
result: `
|
||||||
warewulf:
|
warewulf:
|
||||||
autobuild overlays: true
|
autobuild overlays: true
|
||||||
tls: false
|
|
||||||
grubboot: false
|
grubboot: false
|
||||||
host overlay: true
|
host overlay: true
|
||||||
port: 9873
|
port: 9873
|
||||||
@@ -65,7 +64,6 @@ network: 192.168.0.0
|
|||||||
netmask: 255.255.255.0
|
netmask: 255.255.255.0
|
||||||
warewulf:
|
warewulf:
|
||||||
autobuild overlays: true
|
autobuild overlays: true
|
||||||
tls: false
|
|
||||||
grubboot: false
|
grubboot: false
|
||||||
host overlay: true
|
host overlay: true
|
||||||
port: 9873
|
port: 9873
|
||||||
@@ -115,7 +113,6 @@ network: 192.168.0.0
|
|||||||
netmask: 255.255.0.0
|
netmask: 255.255.0.0
|
||||||
warewulf:
|
warewulf:
|
||||||
autobuild overlays: true
|
autobuild overlays: true
|
||||||
tls: false
|
|
||||||
grubboot: false
|
grubboot: false
|
||||||
host overlay: true
|
host overlay: true
|
||||||
port: 9873
|
port: 9873
|
||||||
@@ -162,7 +159,6 @@ ipaddr6: "2001:db8::1"
|
|||||||
prefixlen6: "64"
|
prefixlen6: "64"
|
||||||
warewulf:
|
warewulf:
|
||||||
autobuild overlays: true
|
autobuild overlays: true
|
||||||
tls: false
|
|
||||||
grubboot: false
|
grubboot: false
|
||||||
host overlay: true
|
host overlay: true
|
||||||
port: 9873
|
port: 9873
|
||||||
@@ -238,7 +234,6 @@ netmask: 255.255.255.0
|
|||||||
network: 192.168.200.0
|
network: 192.168.200.0
|
||||||
warewulf:
|
warewulf:
|
||||||
autobuild overlays: true
|
autobuild overlays: true
|
||||||
tls: false
|
|
||||||
grubboot: false
|
grubboot: false
|
||||||
host overlay: true
|
host overlay: true
|
||||||
port: 9873
|
port: 9873
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import (
|
|||||||
// Returns true if new keys were generated.
|
// Returns true if new keys were generated.
|
||||||
func TLS(force bool) (bool, error) {
|
func TLS(force bool) (bool, error) {
|
||||||
conf := warewulfconf.Get()
|
conf := warewulfconf.Get()
|
||||||
if !conf.Warewulf.EnableTLS() {
|
if !conf.Warewulf.TLSEnabled() {
|
||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ func buildTemplateVars(conf *warewulfconf.WarewulfYaml, rinfo parsedRequest, rem
|
|||||||
Ipaddr: conf.Ipaddr,
|
Ipaddr: conf.Ipaddr,
|
||||||
Ipaddr6: ipaddr6,
|
Ipaddr6: ipaddr6,
|
||||||
Port: strconv.Itoa(conf.Warewulf.Port),
|
Port: strconv.Itoa(conf.Warewulf.Port),
|
||||||
TLS: conf.Warewulf.EnableTLS(),
|
TLS: conf.Warewulf.TLSEnabled(),
|
||||||
Authority: authority,
|
Authority: authority,
|
||||||
Hostname: remoteNode.Id(),
|
Hostname: remoteNode.Id(),
|
||||||
Hwaddr: rinfo.hwaddr,
|
Hwaddr: rinfo.hwaddr,
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ func HandleSystemOverlay(w http.ResponseWriter, req *http.Request) {
|
|||||||
// If TLS is enabled, returns 403 Forbidden for plain-HTTP requests.
|
// If TLS is enabled, returns 403 Forbidden for plain-HTTP requests.
|
||||||
// If an explicit ?overlay= list is present, delegates to HandleOverlayList.
|
// If an explicit ?overlay= list is present, delegates to HandleOverlayList.
|
||||||
func HandleRuntimeOverlay(w http.ResponseWriter, req *http.Request) {
|
func HandleRuntimeOverlay(w http.ResponseWriter, req *http.Request) {
|
||||||
if config.Get().Warewulf.EnableTLS() && req.TLS == nil {
|
if config.Get().Warewulf.TLSEnabled() && req.TLS == nil {
|
||||||
wwlog.Denied("runtime overlay requested over insecure connection")
|
wwlog.Denied("runtime overlay requested over insecure connection")
|
||||||
w.WriteHeader(http.StatusForbidden)
|
w.WriteHeader(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -37,6 +37,17 @@ func (h *slashFix) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.mux.ServeHTTP(w, r)
|
h.mux.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func requireTLS(next http.Handler) http.Handler {
|
||||||
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
if r.TLS == nil {
|
||||||
|
wwlog.Denied("API request over insecure connection")
|
||||||
|
w.WriteHeader(http.StatusForbidden)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
next.ServeHTTP(w, r)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
func configureRootHandler(apiHandler http.Handler) *slashFix {
|
func configureRootHandler(apiHandler http.Handler) *slashFix {
|
||||||
var wwHandler http.ServeMux
|
var wwHandler http.ServeMux
|
||||||
wwHandler.HandleFunc("/provision/", warewulfd.HandleProvision)
|
wwHandler.HandleFunc("/provision/", warewulfd.HandleProvision)
|
||||||
@@ -89,13 +100,16 @@ func RunServer() error {
|
|||||||
var apiHandler http.Handler
|
var apiHandler http.Handler
|
||||||
if conf.API != nil && conf.API.Enabled() {
|
if conf.API != nil && conf.API.Enabled() {
|
||||||
apiHandler = api.Handler(auth, conf.API.AllowedIPNets())
|
apiHandler = api.Handler(auth, conf.API.AllowedIPNets())
|
||||||
|
if conf.API.TLSEnabled() {
|
||||||
|
apiHandler = requireTLS(apiHandler)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
httpHandler := configureRootHandler(apiHandler)
|
httpHandler := configureRootHandler(apiHandler)
|
||||||
|
|
||||||
errChan := make(chan error, 2)
|
errChan := make(chan error, 2)
|
||||||
|
|
||||||
if conf.Warewulf.EnableTLS() {
|
if conf.Warewulf.TLSEnabled() {
|
||||||
key := path.Join(conf.Paths.Sysconfdir, "warewulf", "tls", "warewulf.key")
|
key := path.Join(conf.Paths.Sysconfdir, "warewulf", "tls", "warewulf.key")
|
||||||
crt := path.Join(conf.Paths.Sysconfdir, "warewulf", "tls", "warewulf.crt")
|
crt := path.Join(conf.Paths.Sysconfdir, "warewulf", "tls", "warewulf.crt")
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ WWIPMI_WRITE="{{$.Ipmi.Write.Bool}}"
|
|||||||
{{- if $.Ipmi.Tags.vlan }}
|
{{- if $.Ipmi.Tags.vlan }}
|
||||||
WWIPMI_VLAN="{{$.Ipmi.Tags.vlan}}"
|
WWIPMI_VLAN="{{$.Ipmi.Tags.vlan}}"
|
||||||
{{- end }}
|
{{- end }}
|
||||||
WWTLS={{$.Warewulf.EnableTLS}}
|
WWTLS={{$.Warewulf.TLSEnabled}}
|
||||||
WWTLSPORT={{$.Warewulf.TlsPort}}
|
WWTLSPORT={{$.Warewulf.TlsPort}}
|
||||||
WWIPADDR={{$.Ipaddr}}
|
WWIPADDR={{$.Ipaddr}}
|
||||||
|
|||||||
Reference in New Issue
Block a user