Skip to content

Commit f68bf71

Browse files
committed
Disallow mounting to certain destination /dir paths
When certain directories, like /tmp, get mounted over, FCOS/Linux can act in unexpected ways. Added a sanity check for a list of directories think might be impacted by this. Also, moved the volume parsing earlier in the init process so we can catch problems before the expensive decompression of machine images. The following destinations are forbidden for volumes: `/bin`, `/boot`, `/dev`, `/etc`, `/home`, `/proc`, `/root`, `/run`, `/sbin`, `/sys`, `/tmp`, `/usr`, and `/var`. Subdirectories Fixes: #18230 Signed-off-by: Brent Baude <[email protected]>
1 parent faf8574 commit f68bf71

File tree

4 files changed

+118
-5
lines changed

4 files changed

+118
-5
lines changed

docs/source/markdown/podman-machine-init.1.md.in

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,12 @@ options are:
149149
* **rw**: mount volume read/write (default)
150150
* **security_model=[model]**: specify 9p security model (see below)
151151

152+
Note: The following destinations are forbidden for volumes: `/bin`, `/boot`, `/dev`, `/etc`,
153+
`/home`, `/proc`, `/root`, `/run`, `/sbin`, `/sys`, `/tmp`, `/usr`, and `/var`. Subdirectories
154+
of these destinations are allowed but users should be careful to not mount to important directories
155+
as unexpected results may occur.
156+
157+
152158
The 9p security model [determines] https://wiki.qemu.org/Documentation/9psetup#Starting_the_Guest_directly
153159
if and how the 9p filesystem translates some filesystem operations before
154160
actual storage on the host.

pkg/machine/e2e/init_test.go

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,20 @@ var _ = Describe("podman machine init", func() {
7777
badMemSession, err := mb.setCmd(badMem.withMemory(uint(total))).run()
7878
Expect(err).ToNot(HaveOccurred())
7979
Expect(badMemSession).To(Exit(125))
80+
81+
// Check that mounting to certain target directories like /tmp at the / level is NOT ok
82+
tmpVol := initMachine{}
83+
targetMount := "/tmp"
84+
tmpVolSession, err := mb.setCmd(tmpVol.withVolume(fmt.Sprintf("/whatever:%s", targetMount))).run()
85+
Expect(err).ToNot(HaveOccurred())
86+
Expect(tmpVolSession).To(Exit(125))
87+
Expect(tmpVolSession.errorToString()).To(ContainSubstring(fmt.Sprintf("Error: machine mount destination cannot be %q: consider another location or a subdirectory of an existing location", targetMount)))
88+
89+
// Mounting to /tmp/foo (subdirectory) is OK
90+
tmpSubdir := initMachine{}
91+
tmpSubDirSession, err := mb.setCmd(tmpSubdir.withVolume("/whatever:/tmp/foo")).run()
92+
Expect(err).ToNot(HaveOccurred())
93+
Expect(tmpSubDirSession).To(Exit(0))
8094
})
8195

8296
It("simple init", func() {

pkg/machine/shim/host.go

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import (
66
"fmt"
77
"io"
88
"os"
9+
"path"
910
"path/filepath"
1011
"runtime"
1112
"strings"
@@ -118,6 +119,18 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) error {
118119
createOpts.UserModeNetworking = *umn
119120
}
120121

122+
// Mounts
123+
if mp.VMType() != machineDefine.WSLVirt {
124+
mc.Mounts = CmdLineVolumesToMounts(opts.Volumes, mp.MountType())
125+
}
126+
127+
// Issue #18230 ... do not mount over important directories at the / level (subdirs are fine)
128+
for _, mnt := range mc.Mounts {
129+
if err := validateDestinationPaths(mnt.Target); err != nil {
130+
return err
131+
}
132+
}
133+
121134
// Get Image
122135
// TODO This needs rework bigtime; my preference is most of below of not living in here.
123136
// ideally we could get a func back that pulls the image, and only do so IF everything works because
@@ -251,11 +264,6 @@ func Init(opts machineDefine.InitOptions, mp vmconfigs.VMProvider) error {
251264
}
252265
ignBuilder.WithUnit(readyUnit)
253266

254-
// Mounts
255-
if mp.VMType() != machineDefine.WSLVirt {
256-
mc.Mounts = CmdLineVolumesToMounts(opts.Volumes, mp.MountType())
257-
}
258-
259267
// TODO AddSSHConnectionToPodmanSocket could take an machineconfig instead
260268
if err := connection.AddSSHConnectionsToPodmanSocket(mc.HostUser.UID, mc.SSH.Port, mc.SSH.IdentityPath, mc.Name, mc.SSH.RemoteUsername, opts); err != nil {
261269
return err
@@ -774,3 +782,27 @@ func Reset(mps []vmconfigs.VMProvider, opts machine.ResetOptions) error {
774782
}
775783
return resetErrors.ErrorOrNil()
776784
}
785+
786+
func validateDestinationPaths(dest string) error {
787+
// illegalMounts are locations at the / level of the podman machine where we do want users mounting directly over
788+
illegalMounts := map[string]struct{}{
789+
"/bin": {},
790+
"/boot": {},
791+
"/dev": {},
792+
"/etc": {},
793+
"/home": {},
794+
"/proc": {},
795+
"/root": {},
796+
"/run": {},
797+
"/sbin": {},
798+
"/sys": {},
799+
"/tmp": {},
800+
"/usr": {},
801+
"/var": {},
802+
}
803+
mountTarget := path.Clean(dest)
804+
if _, ok := illegalMounts[mountTarget]; ok {
805+
return fmt.Errorf("machine mount destination cannot be %q: consider another location or a subdirectory of an existing location", mountTarget)
806+
}
807+
return nil
808+
}

pkg/machine/shim/host_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
package shim
2+
3+
import (
4+
"testing"
5+
6+
"github.com/stretchr/testify/assert"
7+
)
8+
9+
func Test_validateDestinationPaths(t *testing.T) {
10+
tests := []struct {
11+
name string
12+
dest string
13+
wantErr bool
14+
}{
15+
{
16+
name: "Expect fail - /tmp",
17+
dest: "/tmp",
18+
wantErr: true,
19+
},
20+
{
21+
name: "Expect fail trailing /",
22+
dest: "/tmp/",
23+
wantErr: true,
24+
},
25+
{
26+
name: "Expect fail double /",
27+
dest: "//tmp",
28+
wantErr: true,
29+
},
30+
{
31+
name: "/var should fail",
32+
dest: "/var",
33+
wantErr: true,
34+
},
35+
{
36+
name: "/etc should fail",
37+
dest: "/etc",
38+
wantErr: true,
39+
},
40+
{
41+
name: "/tmp subdir OK",
42+
dest: "/tmp/foobar",
43+
wantErr: false,
44+
},
45+
{
46+
name: "/foobar OK",
47+
dest: "/foobar",
48+
wantErr: false,
49+
},
50+
}
51+
for _, tt := range tests {
52+
t.Run(tt.name, func(t *testing.T) {
53+
err := validateDestinationPaths(tt.dest)
54+
if tt.wantErr {
55+
assert.ErrorContainsf(t, err, "onsider another location or a subdirectory of an existing location", "illegal mount target")
56+
} else {
57+
assert.NoError(t, err, "mounts to subdirs or non-critical dirs should succeed")
58+
}
59+
})
60+
}
61+
}

0 commit comments

Comments
 (0)