Skip to content

Commit b6cee48

Browse files
authored
Merge pull request #39 from DeterminateSystems/colemickens/seeeeerve-netboot-dir-and-tls
serve /netboot/mac; move away from DefaultServeMux; optionally terminate TLS
2 parents 0588730 + 35faf59 commit b6cee48

File tree

9 files changed

+223
-40
lines changed

9 files changed

+223
-40
lines changed

README.md

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,18 @@ Notes:
2121

2222
```sh
2323
go build .
24-
sudo ./dhcpv6macd -interface enp2s0 -base-address 2001:db8:0123:4567:: -http-boot-url-template 'http://netboot.target/?mac={{.MAC}}'
24+
sudo ./dhcpv6macd \
25+
-interface enp2s0 \
26+
-base-address 2001:db8:0123:4567:: \
27+
-http-boot-url-template 'http://netboot.target/?mac={{.MAC}}' \
28+
-tls-cert-file /tmp/netboot-server.crt.pem \
29+
-tls-key-file /tmp/netboot-server.key.pem \
30+
-netboot-dir /netboot/macs
2531
```
2632

33+
The `-tls-cert-file`, `-tls-key-file`, and `-netboot-dir` are optional and relate to
34+
serving a MAC-oriented directory structure over HTTP(s).
35+
2736
...where the template can use these parameters:
2837

2938
- `MAC` -- the MAC address of the netbooting device

flake.lock

Lines changed: 5 additions & 5 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

flake.nix

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@
9393
#vendorSha256 = pkgs.lib.fakeSha256;
9494

9595
goSum = ./go.sum;
96-
vendorHash = "sha256-wrD6Hllmq9eeKFBi4OpGsp4cjcRrRqlyaSnf6ZBNxU8=";
96+
vendorHash = "sha256-2CV+mHkHBGwECzMTIZWLYKFshzQHPqcy8K9zZ8QDQLk=";
9797
};
9898
});
9999

@@ -131,6 +131,26 @@
131131
Required when httpBootUrlTemplate uses HTTPS and the server certificate is not signed by a well-known CA.
132132
'';
133133
};
134+
netbootDirectory = lib.mkOption {
135+
type = lib.types.nullOr lib.types.path;
136+
description = lib.mdDoc ''
137+
`/netboot/mac`
138+
'';
139+
};
140+
tlsCertFile = lib.mkOption {
141+
type = lib.types.nullOr lib.types.path;
142+
default = null;
143+
description = lib.mdDoc ''
144+
Location of netboot TLS cert PEM.
145+
'';
146+
};
147+
tlsKeyFile = lib.mkOption {
148+
type = lib.types.nullOr lib.types.path;
149+
default = null;
150+
description = lib.mdDoc ''
151+
Location of netboot TLS key PEM.
152+
'';
153+
};
134154
};
135155
};
136156
config =
@@ -166,6 +186,12 @@
166186
cfg.baseAddress
167187
"-http-boot-url-template"
168188
cfg.httpBootUrlTemplate
189+
"-tls-cert-file"
190+
cfg.tlsCertFile
191+
"-tls-key-file"
192+
cfg.tlsKeyFile
193+
"-netboot-dir"
194+
cfg.netbootDirectory
169195
]);
170196
};
171197
};
@@ -186,6 +212,7 @@
186212
enable = true;
187213
interface = "eth1";
188214
baseAddress = "fd19:287e:c5a0:4931::";
215+
netbootDirectory = "/netboot/mac";
189216
};
190217

191218
networking.useNetworkd = true;

go.mod

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,27 @@
11
module github.com/DeterminateSystems/dhcpv6macd
22

3-
go 1.23.0
4-
5-
toolchain go1.23.8
3+
go 1.24.0
64

75
require (
86
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2
97
github.com/looplab/fsm v1.0.3
8+
github.com/matthewpi/certwatcher v1.2.0
109
github.com/mdlayher/netx v0.0.0-20230430222610-7e21880baee8
1110
github.com/pin/tftp/v3 v3.1.0
1211
)
1312

1413
require (
14+
github.com/fsnotify/fsnotify v1.9.0 // indirect
15+
github.com/go-logr/logr v1.4.3 // indirect
16+
github.com/go-logr/stdr v1.2.2 // indirect
1517
github.com/josharian/native v1.1.0 // indirect
1618
github.com/pierrec/lz4/v4 v4.1.19 // indirect
1719
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 // indirect
18-
golang.org/x/net v0.38.0 // indirect
19-
golang.org/x/sys v0.31.0 // indirect
20+
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
21+
go.opentelemetry.io/otel v1.38.0 // indirect
22+
go.opentelemetry.io/otel/metric v1.38.0 // indirect
23+
go.opentelemetry.io/otel/trace v1.38.0 // indirect
24+
golang.org/x/crypto v0.42.0 // indirect
25+
golang.org/x/net v0.43.0 // indirect
26+
golang.org/x/sys v0.36.0 // indirect
2027
)

go.sum

Lines changed: 33 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,56 @@
1-
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
2-
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3-
github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38=
4-
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
1+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
2+
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
4+
github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
5+
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
6+
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
7+
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
8+
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
9+
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
10+
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
11+
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
512
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2 h1:9K06NfxkBh25x56yVhWWlKFE8YpicaSfHwoV8SFbueA=
613
github.com/insomniacslk/dhcp v0.0.0-20231206064809-8c70d406f6d2/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=
714
github.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
815
github.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=
916
github.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=
1017
github.com/looplab/fsm v1.0.3 h1:qtxBsa2onOs0qFOtkqwf5zE0uP0+Te+wlIvXctPKpcw=
1118
github.com/looplab/fsm v1.0.3/go.mod h1:PmD3fFvQEIsjMEfvZdrCDZ6y8VwKTwWNjlpEr6IKPO4=
19+
github.com/matthewpi/certwatcher v1.2.0 h1:lA54/t5ymJD9ntSc959Z3FdBhR+ryfTdLoZvz/M45Dw=
20+
github.com/matthewpi/certwatcher v1.2.0/go.mod h1:nE4zNEJ9TXTcJS6dAmh/MTrLDrPMPg3nyEtd8J5H+aA=
1221
github.com/mdlayher/netx v0.0.0-20230430222610-7e21880baee8 h1:HMgSn3c16SXca3M+n6fLK2hXJLd4mhKAsZZh7lQfYmQ=
1322
github.com/mdlayher/netx v0.0.0-20230430222610-7e21880baee8/go.mod h1:qhZhwMDNWwZglKfwuWm0U9pCr/YKX1QAEwwJk9qfiTQ=
1423
github.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
1524
github.com/pierrec/lz4/v4 v4.1.19 h1:tYLzDnjDXh9qIxSTKHwXwOYmm9d887Y7Y1ZkyXYHAN4=
1625
github.com/pierrec/lz4/v4 v4.1.19/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=
1726
github.com/pin/tftp/v3 v3.1.0 h1:rQaxd4pGwcAJnpId8zC+O2NX3B2/NscjDZQaqEjuE7c=
1827
github.com/pin/tftp/v3 v3.1.0/go.mod h1:xwQaN4viYL019tM4i8iecm++5cGxSqen6AJEOEyEI0w=
19-
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
20-
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
28+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
29+
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
2130
github.com/stretchr/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
2231
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
23-
github.com/stretchr/testify v1.6.1 h1:hDPOHmpOpP40lSULcqw7IrRb/u7w6RpDC9399XyoNd0=
24-
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
32+
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
33+
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
2534
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63 h1:YcojQL98T/OO+rybuzn2+5KrD5dBwXIvYBvQ2cD3Avg=
2635
github.com/u-root/uio v0.0.0-20230305220412-3e8cd9d6bf63/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=
36+
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
37+
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
38+
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
39+
go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM=
40+
go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA=
41+
go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI=
42+
go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE=
43+
go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs=
2744
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
45+
golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI=
46+
golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8=
2847
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
29-
golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8=
30-
golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8=
48+
golang.org/x/net v0.43.0 h1:lat02VYK2j4aLzMzecihNvTlJNQUq316m2Mr9rnM6YE=
49+
golang.org/x/net v0.43.0/go.mod h1:vhO1fvI4dGsIjh73sWfUVjj3N7CA9WkKJNQm2svM6Jg=
3150
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
3251
golang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
33-
golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik=
34-
golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
52+
golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k=
53+
golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
3554
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
36-
gopkg.in/yaml.v3 v3.0.0 h1:hjy8E9ON/egN1tAYqKb61G10WtihqetD4sz2H+8nIeA=
37-
gopkg.in/yaml.v3 v3.0.0/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
55+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
56+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

http.go

Lines changed: 64 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,73 @@ import (
66
"log"
77
"net"
88
"net/http"
9+
"os"
10+
"path/filepath"
911
"time"
1012
)
1113

12-
func webserver(addr string, b *Broker, m *Machines) error {
14+
type neuteredFileSystem struct {
15+
fs http.FileSystem
16+
}
17+
18+
func (nfs neuteredFileSystem) Open(path string) (http.File, error) {
19+
f, err := nfs.fs.Open(path)
20+
if err != nil {
21+
return nil, err
22+
}
23+
24+
s, err := f.Stat()
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
if s.IsDir() {
30+
index := filepath.Join(path, "index.html")
31+
if _, err := nfs.fs.Open(index); err != nil {
32+
closeErr := f.Close()
33+
if closeErr != nil {
34+
return nil, closeErr
35+
}
36+
37+
return nil, err
38+
}
39+
}
40+
41+
return f, nil
42+
}
43+
44+
func webserver(netbootDir string, b *Broker, m *Machines) (*http.ServeMux, error) {
45+
server := http.NewServeMux()
46+
47+
if netbootDir == "" {
48+
log.Printf("netboot directory was not specified, will not serve it: %s", netbootDir)
49+
} else if _, err := os.Stat(netbootDir); os.IsNotExist(err) {
50+
log.Printf("netboot directory does not exist, will not serve it: %s", netbootDir)
51+
} else {
52+
fs := http.FileServer(neuteredFileSystem{http.Dir(netbootDir)})
53+
server.HandleFunc("/mac/{mac_addr}/boot.efi", func(w http.ResponseWriter, r *http.Request) {
54+
// Extract MAC address from path
55+
macStr := r.PathValue("mac_addr")
56+
mac, err := net.ParseMAC(macStr)
57+
if err != nil {
58+
log.Printf("Got request with something that didn't look like a MAC address. Returning 404.")
59+
http.NotFound(w, r)
60+
return
61+
} else {
62+
machine := m.GetOrInitMachine(mac)
63+
64+
// Trigger state transition to http_fetch_uki
65+
if err := machine.Event(r.Context(), "http_fetch_uki"); err != nil {
66+
log.Printf("Failed to transition to http_fetch_uki for %s: %v", macStr, err)
67+
}
68+
}
69+
70+
http.StripPrefix("/mac/", fs).ServeHTTP(w, r)
71+
})
72+
}
73+
1374
// SSE endpoint
14-
http.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) {
75+
server.HandleFunc("/events", func(w http.ResponseWriter, r *http.Request) {
1576
params := r.URL.Query()
1677
macStr := params.Get("mac")
1778

@@ -99,6 +160,5 @@ func webserver(addr string, b *Broker, m *Machines) error {
99160
}
100161
})
101162

102-
log.Printf("SSE server listening on %s", addr)
103-
return http.ListenAndServe(addr, nil)
163+
return server, nil
104164
}

machine.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,9 @@ func NewMachine(mac net.HardwareAddr, broker *Broker) *Machine {
119119
{Name: "served_ipxe_over_tftp", Src: []string{"point_pxe_to_ipxe_over_tftp"}, Dst: "served_ipxe_over_tftp"},
120120
{Name: "point_ipxe_to_http_boot", Src: []string{"served_ipxe_over_tftp"}, Dst: "point_ipxe_to_http_boot"},
121121

122-
{Name: "os_init", Src: []string{"http_boot", "point_ipxe_to_http_boot"}, Dst: "os_init"},
122+
{Name: "http_fetch_uki", Src: []string{"http_boot", "point_ipxe_to_http_boot"}, Dst: "http_fetch_uki"},
123+
124+
{Name: "os_init", Src: []string{"http_fetch_uki"}, Dst: "os_init"},
123125
},
124126
fsm.Callbacks{
125127
"enter_state": func(_ context.Context, e *fsm.Event) {

machine_test.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,21 @@ func TestTransitionsPxeBootMatchEvents(t *testing.T) {
130130
}
131131
}
132132

133+
{
134+
machine.Event(context.Background(), "http_fetch_uki")
135+
expectEvent(t, subscriber, "http_fetch_uki")
136+
137+
want := "[{init bogustime} {firmware_init bogustime} {point_pxe_to_ipxe_over_tftp bogustime} {served_ipxe_over_tftp bogustime} {point_ipxe_to_http_boot bogustime} {http_fetch_uki bogustime}]"
138+
if fmt.Sprint(machine.Events.Slice()) != want {
139+
t.Fatalf("Wanted %s, got %s", want, machine.Events.Slice())
140+
}
141+
}
142+
133143
{
134144
machine.Event(context.Background(), "os_init")
135145
expectEvent(t, subscriber, "os_init")
136146

137-
want := "[{init bogustime} {firmware_init bogustime} {point_pxe_to_ipxe_over_tftp bogustime} {served_ipxe_over_tftp bogustime} {point_ipxe_to_http_boot bogustime} {os_init bogustime}]"
147+
want := "[{init bogustime} {firmware_init bogustime} {point_pxe_to_ipxe_over_tftp bogustime} {served_ipxe_over_tftp bogustime} {point_ipxe_to_http_boot bogustime} {http_fetch_uki bogustime} {os_init bogustime}]"
138148
if fmt.Sprint(machine.Events.Slice()) != want {
139149
t.Fatalf("Wanted %s, got %s", want, machine.Events.Slice())
140150
}
@@ -144,7 +154,7 @@ func TestTransitionsPxeBootMatchEvents(t *testing.T) {
144154
machine.Event(context.Background(), "os_init")
145155
expectNoEvent(t, subscriber)
146156

147-
want := "[{init bogustime} {firmware_init bogustime} {point_pxe_to_ipxe_over_tftp bogustime} {served_ipxe_over_tftp bogustime} {point_ipxe_to_http_boot bogustime} {os_init bogustime}]"
157+
want := "[{init bogustime} {firmware_init bogustime} {point_pxe_to_ipxe_over_tftp bogustime} {served_ipxe_over_tftp bogustime} {point_ipxe_to_http_boot bogustime} {http_fetch_uki bogustime} {os_init bogustime}]"
148158
if fmt.Sprint(machine.Events.Slice()) != want {
149159
t.Fatalf("Wanted %s, got %s", want, machine.Events.Slice())
150160
}

0 commit comments

Comments
 (0)