Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

LXC container support without LXD #552

Merged
merged 3 commits into from
Dec 18, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,9 +75,10 @@ Table of Contents
* [Example: Scan specific servers](#example-scan-specific-servers)
* [Example: Scan via shell instead of SSH.](#example-scan-via-shell-instead-of-ssh)
* [cronで動かす場合](#cronで動かす場合)
* [Example: Scan containers (Docker/LXD)](#example-scan-containers-dockerlxd)
* [Example: Scan containers (Docker/LXD/LXC)](#example-scan-containers-dockerlxdlxc)
* [Docker](#docker)
* [LXDコンテナをスキャンする場合](#lxdコンテナをスキャンする場合)
* [LXCコンテナをスキャンする場合](#lxcコンテナをスキャンする場合)
* [Usage: Report](#usage-report)
* [How to read a report](#how-to-read-a-report)
* [Example](#example-1)
Expand Down Expand Up @@ -734,7 +735,7 @@ host = "172.31.4.82"
# ["key", "value"],
#]
#[servers.172-31-4-82.containers]
#type = "lxd" # or "docker"
#type = "lxd" # or "docker" or "lxc"
#includes = ["${running}"]
#excludes = ["container_name", "container_id"]
```
Expand Down Expand Up @@ -819,7 +820,7 @@ host = "172.31.4.82"
# ["key", "value"],
#]
#[servers.172-31-4-82.containers]
#type = "lxd" # or "docker"
#type = "lxd" # or "docker" or "lxc"
#includes = ["${running}"]
#excludes = ["container_name", "container_id"]
```
Expand Down Expand Up @@ -1101,7 +1102,7 @@ RHEL/CentOSの場合、スキャン対象サーバの/etc/sudoersに以下を追
Defaults:vuls !requiretty
```

## Example: Scan containers (Docker/LXD)
## Example: Scan containers (Docker/LXD/LXC)


コンテナはSSHデーモンを起動しないで運用するケースが一般的。
Expand Down Expand Up @@ -1175,6 +1176,30 @@ type = "lxd"
includes = ["${running}"]
```

### LXC

Vulsは、ホストにSSHで接続し、`lxc-attach`でLXCコンテナにコマンドを発行して脆弱性をスキャンする。
```
[servers]

[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"

[servers.172-31-4-82.containers]
type = "lxc"
includes = ["${running}"]
```

LXCコンテナの操作にはroot権限が必要です。

スキャン対象サーバ上の`/etc/sudoers`のサンプル

```
vuls ALL=(ALL) NOPASSWD:/usr/bin/lxc-attach -n *, /usr/bin/lxc-ls *
```

# Usage: Report

```
Expand Down
35 changes: 30 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ Table of Contents
* [Example: Scan specific servers](#example-scan-specific-servers)
* [Example: Scan via shell instead of SSH.](#example-scan-via-shell-instead-of-ssh)
* [cron](#cron)
* [Example: Scan containers (Docker/LXD)](#example-scan-containers-dockerlxd)
* [Example: Scan containers (Docker/LXD/LXC)](#example-scan-containers-dockerlxdlxc)
* [Docker](#docker)
* [LXD](#lxd)
* [LXC](#lxc)
* [Usage: Report](#usage-report)
* [How to read a report](#how-to-read-a-report)
* [Example](#example-1)
Expand Down Expand Up @@ -747,7 +748,7 @@ host = "172.31.4.82"
# ["key", "value"],
#]
#[servers.172-31-4-82.containers]
#type = "lxd" # or "docker"
#type = "lxd" # or "docker" or "lxc"
#includes = ["${running}"]
#excludes = ["container_name", "container_id"]
```
Expand Down Expand Up @@ -852,7 +853,7 @@ You can customize your configuration using this template.
# ["key", "value"],
#]
#[servers.172-31-4-82.containers]
#type = "lxd" # or "docker"
#type = "lxd" # or "docker" or "lxc"
#includes = ["${running}"]
#excludes = ["container_name", "container_id"]
```
Expand All @@ -867,7 +868,7 @@ You can customize your configuration using this template.
- cpeNames: see [Usage: Scan vulnerability of non-OS package](#usage-scan-vulnerability-of-non-os-package)
- ignoreCves: CVE IDs that will not be reported. But output to JSON file.
- optional: Add additional information to JSON report.
- containers: see [Example: Scan containers (Docker/LXD)(#example-scan-containers-dockerlxd)
- containers: see [Example: Scan containers (Docker/LXD/LXC)(#example-scan-containers-dockerlxdlxc)

Vuls supports two types of SSH. One is external command. The other is native go implementation. For details, see [-ssh-native-insecure option](#-ssh-native-insecure-option)

Expand Down Expand Up @@ -1106,7 +1107,7 @@ If you use local scan mode for cron jobs, don't forget to add below line to `/et
Defaults:vuls !requiretty
```

## Example: Scan containers (Docker/LXD)
## Example: Scan containers (Docker/LXD/LXC)

It is common that keep containers running without SSHd daemon.
see [Docker Blog:Why you don't need to run SSHd in your Docker containers](https://blog.docker.com/2014/06/why-you-dont-need-to-run-sshd-in-docker/)
Expand Down Expand Up @@ -1180,6 +1181,30 @@ type = "lxd"
includes = ["${running}"]
```

### LXC

Vuls scans lxc via `lxc-attach` instead of SSH.
```
[servers]

[servers.172-31-4-82]
host = "172.31.4.82"
user = "ec2-user"
keyPath = "/home/username/.ssh/id_rsa"

[servers.172-31-4-82.containers]
type = "lxc"
includes = ["${running}"]
```

LXC required root privilege.

Example of /etc/sudoers on target servers

```
vuls ALL=(ALL) NOPASSWD:/usr/bin/lxc-attach -n *, /usr/bin/lxc-ls *
```

----

# Usage: Report
Expand Down
42 changes: 42 additions & 0 deletions scan/base.go
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ func (l *base) allContainers() (containers []config.Container, err error) {
return containers, err
}
return l.parseLxdPs(stdout)
case "lxc":
stdout, err := l.lxcPs("-1")
if err != nil {
return containers, err
}
return l.parseLxcPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Containers.Type)
Expand All @@ -130,6 +136,12 @@ func (l *base) runningContainers() (containers []config.Container, err error) {
return containers, err
}
return l.parseLxdPs(stdout)
case "lxc":
stdout, err := l.lxcPs("-1 --running")
if err != nil {
return containers, err
}
return l.parseLxcPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Containers.Type)
Expand All @@ -150,6 +162,12 @@ func (l *base) exitedContainers() (containers []config.Container, err error) {
return containers, err
}
return l.parseLxdPs(stdout)
case "lxc":
stdout, err := l.lxcPs("-1 --stopped")
if err != nil {
return containers, err
}
return l.parseLxcPs(stdout)
default:
return containers, fmt.Errorf(
"Not supported yet: %s", l.ServerInfo.Containers.Type)
Expand All @@ -174,6 +192,15 @@ func (l *base) lxdPs(option string) (string, error) {
return r.Stdout, nil
}

func (l *base) lxcPs(option string) (string, error) {
cmd := fmt.Sprintf("lxc-ls %s 2>/dev/null", option)
r := l.exec(cmd, sudo)
if !r.isSuccess() {
return "", fmt.Errorf("failed to SSH: %s", r)
}
return r.Stdout, nil
}

func (l *base) parseDockerPs(stdout string) (containers []config.Container, err error) {
lines := strings.Split(stdout, "\n")
for _, line := range lines {
Expand Down Expand Up @@ -214,6 +241,21 @@ func (l *base) parseLxdPs(stdout string) (containers []config.Container, err err
return
}

func (l *base) parseLxcPs(stdout string) (containers []config.Container, err error) {
lines := strings.Split(stdout, "\n")
for _, line := range lines {
fields := strings.Fields(line)
if len(fields) == 0 {
break
}
containers = append(containers, config.Container{
ContainerID: fields[0],
Name: fields[0],
})
}
return
}

func (l *base) detectPlatform() {
ok, instanceID, err := l.detectRunningOnAws()
if err != nil {
Expand Down
8 changes: 8 additions & 0 deletions scan/executil.go
Original file line number Diff line number Diff line change
Expand Up @@ -365,8 +365,16 @@ func decorateCmd(c conf.ServerInfo, cmd string, sudo bool) string {
cmd = fmt.Sprintf(`docker exec --user 0 %s %s -c '%s'`,
c.Container.ContainerID, dockerShell(c.Distro.Family), cmd)
case "lxd":
// If the user belong to the "lxd" group, root privilege is not required.
cmd = fmt.Sprintf(`lxc exec %s -- %s -c '%s'`,
c.Container.Name, dockerShell(c.Distro.Family), cmd)
case "lxc":
cmd = fmt.Sprintf(`lxc-attach -n %s 2>/dev/null -- %s -c '%s'`,
c.Container.Name, dockerShell(c.Distro.Family), cmd)
// LXC required root privilege
if c.User != "root" {
cmd = fmt.Sprintf("sudo -S %s", cmd)
}
}
}
// cmd = fmt.Sprintf("set -x; %s", cmd)
Expand Down
72 changes: 64 additions & 8 deletions scan/executil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,21 +46,21 @@ func TestDecorateCmd(t *testing.T) {
},
// non-root sudo false
{
conf: config.ServerInfo{User: "non-roor"},
conf: config.ServerInfo{User: "non-root"},
cmd: "ls",
sudo: false,
expected: "ls",
},
// non-root sudo true
{
conf: config.ServerInfo{User: "non-roor"},
conf: config.ServerInfo{User: "non-root"},
cmd: "ls",
sudo: true,
expected: "sudo -S ls",
},
// non-root sudo true
{
conf: config.ServerInfo{User: "non-roor"},
conf: config.ServerInfo{User: "non-root"},
cmd: "ls | grep hoge",
sudo: true,
expected: "sudo -S ls | grep hoge",
Expand All @@ -70,7 +70,7 @@ func TestDecorateCmd(t *testing.T) {
{
conf: config.ServerInfo{
User: "root",
Container: config.Container{ContainerID: "abc"},
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "docker"},
},
cmd: "ls",
Expand All @@ -81,7 +81,7 @@ func TestDecorateCmd(t *testing.T) {
{
conf: config.ServerInfo{
User: "root",
Container: config.Container{ContainerID: "abc"},
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "docker"},
},
cmd: "ls",
Expand All @@ -92,7 +92,7 @@ func TestDecorateCmd(t *testing.T) {
{
conf: config.ServerInfo{
User: "non-root",
Container: config.Container{ContainerID: "abc"},
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "docker"},
},
cmd: "ls",
Expand All @@ -103,7 +103,7 @@ func TestDecorateCmd(t *testing.T) {
{
conf: config.ServerInfo{
User: "non-root",
Container: config.Container{ContainerID: "abc"},
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "docker"},
},
cmd: "ls",
Expand All @@ -114,7 +114,7 @@ func TestDecorateCmd(t *testing.T) {
{
conf: config.ServerInfo{
User: "non-root",
Container: config.Container{ContainerID: "abc"},
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "docker"},
},
cmd: "ls | grep hoge",
Expand Down Expand Up @@ -177,6 +177,62 @@ func TestDecorateCmd(t *testing.T) {
sudo: true,
expected: `lxc exec def -- /bin/sh -c 'ls | grep hoge'`,
},
// -------------lxc-------------
// root sudo false lxc
{
conf: config.ServerInfo{
User: "root",
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "lxc"},
},
cmd: "ls",
sudo: false,
expected: `lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls'`,
},
// root sudo true lxc
{
conf: config.ServerInfo{
User: "root",
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "lxc"},
},
cmd: "ls",
sudo: true,
expected: `lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls'`,
},
// non-root sudo false, lxc
{
conf: config.ServerInfo{
User: "non-root",
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "lxc"},
},
cmd: "ls",
sudo: false,
expected: `sudo -S lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls'`,
},
// non-root sudo true, lxc
{
conf: config.ServerInfo{
User: "non-root",
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "lxc"},
},
cmd: "ls",
sudo: true,
expected: `sudo -S lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls'`,
},
// non-root sudo true lxc
{
conf: config.ServerInfo{
User: "non-root",
Container: config.Container{ContainerID: "abc", Name: "def"},
Containers: config.Containers{Type: "lxc"},
},
cmd: "ls | grep hoge",
sudo: true,
expected: `sudo -S lxc-attach -n def 2>/dev/null -- /bin/sh -c 'ls | grep hoge'`,
},
}

for _, tt := range tests {
Expand Down