Skip to content

Commit 8bbddc3

Browse files
authored
initial acl support (#555)
Signed-off-by: Petr Fedchenkov <[email protected]>
1 parent 3313156 commit 8bbddc3

File tree

10 files changed

+221
-51
lines changed

10 files changed

+221
-51
lines changed

cmd/edenPod.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ var (
3636
diskSize string
3737
imageFormat string
3838
volumeType string
39+
acl []string
3940

4041
deleteVolumes bool
4142

@@ -103,7 +104,11 @@ var podDeployCmd = &cobra.Command{
103104
opts = append(opts, expect.WithVolumeType(expect.VolumeTypeByName(volumeType)))
104105
opts = append(opts, expect.WithResources(appCpus, uint32(appMemoryParsed/1000)))
105106
opts = append(opts, expect.WithImageFormat(imageFormat))
106-
opts = append(opts, expect.WithACL(aclOnlyHost))
107+
if aclOnlyHost {
108+
opts = append(opts, expect.WithACL([]string{""}))
109+
} else {
110+
opts = append(opts, expect.WithACL(acl))
111+
}
107112
opts = append(opts, expect.WithSFTPLoad(sftpLoad))
108113
if !sftpLoad {
109114
opts = append(opts, expect.WithHTTPDirectLoad(directLoad))
@@ -444,6 +449,7 @@ func podInit() {
444449
podDeployCmd.Flags().BoolVar(&directLoad, "direct", true, "Use direct download for image instead of eserver")
445450
podDeployCmd.Flags().BoolVar(&sftpLoad, "sftp", false, "Force use of sftp to load http/file image from eserver")
446451
podDeployCmd.Flags().StringSliceVar(&disks, "disks", nil, "Additional disks to use")
452+
podDeployCmd.Flags().StringSliceVar(&acl, "acl", nil, "Allow access only to defined hosts/ips/subnets")
447453
podCmd.AddCommand(podPsCmd)
448454
podCmd.AddCommand(podStopCmd)
449455
podCmd.AddCommand(podStartCmd)

cmd/podModify.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import (
1313

1414
//podModifyCmd is a command to modify app
1515
var podModifyCmd = &cobra.Command{
16-
Use: "modify",
16+
Use: "modify <app>",
1717
Short: "Modify pod",
1818
Args: cobra.ExactArgs(1),
1919
PreRunE: func(cmd *cobra.Command, args []string) error {
@@ -50,6 +50,7 @@ var podModifyCmd = &cobra.Command{
5050
} else {
5151
opts = append(opts, expect.WithPortsPublish(portPublish))
5252
}
53+
opts = append(opts, expect.WithACL(acl))
5354
opts = append(opts, expect.WithOldApp(appName))
5455
expectation := expect.AppExpectationFromURL(ctrl, dev, defaults.DefaultDummyExpect, appName, opts...)
5556
appInstanceConfig := expectation.Application()
@@ -88,4 +89,5 @@ func podModifyInit() {
8889
podModifyCmd.Flags().StringSliceVarP(&portPublish, "publish", "p", nil, "Ports to publish in format EXTERNAL_PORT:INTERNAL_PORT")
8990
podModifyCmd.Flags().BoolVar(&aclOnlyHost, "only-host", false, "Allow access only to host and external networks")
9091
podModifyCmd.Flags().StringSliceVar(&podNetworks, "networks", nil, "Networks to connect to app (ports will be mapped to first network)")
92+
podModifyCmd.Flags().StringSliceVar(&acl, "acl", nil, "Allow access only to defined hosts/ips/subnets")
9193
}

docs/applications.md

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# Applications
2+
3+
## Deployment
4+
5+
In order to deploy application you can use `eden pod deploy` command:
6+
7+
```console
8+
Deploy app in pod.
9+
10+
Usage:
11+
eden pod deploy (docker|http(s)|file)://(<TAG>[:<VERSION>] | <URL for qcow2 image> | <path to qcow2 image>) [flags]
12+
13+
Flags:
14+
--acl strings Allow access only to defined hosts/ips/subnets
15+
--adapters strings adapters to assign to the application instance
16+
--cpus uint32 cpu number for app (default 1)
17+
--direct Use direct download for image instead of eserver (default true)
18+
--disk-size string disk size (empty or 0 - same as in image) (default "0 B")
19+
--disks strings Additional disks to use
20+
--format string format for image, one of 'container','qcow2','raw','qcow','vmdk','vhdx'; if not provided, defaults to container image for docker and oci transports, qcow2 for file and http/s transports
21+
-h, --help help for deploy
22+
--memory string memory for app (default "1.0 GB")
23+
--metadata string metadata for pod
24+
-n, --name string name for pod
25+
--networks strings Networks to connect to app (ports will be mapped to first network)
26+
--no-hyper Run pod without hypervisor
27+
--only-host Allow access only to host and external networks
28+
-p, --publish strings Ports to publish in format EXTERNAL_PORT:INTERNAL_PORT
29+
--registry string Select registry to use for containers (remote/local) (default "remote")
30+
--sftp Force use of sftp to load http/file image from eserver
31+
--vnc-display uint32 display number for VNC pod (0 - no VNC)
32+
--vnc-password string VNC password (empty - no password)
33+
--volume-type string volume type for empty volumes (qcow2, raw, qcow, vmdk, vhdx or oci); set it to none to not use volumes (default "qcow2")
34+
35+
Global Flags:
36+
--config string Name of config (default "default")
37+
-v, --verbosity string Log level (debug, info, warn, error, fatal, panic (default "info")
38+
```
39+
40+
## Modification
41+
42+
In order to modify existing application you can use `eden pod modify` command:
43+
44+
```console
45+
Modify pod
46+
47+
Usage:
48+
eden pod modify <app> [flags]
49+
50+
Flags:
51+
--acl strings Allow access only to defined hosts/ips/subnets
52+
-h, --help help for modify
53+
--networks strings Networks to connect to app (ports will be mapped to first network)
54+
--only-host Allow access only to host and external networks
55+
-p, --publish strings Ports to publish in format EXTERNAL_PORT:INTERNAL_PORT
56+
57+
Global Flags:
58+
--config string Name of config (default "default")
59+
-v, --verbosity string Log level (debug, info, warn, error, fatal, panic (default "info")
60+
```

pkg/defaults/defaults.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ const (
4848
DefaultRegistryPort = 5000
4949

5050
//tags, versions, repos
51-
DefaultEVETag = "6.1.0" //DefaultEVETag tag for EVE image
51+
DefaultEVETag = "0.0.0-master-efa32c66" //DefaultEVETag tag for EVE image
5252
DefaultAdamTag = "0.0.12"
5353
DefaultRedisTag = "6"
5454
DefaultRegistryTag = "2.7"

pkg/expect/application.go

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package expect
22

33
import (
44
"fmt"
5-
"strconv"
65

76
"github.com/lf-edge/eden/pkg/defaults"
87
"github.com/lf-edge/eve/api/go/config"
@@ -69,47 +68,10 @@ func (exp *AppExpectation) createAppInstanceConfig(img *config.Image, netInstanc
6968
bundle.appInstanceConfig.Interfaces = []*config.NetworkAdapter{}
7069

7170
for k, ni := range netInstances {
72-
var acls []*config.ACE
73-
if exp.onlyHostACL {
74-
acls = append(acls, &config.ACE{
75-
Matches: []*config.ACEMatch{{
76-
Type: "host",
77-
}},
78-
Id: 1,
79-
})
80-
} else {
81-
acls = append(acls, &config.ACE{
82-
Matches: []*config.ACEMatch{{
83-
Type: "ip",
84-
Value: "0.0.0.0/0",
85-
}},
86-
Id: 1,
87-
})
88-
}
89-
var aclID int32 = 2
90-
if k.ports != nil {
91-
for po, pi := range k.ports {
92-
acls = append(acls, &config.ACE{
93-
Id: aclID,
94-
Matches: []*config.ACEMatch{{
95-
Type: "protocol",
96-
Value: "tcp",
97-
}, {
98-
Type: "lport",
99-
Value: strconv.Itoa(po),
100-
}},
101-
Actions: []*config.ACEAction{{
102-
Portmap: true,
103-
AppPort: uint32(pi),
104-
}},
105-
Dir: config.ACEDirection_BOTH})
106-
aclID++
107-
}
108-
}
10971
bundle.appInstanceConfig.Interfaces = append(bundle.appInstanceConfig.Interfaces, &config.NetworkAdapter{
11072
Name: "default",
11173
NetworkId: ni.Uuidandversion.Uuid,
112-
Acls: acls,
74+
Acls: exp.getAcls(k),
11375
})
11476
}
11577
if exp.vncDisplay != 0 {

pkg/expect/expectation.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,6 @@ type AppExpectation struct {
5555

5656
volumesType VolumeType
5757

58-
onlyHostACL bool
59-
6058
registry string
6159

6260
oldAppName string
@@ -65,6 +63,7 @@ type AppExpectation struct {
6563
sftpLoad bool
6664

6765
disks []string
66+
acl []string
6867
}
6968

7069
//AppExpectationFromURL init AppExpectation with defined:
@@ -96,7 +95,6 @@ func AppExpectationFromURL(ctrl controller.Cloud, device *device.Ctx, appLink st
9695
uplinkAdapter: adapter,
9796
device: device,
9897
volumesType: VolumeQcow2,
99-
onlyHostACL: false,
10098
}
10199
switch expectation.ctrl.GetVars().ZArch {
102100
case "amd64":

pkg/expect/networkInstance.go

Lines changed: 75 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
package expect
22

33
import (
4+
"math/rand"
5+
"net"
6+
"strconv"
7+
"time"
8+
49
"github.com/docker/docker/pkg/namesgenerator"
510
"github.com/lf-edge/eden/pkg/models"
611
"github.com/lf-edge/eden/pkg/utils"
712
"github.com/lf-edge/eve/api/go/config"
813
uuid "github.com/satori/go.uuid"
914
log "github.com/sirupsen/logrus"
10-
"math/rand"
11-
"time"
1215
)
1316

1417
//NetInstanceExpectation stores options for create NetworkInstanceConfigs for apps
@@ -111,3 +114,73 @@ func (exp *AppExpectation) NetworkInstances() (networkInstances map[*NetInstance
111114
}
112115
return
113116
}
117+
118+
// parseACE returns ACE from string notation
119+
func parseACE(inp string) *config.ACE {
120+
//set default to host
121+
aclType := "host"
122+
if _, _, err := net.ParseCIDR(inp); err == nil {
123+
//check if it is subnet
124+
aclType = "ip"
125+
} else {
126+
if ip := net.ParseIP(inp); ip != nil {
127+
//check if it is ip
128+
aclType = "ip"
129+
}
130+
}
131+
return &config.ACE{
132+
Matches: []*config.ACEMatch{{
133+
Type: aclType,
134+
Value: inp,
135+
}},
136+
Dir: config.ACEDirection_BOTH,
137+
}
138+
}
139+
140+
// getAcls returns rules for access/deny/forwarding traffic
141+
func (exp *AppExpectation) getAcls(ni *NetInstanceExpectation) []*config.ACE {
142+
var acls []*config.ACE
143+
var aclID int32 = 1
144+
if exp.acl != nil {
145+
// in case of defined acl allow access only to them
146+
for _, el := range exp.acl {
147+
acl := parseACE(el)
148+
if acl != nil {
149+
acl.Id = aclID
150+
acls = append(acls, acl)
151+
aclID++
152+
}
153+
}
154+
} else {
155+
// allow access to all addresses
156+
acls = append(acls, &config.ACE{
157+
Matches: []*config.ACEMatch{{
158+
Type: "ip",
159+
Value: "0.0.0.0/0",
160+
}},
161+
Id: aclID,
162+
})
163+
aclID++
164+
}
165+
if ni.ports != nil {
166+
// forward defined ports
167+
for po, pi := range ni.ports {
168+
acls = append(acls, &config.ACE{
169+
Id: aclID,
170+
Matches: []*config.ACEMatch{{
171+
Type: "protocol",
172+
Value: "tcp",
173+
}, {
174+
Type: "lport",
175+
Value: strconv.Itoa(po),
176+
}},
177+
Actions: []*config.ACEAction{{
178+
Portmap: true,
179+
AppPort: uint32(pi),
180+
}},
181+
Dir: config.ACEDirection_BOTH})
182+
aclID++
183+
}
184+
}
185+
return acls
186+
}

pkg/expect/options.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,10 +157,10 @@ func WithVolumeType(volumesType VolumeType) ExpectationOption {
157157
}
158158
}
159159

160-
//WithACL sets access for app only to external networks if onlyHost sets
161-
func WithACL(onlyHost bool) ExpectationOption {
160+
//WithACL sets access only for defined hosts
161+
func WithACL(acl []string) ExpectationOption {
162162
return func(expectation *AppExpectation) {
163-
expectation.onlyHostACL = onlyHost
163+
expectation.acl = acl
164164
}
165165
}
166166

tests/eclient/testdata/acl.txt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
# Test particular host access
2+
3+
{{define "ssh"}}ssh -o ConnectTimeout=10 -o StrictHostKeyChecking=no -o PasswordAuthentication=no -i {{EdenConfig "eden.tests"}}/eclient/image/cert/id_rsa root@{{end}}
4+
5+
[!exec:bash] stop
6+
[!exec:sleep] stop
7+
[!exec:ssh] stop
8+
[!exec:chmod] stop
9+
10+
exec chmod 600 {{EdenConfig "eden.tests"}}/eclient/image/cert/id_rsa
11+
12+
# Starting of reboot detector with a 1 reboot limit
13+
! test eden.reboot.test -test.v -timewait 40m -reboot=0 -count=1 &
14+
15+
# Define access only to github.com
16+
eden pod deploy -n curl-acl --memory=512MB docker://itmoeve/eclient:0.4 -p 2223:22 --acl=github.com
17+
18+
test eden.app.test -test.v -timewait 10m RUNNING curl-acl
19+
20+
exec -t 10m bash wait_ssh.sh 2223
21+
22+
exec sleep 10
23+
24+
# Try to curl host we defined
25+
exec -t 1m bash curl.sh 2223 github.com
26+
stderr 'Connected to github.com'
27+
28+
# Try to curl another host
29+
! exec -t 1m bash curl.sh 2223 google.com
30+
! stderr 'Connected'
31+
32+
eden pod delete curl-acl
33+
34+
test eden.app.test -test.v -timewait 10m - curl-acl
35+
36+
-- wait_ssh.sh --
37+
38+
EDEN={{EdenConfig "eden.root"}}/{{EdenConfig "eden.bin-dist"}}/{{EdenConfig "eden.eden-bin"}}
39+
HOST=$($EDEN eve ip)
40+
for p in $*
41+
do
42+
for i in `seq 20`
43+
do
44+
sleep 20
45+
# Test SSH-access to container
46+
echo {{template "ssh"}}$HOST -p $p grep -q Ubuntu /etc/issue
47+
{{template "ssh"}}$HOST -p $p grep -q Ubuntu /etc/issue && break
48+
done
49+
done
50+
51+
-- curl.sh --
52+
53+
EDEN={{EdenConfig "eden.root"}}/{{EdenConfig "eden.bin-dist"}}/{{EdenConfig "eden.eden-bin"}}
54+
HOST=$($EDEN eve ip)
55+
56+
echo {{template "ssh"}}$HOST -p $1 curl -v --max-time 30 "$2"
57+
{{template "ssh"}}$HOST -p $1 curl -v --max-time 30 "$2"
58+
59+
-- eden-config.yml --
60+
{{/* Test's config file */}}
61+
test:
62+
controller: adam://{{EdenConfig "adam.ip"}}:{{EdenConfig "adam.port"}}
63+
eve:
64+
{{EdenConfig "eve.name"}}:
65+
onboard-cert: {{EdenConfigPath "eve.cert"}}
66+
serial: "{{EdenConfig "eve.serial"}}"
67+
model: {{EdenConfig "eve.devmodel"}}

tests/workflow/eden.workflow.tests.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,10 @@ eden.escript.test -testdata ../volume/testdata/ -test.run TestEdenScripts/volume
7575
/bin/echo Eden sftp volumes test (17.2/{{$tests}})
7676
eden.escript.test -testdata ../volume/testdata/ -test.run TestEdenScripts/volume_sftp
7777

78-
/bin/echo Eden Host only ACL (18/{{$tests}})
78+
/bin/echo Eden Host only ACL (18.1/{{$tests}})
7979
eden.escript.test -testdata ../eclient/testdata/ -test.run TestEdenScripts/host-only
80+
/bin/echo Eden ACL to particular host (18.2/{{$tests}})
81+
eden.escript.test -testdata ../eclient/testdata/ -test.run TestEdenScripts/acl
8082
/bin/echo Eden Network light (19/{{$tests}})
8183
eden.escript.test -testdata ../eclient/testdata/ -test.run TestEdenScripts/networking_light
8284
/bin/echo Eden Networks switch (20/{{$tests}})

0 commit comments

Comments
 (0)