Skip to content

Commit b4ff2fd

Browse files
committed
sec: steps towards docker iamge harderning
1 parent beb7a45 commit b4ff2fd

File tree

24 files changed

+99
-39
lines changed

24 files changed

+99
-39
lines changed

.github/workflows/release-packages.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,7 @@ jobs:
170170
name: wireport ${{ steps.vars.outputs.version }}
171171
draft: false
172172
prerelease: false
173+
generate_release_notes: true
173174
files: dist-aggregate/**
174175

175176
#-------------------------------------------------------------------

Dockerfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,15 @@ RUN go build -o wireport ./cmd/server/main.go
3939
# wireguard, tcpdump & other tools
4040
FROM alpine:3.21
4141

42-
RUN apk add --no-cache -U \
42+
# Update base image with security patches and install only the minimal runtime
43+
RUN apk --no-cache upgrade && apk add --no-cache \
4344
wireguard-tools \
4445
iptables \
4546
nano \
4647
bind-tools \
4748
tcpdump \
4849
runit \
49-
docker
50+
docker-cli
5051

5152
COPY --from=caddy-builder /usr/bin/caddy /usr/bin/caddy
5253
COPY --from=coredns-builder /app/coredns/coredns /usr/bin/coredns

app/cmd/server/commands/gateway.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package commands
22

33
import (
44
"fmt"
5+
"wireport/cmd/server/config"
56
"wireport/internal/routes"
67
"wireport/internal/ssh"
78
"wireport/internal/utils"
@@ -12,6 +13,7 @@ import (
1213

1314
var GatewayStartConfigureOnly = false
1415
var GatewaySSHKeyPassEmpty = false
16+
var GatewayDockerImage = config.Config.WireportGatewayContainerImage
1517
var GatewayDockerImageTag = version.Version
1618
var forceGatewayTeardown = false
1719

@@ -72,7 +74,7 @@ var UpGatewayCmd = &cobra.Command{
7274
return
7375
}
7476

75-
commandsService.GatewayUp(creds, GatewayDockerImageTag, cmd.OutOrStdout(), cmd.ErrOrStderr())
77+
commandsService.GatewayUp(creds, GatewayDockerImage, GatewayDockerImageTag, cmd.OutOrStdout(), cmd.ErrOrStderr())
7678
},
7779
}
7880

@@ -128,7 +130,7 @@ var UpgradeGatewayCmd = &cobra.Command{
128130
return
129131
}
130132

131-
commandsService.GatewayUpgrade(creds, GatewayDockerImageTag, cmd.OutOrStdout(), cmd.ErrOrStderr())
133+
commandsService.GatewayUpgrade(creds, GatewayDockerImage, GatewayDockerImageTag, cmd.OutOrStdout(), cmd.ErrOrStderr())
132134
},
133135
}
134136

@@ -146,6 +148,7 @@ func init() {
146148

147149
UpGatewayCmd.Flags().String("ssh-key-path", "", "Path to SSH private key file (for passwordless authentication)")
148150
UpGatewayCmd.Flags().BoolVar(&GatewaySSHKeyPassEmpty, "ssh-key-pass-empty", false, "Skip SSH key passphrase prompt (for passwordless SSH keys)")
151+
UpGatewayCmd.Flags().StringVar(&GatewayDockerImage, "image", config.Config.WireportGatewayContainerImage, "Docker image to use for the wireport gateway container")
149152
UpGatewayCmd.Flags().StringVar(&GatewayDockerImageTag, "image-tag", version.Version, "Image tag to use for the wireport gateway container")
150153

151154
DownGatewayCmd.Flags().String("ssh-key-path", "", "Path to SSH private key file (for passwordless authentication)")
@@ -154,5 +157,6 @@ func init() {
154157

155158
UpgradeGatewayCmd.Flags().String("ssh-key-path", "", "Path to SSH private key file (for passwordless authentication)")
156159
UpgradeGatewayCmd.Flags().BoolVar(&GatewaySSHKeyPassEmpty, "ssh-key-pass-empty", false, "Skip SSH key passphrase prompt (for passwordless SSH keys)")
160+
UpgradeGatewayCmd.Flags().StringVar(&GatewayDockerImage, "image", config.Config.WireportGatewayContainerImage, "Docker image to use for the wireport gateway container")
157161
UpgradeGatewayCmd.Flags().StringVar(&GatewayDockerImageTag, "image-tag", version.Version, "Image tag to use for the wireport gateway container")
158162
}

app/cmd/server/commands/server.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package commands
22

33
import (
44
"fmt"
5+
"wireport/cmd/server/config"
56
"wireport/internal/nodes/types"
67
"wireport/internal/ssh"
78
"wireport/version"
@@ -13,6 +14,7 @@ var forceServerCreation = false
1314
var quietServerCreation = false
1415
var dockerSubnet = ""
1516
var ServerSSHKeyPassEmpty = false
17+
var ServerDockerImage = config.Config.WireportServerContainerImage
1618
var ServerDockerImageTag = version.Version
1719
var forceServerTeardown = false
1820

@@ -81,7 +83,7 @@ var UpServerCmd = &cobra.Command{
8183
}
8284
}
8385

84-
commandsService.ServerUp(creds, ServerDockerImageTag, cmd.OutOrStdout(), cmd.ErrOrStderr(), dockerSubnet)
86+
commandsService.ServerUp(creds, ServerDockerImage, ServerDockerImageTag, cmd.OutOrStdout(), cmd.ErrOrStderr(), dockerSubnet)
8587
},
8688
}
8789

@@ -147,7 +149,7 @@ var UpgradeServerCmd = &cobra.Command{
147149
return
148150
}
149151

150-
commandsService.ServerUpgrade(creds, ServerDockerImageTag, cmd.OutOrStdout(), cmd.ErrOrStderr())
152+
commandsService.ServerUpgrade(creds, ServerDockerImage, ServerDockerImageTag, cmd.OutOrStdout(), cmd.ErrOrStderr())
151153
},
152154
}
153155

@@ -170,6 +172,7 @@ func init() {
170172
UpServerCmd.Flags().String("ssh-key-path", "", "Path to SSH private key file (for passwordless authentication)")
171173
UpServerCmd.Flags().BoolVar(&ServerSSHKeyPassEmpty, "ssh-key-pass-empty", false, "Skip SSH key passphrase prompt (for passwordless SSH keys)")
172174
UpServerCmd.Flags().StringVar(&dockerSubnet, "docker-subnet", "", "Specify a custom Docker subnet for the server (e.g. 172.20.0.0/16)")
175+
UpServerCmd.Flags().StringVar(&ServerDockerImage, "image", config.Config.WireportServerContainerImage, "Docker image to use for the wireport server container")
173176
UpServerCmd.Flags().StringVar(&ServerDockerImageTag, "image-tag", version.Version, "Image tag to use for the wireport server container")
174177

175178
DownServerCmd.Flags().String("ssh-key-path", "", "Path to SSH private key file (for passwordless authentication)")
@@ -178,5 +181,6 @@ func init() {
178181

179182
UpgradeServerCmd.Flags().String("ssh-key-path", "", "Path to SSH private key file (for passwordless authentication)")
180183
UpgradeServerCmd.Flags().BoolVar(&ServerSSHKeyPassEmpty, "ssh-key-pass-empty", false, "Skip SSH key passphrase prompt (for passwordless SSH keys)")
184+
UpgradeServerCmd.Flags().StringVar(&ServerDockerImage, "image", config.Config.WireportServerContainerImage, "Docker image to use for the wireport server container")
181185
UpgradeServerCmd.Flags().StringVar(&ServerDockerImageTag, "image-tag", version.Version, "Image tag to use for the wireport server container")
182186
}

app/internal/commands/local_commands_service.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -197,7 +197,7 @@ func (s *LocalCommandsService) GatewayStatus(creds *ssh.Credentials, stdOut io.W
197197
fmt.Fprintf(stdOut, "✨ Gateway Status check completed successfully!\n")
198198
}
199199

200-
func (s *LocalCommandsService) GatewayUp(creds *ssh.Credentials, imageTag string, stdOut io.Writer, errOut io.Writer) {
200+
func (s *LocalCommandsService) GatewayUp(creds *ssh.Credentials, image string, imageTag string, stdOut io.Writer, errOut io.Writer) {
201201
sshService := ssh.NewService()
202202

203203
fmt.Fprintf(errOut, "🚀 wireport Gateway Bootstrapping\n")
@@ -238,7 +238,7 @@ func (s *LocalCommandsService) GatewayUp(creds *ssh.Credentials, imageTag string
238238
fmt.Fprintf(errOut, "📦 Installing wireport gateway...\n")
239239
fmt.Fprintf(errOut, " Gateway: %s@%s:%d\n", creds.Username, creds.Host, creds.Port)
240240

241-
_, clientJoinToken, err := sshService.InstallWireportGateway(imageTag)
241+
_, clientJoinToken, err := sshService.InstallWireportGateway(image, imageTag)
242242

243243
if err != nil {
244244
fmt.Fprintf(errOut, " Status: ❌ Installation Failed\n")
@@ -352,7 +352,7 @@ func (s *LocalCommandsService) GatewayDown(creds *ssh.Credentials, stdOut io.Wri
352352
fmt.Fprintf(stdOut, "✨ Gateway Teardown completed successfully!\n")
353353
}
354354

355-
func (s *LocalCommandsService) GatewayUpgrade(creds *ssh.Credentials, imageTag string, stdOut io.Writer, _ io.Writer) {
355+
func (s *LocalCommandsService) GatewayUpgrade(creds *ssh.Credentials, image string, imageTag string, stdOut io.Writer, _ io.Writer) {
356356
sshService := ssh.NewService()
357357

358358
fmt.Fprintf(stdOut, "🔄 wireport Gateway Upgrade\n")
@@ -377,7 +377,7 @@ func (s *LocalCommandsService) GatewayUpgrade(creds *ssh.Credentials, imageTag s
377377
fmt.Fprintf(stdOut, "🔄 Upgrading wireport gateway...\n")
378378
fmt.Fprintf(stdOut, " Gateway: %s@%s:%d\n", creds.Username, creds.Host, creds.Port)
379379

380-
success, err := sshService.UpgradeWireportGateway(imageTag)
380+
success, err := sshService.UpgradeWireportGateway(image, imageTag)
381381

382382
if err != nil {
383383
fmt.Fprintf(stdOut, " Status: ❌ Failed\n")
@@ -682,7 +682,7 @@ func (s *LocalCommandsService) ServerStatus(creds *ssh.Credentials, stdOut io.Wr
682682
fmt.Fprintf(stdOut, "✨ Server Status check completed successfully!\n")
683683
}
684684

685-
func (s *LocalCommandsService) ServerUp(creds *ssh.Credentials, imageTag string, stdOut io.Writer, errOut io.Writer, dockerSubnet string, commandsService *Service) {
685+
func (s *LocalCommandsService) ServerUp(creds *ssh.Credentials, image string, imageTag string, stdOut io.Writer, errOut io.Writer, dockerSubnet string, commandsService *Service) {
686686
sshService := ssh.NewService()
687687

688688
fmt.Fprintf(stdOut, "🚀 wireport Server Bootstrapping\n")
@@ -721,7 +721,7 @@ func (s *LocalCommandsService) ServerUp(creds *ssh.Credentials, imageTag string,
721721
fmt.Fprintf(stdOut, "📦 Installing wireport server...\n")
722722
fmt.Fprintf(stdOut, " Server: %s@%s:%d\n", creds.Username, creds.Host, creds.Port)
723723

724-
_, err = sshService.InstallWireportServer(serverJoinToken, imageTag)
724+
_, err = sshService.InstallWireportServer(serverJoinToken, image, imageTag)
725725

726726
if err != nil {
727727
fmt.Fprintf(stdOut, " Status: ❌ Connection Failed\n")
@@ -857,7 +857,7 @@ func (s *LocalCommandsService) ServerList(requestFromNodeID *string, stdOut io.W
857857
}
858858
}
859859

860-
func (s *LocalCommandsService) ServerUpgrade(creds *ssh.Credentials, imageTag string, stdOut io.Writer, _ io.Writer) {
860+
func (s *LocalCommandsService) ServerUpgrade(creds *ssh.Credentials, image string, imageTag string, stdOut io.Writer, _ io.Writer) {
861861
sshService := ssh.NewService()
862862

863863
fmt.Fprintf(stdOut, "🔄 wireport Server Upgrade\n")
@@ -882,7 +882,7 @@ func (s *LocalCommandsService) ServerUpgrade(creds *ssh.Credentials, imageTag st
882882
fmt.Fprintf(stdOut, "🔄 Upgrading wireport server...\n")
883883
fmt.Fprintf(stdOut, " Server: %s@%s:%d\n", creds.Username, creds.Host, creds.Port)
884884

885-
success, err := sshService.UpgradeWireportServer(imageTag)
885+
success, err := sshService.UpgradeWireportServer(image, imageTag)
886886

887887
if err != nil {
888888
fmt.Fprintf(stdOut, " Status: ❌ Failed\n")

app/internal/commands/service.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -153,15 +153,15 @@ func (s *Service) GatewayStatus(creds *ssh.Credentials, stdOut io.Writer) {
153153
s.LocalCommandsService.GatewayStatus(creds, stdOut)
154154
}
155155

156-
func (s *Service) GatewayUp(creds *ssh.Credentials, imageTag string, stdOut io.Writer, errOut io.Writer) {
156+
func (s *Service) GatewayUp(creds *ssh.Credentials, image string, imageTag string, stdOut io.Writer, errOut io.Writer) {
157157
s.executeCommand(
158158
stdOut,
159159
errOut,
160160
[]RoleGroup{
161161
{
162162
Roles: []types.NodeRole{types.NodeRoleEmpty},
163163
Handler: func(_ *types.Node, _ *APICommandsService, local *LocalCommandsService) (*commandstypes.ExecResponseDTO, error) {
164-
local.GatewayUp(creds, imageTag, stdOut, errOut)
164+
local.GatewayUp(creds, image, imageTag, stdOut, errOut)
165165
return nil, nil
166166
},
167167
},
@@ -201,15 +201,15 @@ func (s *Service) GatewayDown(creds *ssh.Credentials, stdOut io.Writer, errOut i
201201
)
202202
}
203203

204-
func (s *Service) GatewayUpgrade(creds *ssh.Credentials, imageTag string, stdOut io.Writer, errOut io.Writer) {
204+
func (s *Service) GatewayUpgrade(creds *ssh.Credentials, image string, imageTag string, stdOut io.Writer, errOut io.Writer) {
205205
s.executeCommand(
206206
stdOut,
207207
errOut,
208208
[]RoleGroup{
209209
{
210210
Roles: []types.NodeRole{types.NodeRoleClient},
211211
Handler: func(_ *types.Node, _ *APICommandsService, local *LocalCommandsService) (*commandstypes.ExecResponseDTO, error) {
212-
local.GatewayUpgrade(creds, imageTag, stdOut, errOut)
212+
local.GatewayUpgrade(creds, image, imageTag, stdOut, errOut)
213213
return nil, nil
214214
},
215215
},
@@ -340,15 +340,15 @@ func (s *Service) ServerStatus(creds *ssh.Credentials, stdOut io.Writer) {
340340
s.LocalCommandsService.ServerStatus(creds, stdOut)
341341
}
342342

343-
func (s *Service) ServerUp(creds *ssh.Credentials, imageTag string, stdOut io.Writer, errOut io.Writer, dockerSubnet string) {
343+
func (s *Service) ServerUp(creds *ssh.Credentials, image string, imageTag string, stdOut io.Writer, errOut io.Writer, dockerSubnet string) {
344344
s.executeCommand(
345345
stdOut,
346346
errOut,
347347
[]RoleGroup{
348348
{
349349
Roles: []types.NodeRole{types.NodeRoleClient},
350350
Handler: func(_ *types.Node, _ *APICommandsService, local *LocalCommandsService) (*commandstypes.ExecResponseDTO, error) {
351-
local.ServerUp(creds, imageTag, stdOut, errOut, dockerSubnet, s)
351+
local.ServerUp(creds, image, imageTag, stdOut, errOut, dockerSubnet, s)
352352
return nil, nil
353353
},
354354
},
@@ -410,15 +410,15 @@ func (s *Service) ServerList(requestFromNodeID *string, stdOut io.Writer, errOut
410410
)
411411
}
412412

413-
func (s *Service) ServerUpgrade(creds *ssh.Credentials, imageTag string, stdOut io.Writer, errOut io.Writer) {
413+
func (s *Service) ServerUpgrade(creds *ssh.Credentials, image string, imageTag string, stdOut io.Writer, errOut io.Writer) {
414414
s.executeCommand(
415415
stdOut,
416416
errOut,
417417
[]RoleGroup{
418418
{
419419
Roles: []types.NodeRole{types.NodeRoleClient},
420420
Handler: func(_ *types.Node, _ *APICommandsService, local *LocalCommandsService) (*commandstypes.ExecResponseDTO, error) {
421-
local.ServerUpgrade(creds, imageTag, stdOut, errOut)
421+
local.ServerUpgrade(creds, image, imageTag, stdOut, errOut)
422422
return nil, nil
423423
},
424424
},

app/internal/ssh/service.go

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,7 @@ func (s *Service) GetWireportNetworkStatus() (string, error) {
207207
return result.Stdout, nil
208208
}
209209

210-
func (s *Service) InstallWireportGateway(imageTag string) (bool, *string, error) {
210+
func (s *Service) InstallWireportGateway(image string, imageTag string) (bool, *string, error) {
211211
isRunning, err := s.IsWireportGatewayContainerRunning()
212212

213213
if err != nil {
@@ -235,7 +235,7 @@ func (s *Service) InstallWireportGateway(imageTag string) (bool, *string, error)
235235

236236
installCmdStr, err := tpl.Exec(map[string]string{
237237
"wireportGatewayContainerName": config.Config.WireportGatewayContainerName,
238-
"wireportGatewayContainerImage": fmt.Sprintf("%s:%s", config.Config.WireportGatewayContainerImage, imageTag),
238+
"wireportGatewayContainerImage": fmt.Sprintf("%s:%s", image, imageTag),
239239
})
240240

241241
if err != nil {
@@ -381,7 +381,7 @@ func (s *Service) GetWireportServerContainerStatus() (string, error) {
381381
return result.Stdout, nil
382382
}
383383

384-
func (s *Service) InstallWireportServer(serverJoinToken string, imageTag string) (bool, error) {
384+
func (s *Service) InstallWireportServer(serverJoinToken string, image string, imageTag string) (bool, error) {
385385
isRunning, err := s.IsWireportServerContainerRunning()
386386

387387
if err != nil {
@@ -408,7 +408,7 @@ func (s *Service) InstallWireportServer(serverJoinToken string, imageTag string)
408408

409409
installCmdStr, err := tpl.Exec(map[string]string{
410410
"wireportServerContainerName": config.Config.WireportServerContainerName,
411-
"wireportServerContainerImage": fmt.Sprintf("%s:%s", config.Config.WireportServerContainerImage, imageTag),
411+
"wireportServerContainerImage": fmt.Sprintf("%s:%s", image, imageTag),
412412
"serverJoinToken": serverJoinToken,
413413
})
414414

@@ -464,7 +464,7 @@ func (s *Service) TeardownWireportServer() (bool, error) {
464464
return true, nil
465465
}
466466

467-
func (s *Service) UpgradeWireportGateway(imageTag string) (bool, error) {
467+
func (s *Service) UpgradeWireportGateway(image string, imageTag string) (bool, error) {
468468
upgradeCmdTemplate, err := templates.Scripts.ReadFile(config.Config.UpgradeGatewayScriptTemplatePath)
469469

470470
if err != nil {
@@ -479,7 +479,7 @@ func (s *Service) UpgradeWireportGateway(imageTag string) (bool, error) {
479479

480480
upgradeCmdStr, err := tpl.Exec(map[string]string{
481481
"wireportGatewayContainerName": config.Config.WireportGatewayContainerName,
482-
"wireportGatewayContainerImage": fmt.Sprintf("%s:%s", config.Config.WireportGatewayContainerImage, imageTag),
482+
"wireportGatewayContainerImage": fmt.Sprintf("%s:%s", image, imageTag),
483483
})
484484

485485
if err != nil {
@@ -499,7 +499,7 @@ func (s *Service) UpgradeWireportGateway(imageTag string) (bool, error) {
499499
return true, nil
500500
}
501501

502-
func (s *Service) UpgradeWireportServer(imageTag string) (bool, error) {
502+
func (s *Service) UpgradeWireportServer(image string, imageTag string) (bool, error) {
503503
upgradeCmdTemplate, err := templates.Scripts.ReadFile(config.Config.UpgradeServerScriptTemplatePath)
504504

505505
if err != nil {
@@ -514,7 +514,7 @@ func (s *Service) UpgradeWireportServer(imageTag string) (bool, error) {
514514

515515
upgradeCmdStr, err := tpl.Exec(map[string]string{
516516
"wireportServerContainerName": config.Config.WireportServerContainerName,
517-
"wireportServerContainerImage": fmt.Sprintf("%s:%s", config.Config.WireportServerContainerImage, imageTag),
517+
"wireportServerContainerImage": fmt.Sprintf("%s:%s", image, imageTag),
518518
})
519519

520520
if err != nil {

app/internal/templates/scripts/up/gateway.hbs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
11
docker pull {{ wireportGatewayContainerImage }} && \
2-
docker run -d --privileged \
3-
--restart=unless-stopped \
2+
docker run -d --cap-drop ALL \
3+
--cap-add NET_ADMIN --cap-add NET_RAW --cap-add NET_BIND_SERVICE \
4+
--device /dev/net/tun \
5+
--security-opt no-new-privileges:true \
46
--sysctl "net.ipv4.ip_forward=1" \
57
--sysctl "net.ipv4.conf.all.src_valid_mark=1" \
8+
--restart=unless-stopped \
69
-p 80:80/tcp -p 443:443/tcp \
710
-p 51820:51820/udp \
811
-p 4060:4060/tcp \

app/internal/templates/scripts/up/server.hbs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
docker pull {{ wireportServerContainerImage }} && \
2-
docker run -d --privileged \
2+
docker run -d \
3+
--cap-drop ALL \
4+
--cap-add NET_ADMIN \
5+
--cap-add NET_RAW \
6+
--cap-add NET_BIND_SERVICE \
7+
--security-opt no-new-privileges:true \
38
--restart=unless-stopped \
49
--sysctl "net.ipv4.ip_forward=1" \
510
--sysctl "net.ipv4.conf.all.src_valid_mark=1" \

app/internal/templates/scripts/upgrade/gateway.hbs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
docker pull {{ wireportGatewayContainerImage }} && \
22
docker stop {{ wireportGatewayContainerName }} && \
33
docker rm {{ wireportGatewayContainerName }} && \
4-
docker run -d --privileged --restart=unless-stopped --sysctl "net.ipv4.ip_forward=1" --sysctl "net.ipv4.conf.all.src_valid_mark=1" \
4+
docker run -d --cap-drop ALL \
5+
--cap-add NET_ADMIN --cap-add NET_RAW --cap-add NET_BIND_SERVICE \
6+
--device /dev/net/tun \
7+
--security-opt no-new-privileges:true \
8+
--sysctl "net.ipv4.ip_forward=1" \
9+
--sysctl "net.ipv4.conf.all.src_valid_mark=1" \
10+
--restart=unless-stopped \
511
-p 80:80/tcp -p 443:443/tcp \
612
-p 51820:51820/udp \
713
-p 4060:4060/tcp \

0 commit comments

Comments
 (0)