In TP5, Hyper-V containers need all image files ACLed so that the virtual
machine process can access them. This was fixed post-TP5 in Windows, but
for TP5 we need to explicitly add these ACLs.
Signed-off-by: John Starks <jostarks@microsoft.com>
| ... | ... |
@@ -15,6 +15,7 @@ import ( |
| 15 | 15 |
"strings" |
| 16 | 16 |
"syscall" |
| 17 | 17 |
"time" |
| 18 |
+ "unsafe" |
|
| 18 | 19 |
|
| 19 | 20 |
"github.com/Microsoft/go-winio" |
| 20 | 21 |
"github.com/Microsoft/go-winio/archive/tar" |
| ... | ... |
@@ -27,6 +28,7 @@ import ( |
| 27 | 27 |
"github.com/docker/docker/pkg/idtools" |
| 28 | 28 |
"github.com/docker/docker/pkg/ioutils" |
| 29 | 29 |
"github.com/docker/docker/pkg/longpath" |
| 30 |
+ "github.com/docker/docker/pkg/system" |
|
| 30 | 31 |
"github.com/vbatts/tar-split/tar/storage" |
| 31 | 32 |
) |
| 32 | 33 |
|
| ... | ... |
@@ -51,6 +53,10 @@ type Driver struct {
|
| 51 | 51 |
|
| 52 | 52 |
var _ graphdriver.DiffGetterDriver = &Driver{}
|
| 53 | 53 |
|
| 54 |
+func isTP5OrOlder() bool {
|
|
| 55 |
+ return system.GetOSVersion().Build <= 14300 |
|
| 56 |
+} |
|
| 57 |
+ |
|
| 54 | 58 |
// InitFilter returns a new Windows storage filter driver. |
| 55 | 59 |
func InitFilter(home string, options []string, uidMaps, gidMaps []idtools.IDMap) (graphdriver.Driver, error) {
|
| 56 | 60 |
logrus.Debugf("WindowsGraphDriver InitFilter at %s", home)
|
| ... | ... |
@@ -158,6 +164,30 @@ func (d *Driver) create(id, parent, mountLabel string, readOnly bool, storageOpt |
| 158 | 158 |
if len(layerChain) != 0 {
|
| 159 | 159 |
parentPath = layerChain[0] |
| 160 | 160 |
} |
| 161 |
+ |
|
| 162 |
+ if isTP5OrOlder() {
|
|
| 163 |
+ // Pre-create the layer directory, providing an ACL to give the Hyper-V Virtual Machines |
|
| 164 |
+ // group access. This is necessary to ensure that Hyper-V containers can access the |
|
| 165 |
+ // virtual machine data. This is not necessary post-TP5. |
|
| 166 |
+ path, err := syscall.UTF16FromString(filepath.Join(d.info.HomeDir, id)) |
|
| 167 |
+ if err != nil {
|
|
| 168 |
+ return err |
|
| 169 |
+ } |
|
| 170 |
+ // Give system and administrators full control, and VMs read, write, and execute. |
|
| 171 |
+ // Mark these ACEs as inherited. |
|
| 172 |
+ sd, err := winio.SddlToSecurityDescriptor("D:(A;OICI;FA;;;SY)(A;OICI;FA;;;BA)(A;OICI;FRFWFX;;;S-1-5-83-0)")
|
|
| 173 |
+ if err != nil {
|
|
| 174 |
+ return err |
|
| 175 |
+ } |
|
| 176 |
+ err = syscall.CreateDirectory(&path[0], &syscall.SecurityAttributes{
|
|
| 177 |
+ Length: uint32(unsafe.Sizeof(syscall.SecurityAttributes{})),
|
|
| 178 |
+ SecurityDescriptor: uintptr(unsafe.Pointer(&sd[0])), |
|
| 179 |
+ }) |
|
| 180 |
+ if err != nil {
|
|
| 181 |
+ return err |
|
| 182 |
+ } |
|
| 183 |
+ } |
|
| 184 |
+ |
|
| 161 | 185 |
if err := hcsshim.CreateSandboxLayer(d.info, id, parentPath, layerChain); err != nil {
|
| 162 | 186 |
return err |
| 163 | 187 |
} |
| ... | ... |
@@ -578,6 +608,24 @@ func writeLayerFromTar(r archive.Reader, w hcsshim.LayerWriter) (int64, error) {
|
| 578 | 578 |
return 0, err |
| 579 | 579 |
} |
| 580 | 580 |
buf.Reset(w) |
| 581 |
+ |
|
| 582 |
+ // Add the Hyper-V Virutal Machine group ACE to the security descriptor |
|
| 583 |
+ // for TP5 so that Xenons can access all files. This is not necessary |
|
| 584 |
+ // for post-TP5 builds. |
|
| 585 |
+ if isTP5OrOlder() {
|
|
| 586 |
+ if sddl, ok := hdr.Winheaders["sd"]; ok {
|
|
| 587 |
+ var ace string |
|
| 588 |
+ if hdr.Typeflag == tar.TypeDir {
|
|
| 589 |
+ ace = "(A;OICI;0x1200a9;;;S-1-5-83-0)" |
|
| 590 |
+ } else {
|
|
| 591 |
+ ace = "(A;;0x1200a9;;;S-1-5-83-0)" |
|
| 592 |
+ } |
|
| 593 |
+ if hdr.Winheaders["sd"], ok = addAceToSddlDacl(sddl, ace); !ok {
|
|
| 594 |
+ logrus.Debugf("failed to add VM ACE to %s", sddl)
|
|
| 595 |
+ } |
|
| 596 |
+ } |
|
| 597 |
+ } |
|
| 598 |
+ |
|
| 581 | 599 |
hdr, err = backuptar.WriteBackupStreamFromTarFile(buf, t, hdr) |
| 582 | 600 |
ferr := buf.Flush() |
| 583 | 601 |
if ferr != nil {
|
| ... | ... |
@@ -592,6 +640,46 @@ func writeLayerFromTar(r archive.Reader, w hcsshim.LayerWriter) (int64, error) {
|
| 592 | 592 |
return totalSize, nil |
| 593 | 593 |
} |
| 594 | 594 |
|
| 595 |
+func addAceToSddlDacl(sddl, ace string) (string, bool) {
|
|
| 596 |
+ daclStart := strings.Index(sddl, "D:") |
|
| 597 |
+ if daclStart < 0 {
|
|
| 598 |
+ return sddl, false |
|
| 599 |
+ } |
|
| 600 |
+ |
|
| 601 |
+ dacl := sddl[daclStart:] |
|
| 602 |
+ daclEnd := strings.Index(dacl, "S:") |
|
| 603 |
+ if daclEnd < 0 {
|
|
| 604 |
+ daclEnd = len(dacl) |
|
| 605 |
+ } |
|
| 606 |
+ dacl = dacl[:daclEnd] |
|
| 607 |
+ |
|
| 608 |
+ if strings.Contains(dacl, ace) {
|
|
| 609 |
+ return sddl, true |
|
| 610 |
+ } |
|
| 611 |
+ |
|
| 612 |
+ i := 2 |
|
| 613 |
+ for i+1 < len(dacl) {
|
|
| 614 |
+ if dacl[i] != '(' {
|
|
| 615 |
+ return sddl, false |
|
| 616 |
+ } |
|
| 617 |
+ |
|
| 618 |
+ if dacl[i+1] == 'A' {
|
|
| 619 |
+ break |
|
| 620 |
+ } |
|
| 621 |
+ |
|
| 622 |
+ i += 2 |
|
| 623 |
+ for p := 1; i < len(dacl) && p > 0; i++ {
|
|
| 624 |
+ if dacl[i] == '(' {
|
|
| 625 |
+ p++ |
|
| 626 |
+ } else if dacl[i] == ')' {
|
|
| 627 |
+ p-- |
|
| 628 |
+ } |
|
| 629 |
+ } |
|
| 630 |
+ } |
|
| 631 |
+ |
|
| 632 |
+ return sddl[:daclStart+i] + ace + sddl[daclStart+i:], true |
|
| 633 |
+} |
|
| 634 |
+ |
|
| 595 | 635 |
// importLayer adds a new layer to the tag and graph store based on the given data. |
| 596 | 636 |
func (d *Driver) importLayer(id string, layerData archive.Reader, parentLayerPaths []string) (size int64, err error) {
|
| 597 | 637 |
var w hcsshim.LayerWriter |
| 598 | 638 |
new file mode 100644 |
| ... | ... |
@@ -0,0 +1,18 @@ |
| 0 |
+package windows |
|
| 1 |
+ |
|
| 2 |
+import "testing" |
|
| 3 |
+ |
|
| 4 |
+func TestAddAceToSddlDacl(t *testing.T) {
|
|
| 5 |
+ cases := [][3]string{
|
|
| 6 |
+ {"D:", "(A;;;)", "D:(A;;;)"},
|
|
| 7 |
+ {"D:(A;;;)", "(A;;;)", "D:(A;;;)"},
|
|
| 8 |
+ {"O:D:(A;;;stuff)", "(A;;;new)", "O:D:(A;;;new)(A;;;stuff)"},
|
|
| 9 |
+ {"O:D:(D;;;no)(A;;;stuff)", "(A;;;new)", "O:D:(D;;;no)(A;;;new)(A;;;stuff)"},
|
|
| 10 |
+ } |
|
| 11 |
+ |
|
| 12 |
+ for _, c := range cases {
|
|
| 13 |
+ if newSddl, worked := addAceToSddlDacl(c[0], c[1]); !worked || newSddl != c[2] {
|
|
| 14 |
+ t.Errorf("%s + %s == %s, expected %s (%v)", c[0], c[1], newSddl, c[2], worked)
|
|
| 15 |
+ } |
|
| 16 |
+ } |
|
| 17 |
+} |