summaryrefslogtreecommitdiffstats
path: root/node-admin/src/main/java/com/yahoo/vespa/hosted/node/admin/maintenance/acl/Acl.java
blob: 250b4ee6fb30b479924fe97b06edd34fdf344b55 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
package com.yahoo.vespa.hosted.node.admin.maintenance.acl;

import com.google.common.collect.ImmutableList;
import com.google.common.net.InetAddresses;
import com.yahoo.vespa.hosted.node.admin.ContainerAclSpec;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.iptables.Action;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.iptables.Chain;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.iptables.Command;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.iptables.FilterCommand;
import com.yahoo.vespa.hosted.node.admin.maintenance.acl.iptables.PolicyCommand;

import java.net.Inet6Address;
import java.util.List;
import java.util.Objects;

/**
 * This class represents an ACL for a specific container instance
 *
 * @author mpolden
 */
public class Acl {

    private final int containerPid;
    private final List<ContainerAclSpec> containerAclSpecs;

    public Acl(int containerPid, List<ContainerAclSpec> containerAclSpecs) {
        this.containerPid = containerPid;
        this.containerAclSpecs = ImmutableList.copyOf(containerAclSpecs);
    }

    public List<Command> toCommands() {
        final ImmutableList.Builder<Command> commands = ImmutableList.builder();
        commands.add(
                // Default policies. Packets that do not match any rules will be processed according to policy.
                new PolicyCommand(Chain.INPUT, Action.DROP),
                new PolicyCommand(Chain.FORWARD, Action.DROP),
                new PolicyCommand(Chain.OUTPUT, Action.ACCEPT),

                // Allow packets belonging to established connections
                new FilterCommand(Chain.INPUT, Action.ACCEPT)
                        .withOption("-m", "state")
                        .withOption("--state", "RELATED,ESTABLISHED"),

                // Allow any loopback traffic
                new FilterCommand(Chain.INPUT, Action.ACCEPT)
                         .withOption("-i", "lo"),

                // Allow IPv6 ICMP packets. This is required for IPv6 routing (e.g. path MTU) to work correctly.
                new FilterCommand(Chain.INPUT, Action.ACCEPT)
                        .withOption("-p", "ipv6-icmp"));

        // Allow traffic from trusted containers
        containerAclSpecs.stream()
                .map(ContainerAclSpec::ipAddress)
                .filter(Acl::isIpv6)
                .map(ipAddress -> new FilterCommand(Chain.INPUT, Action.ACCEPT)
                        .withOption("-s", String.format("%s/128", ipAddress)))
                .forEach(commands::add);

        // Reject all other packets. This means that packets that would otherwise be processed according to policy, are
        // matched by the following rule.
        //
        // Ideally, we want to set the INPUT policy to REJECT and get rid of this rule, but unfortunately REJECT is not
        // a valid policy action.
        commands.add(new FilterCommand(Chain.INPUT, Action.REJECT));

        return commands.build();
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Acl that = (Acl) o;
        return containerPid == that.containerPid &&
                Objects.equals(containerAclSpecs, that.containerAclSpecs);
    }

    @Override
    public int hashCode() {
        return Objects.hash(containerPid, containerAclSpecs);
    }

    private static boolean isIpv6(String ipAddress) {
        return InetAddresses.forString(ipAddress) instanceof Inet6Address;
    }
}