-
Notifications
You must be signed in to change notification settings - Fork 624
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
Enable sudo nerdctl run to expose ports to localhost #242
Changes from all commits
b32e9b5
c09f01e
9bd9458
6b4315c
cc59dff
2dbb2fd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,4 +7,4 @@ Type=simple | |
Restart=on-failure | ||
|
||
[Install] | ||
WantedBy=default.target | ||
WantedBy=multi-user.target |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,11 +17,6 @@ install -m 755 "${LIMA_CIDATA_MNT}"/lima-guestagent /usr/local/bin/lima-guestage | |
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We probably need to uninstall the previous version of the guest agent There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Just out of curiosity: why do we need to remove it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not overwritten because we switched away from non-root to root There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I meant we have to remove non-root systemd unit There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Ok, thanks, because that is not what mattfarina@3aba8f3 is doing... There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This makes sense. Just pushed a change to remove the legacy systemd configuration. |
||
# Launch the guestagent service | ||
if [ -f /etc/alpine-release ]; then | ||
# Create directory for the lima-guestagent socket (normally done by systemd) | ||
mkdir -p /run/user/"${LIMA_CIDATA_UID}" | ||
gid=$(id -g "${LIMA_CIDATA_USER}") | ||
chown "${LIMA_CIDATA_UID}:${gid}" /run/user/"${LIMA_CIDATA_UID}" | ||
chmod 700 /run/user/"${LIMA_CIDATA_UID}" | ||
# Install the openrc lima-guestagent service script | ||
cat >/etc/init.d/lima-guestagent <<'EOF' | ||
#!/sbin/openrc-run | ||
|
@@ -30,18 +25,18 @@ supervisor=supervise-daemon | |
name="lima-guestagent" | ||
description="Forward ports to the lima-hostagent" | ||
|
||
export XDG_RUNTIME_DIR="/run/user/${LIMA_CIDATA_UID}" | ||
command=/usr/local/bin/lima-guestagent | ||
command_args="daemon" | ||
command_background=true | ||
command_user="${LIMA_CIDATA_USER}:${LIMA_CIDATA_USER}" | ||
pidfile="${XDG_RUNTIME_DIR}/lima-guestagent.pid" | ||
pidfile="/run/lima-guestagent.pid" | ||
EOF | ||
chmod 755 /etc/init.d/lima-guestagent | ||
|
||
rc-update add lima-guestagent default | ||
rc-service lima-guestagent start | ||
else | ||
until [ -e "/run/user/${LIMA_CIDATA_UID}/systemd/private" ]; do sleep 3; done | ||
sudo -iu "${LIMA_CIDATA_USER}" "XDG_RUNTIME_DIR=/run/user/${LIMA_CIDATA_UID}" lima-guestagent install-systemd | ||
# Remove legacy systemd service | ||
rm -f "/home/${LIMA_CIDATA_USER}.linux/.config/systemd/user/lima-guestagent.service" | ||
|
||
sudo lima-guestagent install-systemd | ||
fi |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
package iptables | ||
|
||
import ( | ||
"bytes" | ||
"errors" | ||
"net" | ||
"os/exec" | ||
"regexp" | ||
"strconv" | ||
"strings" | ||
"time" | ||
) | ||
|
||
type Entry struct { | ||
TCP bool | ||
IP net.IP | ||
Port int | ||
} | ||
|
||
// This regex can detect a line in the iptables added by portmap to do the | ||
// forwarding. The following two are examples of lines (notice that one has the | ||
// destination IP and the other does not): | ||
// -A CNI-DN-2e2f8d5b91929ef9fc152 -d 127.0.0.1/32 -p tcp -m tcp --dport 8081 -j DNAT --to-destination 10.4.0.7:80 | ||
// -A CNI-DN-04579c7bb67f4c3f6cca0 -p tcp -m tcp --dport 8082 -j DNAT --to-destination 10.4.0.10:80 | ||
// The -A on the front is to amend the rule that was already created. portmap | ||
// ensures the rule is created before creating this line so it is always -A. | ||
// CNI-DN- is the prefix used for rule for an individual container. | ||
// -d is followed by the IP address. The regular expression looks for a valid | ||
// ipv4 IP address. We need to detect this IP. | ||
// --dport is the destination port. We need to detect this port | ||
// -j DNAT this tells us it's the line doing the port forwarding. | ||
var findPortRegex = regexp.MustCompile(`-A\s+CNI-DN-\w*\s+(?:-d ((?:\b25[0-5]|\b2[0-4][0-9]|\b[01]?[0-9][0-9]?)(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}))?(?:/32\s+)?-p (tcp)?.*--dport (\d+) -j DNAT`) | ||
|
||
func GetPorts() ([]Entry, error) { | ||
// TODO: add support for ipv6 | ||
|
||
// Detect the location of iptables. If it is not installed skip the lookup | ||
// and return no results. The lookup is performed on each run so that the | ||
// agent does not need to be started to detect if iptables was installed | ||
// after the agent is already running. | ||
pth, err := exec.LookPath("iptables") | ||
if err != nil { | ||
if errors.Is(err, exec.ErrNotFound) { | ||
return nil, nil | ||
} | ||
|
||
return nil, err | ||
} | ||
|
||
res, err := listNATRules(pth) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
pts, err := parsePortsFromRules(res) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
return checkPortsOpen(pts) | ||
} | ||
|
||
func parsePortsFromRules(rules []string) ([]Entry, error) { | ||
var entries []Entry | ||
for _, rule := range rules { | ||
if found := findPortRegex.FindStringSubmatch(rule); found != nil { | ||
if len(found) == 4 { | ||
port, err := strconv.Atoi(found[3]) | ||
if err != nil { | ||
return nil, err | ||
} | ||
|
||
istcp := false | ||
if found[2] == "tcp" { | ||
istcp = true | ||
} | ||
|
||
// if the IP is blank the port forwarding the portforwarding, | ||
// which gets information from this, will skip it. When no IP | ||
// is present localhost will work. | ||
ip := found[1] | ||
if ip == "" { | ||
ip = "127.0.0.1" | ||
} | ||
ent := Entry{ | ||
IP: net.ParseIP(ip), | ||
Port: port, | ||
TCP: istcp, | ||
} | ||
entries = append(entries, ent) | ||
} | ||
} | ||
} | ||
|
||
return entries, nil | ||
} | ||
|
||
// listNATRules performs the lookup with iptables and returns the raw rules | ||
// Note, this does not use github.com/coreos/go-iptables (a transitive dependency | ||
// of lima) because that package would require multiple calls to iptables. This | ||
// function does everything in a single call. | ||
func listNATRules(pth string) ([]string, error) { | ||
args := []string{pth, "-t", "nat", "-S"} | ||
|
||
var stdout bytes.Buffer | ||
var stderr bytes.Buffer | ||
cmd := exec.Cmd{ | ||
Path: pth, | ||
Args: args, | ||
Stdout: &stdout, | ||
Stderr: &stderr, | ||
} | ||
if err := cmd.Run(); err != nil { | ||
return nil, err | ||
} | ||
|
||
// turn the output into a rule per line. | ||
rules := strings.Split(stdout.String(), "\n") | ||
if len(rules) > 0 && rules[len(rules)-1] == "" { | ||
rules = rules[:len(rules)-1] | ||
} | ||
|
||
return rules, nil | ||
} | ||
|
||
func checkPortsOpen(pts []Entry) ([]Entry, error) { | ||
var entries []Entry | ||
for _, pt := range pts { | ||
if pt.TCP { | ||
conn, err := net.DialTimeout("tcp", net.JoinHostPort(pt.IP.String(), strconv.Itoa(pt.Port)), time.Second) | ||
if err == nil && conn != nil { | ||
conn.Close() | ||
entries = append(entries, pt) | ||
} | ||
} else { | ||
entries = append(entries, pt) | ||
} | ||
} | ||
|
||
return entries, nil | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably this ticking is too heavy for watching iptables. Instead we should wait for
NETLINK_AUDIT
events or some eBPF stuff, but I can work on that in a separate PR after merging this.