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
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
|
// Copyright 2018 Yahoo Holdings. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.
package com.yahoo.vespa.hosted.node.admin.maintenance.acl;
import com.yahoo.vespa.hosted.dockerapi.ContainerName;
import com.yahoo.vespa.hosted.dockerapi.ProcessResult;
import com.yahoo.vespa.hosted.node.admin.docker.DockerOperations;
import com.yahoo.vespa.hosted.node.admin.task.util.file.Editor;
import com.yahoo.vespa.hosted.node.admin.task.util.file.LineEditor;
import com.yahoo.vespa.hosted.node.admin.task.util.network.IPVersion;
import com.yahoo.vespa.hosted.node.admin.util.PrefixLogger;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.stream.Collectors;
/**
* Edit the iptables for docker containers.
*/
class IPTablesEditor {
private final PrefixLogger log;
private final DockerOperations dockerOperations;
private final ContainerName containerName;
private final Consumer<String> testInterceptor;
public IPTablesEditor(DockerOperations dockerOperations, ContainerName containerName) {
this(dockerOperations, containerName, (result) -> {});
}
IPTablesEditor(DockerOperations dockerOperations, ContainerName containerName, Consumer<String> testInterceptor) {
this.dockerOperations = dockerOperations;
this.containerName = containerName;
this.testInterceptor = testInterceptor;
this.log = PrefixLogger.getNodeAgentLogger(AclMaintainer.class, containerName);
}
public static boolean editFlushOnError(DockerOperations dockerOperations, ContainerName containerName, IPVersion ipVersion, String table, LineEditor lineEditor) {
return new IPTablesEditor(dockerOperations, containerName).edit(table, ipVersion, lineEditor, true);
}
public static boolean editLogOnError(DockerOperations dockerOperations, ContainerName containerName, IPVersion ipVersion, String table, LineEditor lineEditor) {
return new IPTablesEditor(dockerOperations, containerName).edit(table, ipVersion, lineEditor, false);
}
public boolean edit(String table, IPVersion ipVersion, LineEditor lineEditor, boolean flush) {
String editId = ipVersion.iptablesCmd() + "-" + table;
Editor editor = new Editor(editId, listTable(table, ipVersion), restoreTable(table, ipVersion, flush), lineEditor);
return editor.edit(log::info);
}
private Supplier<List<String>> listTable(String table, IPVersion ipVersion) {
return () -> {
ProcessResult currentRulesResult =
dockerOperations.executeCommandInNetworkNamespace(containerName, ipVersion.iptablesCmd(), "-S", "-t", table);
return Arrays.stream(currentRulesResult.getOutput().split("\n"))
.map(String::trim)
.collect(Collectors.toList());
};
}
private Consumer<List<String>> restoreTable(String table, IPVersion ipVersion, boolean flush) {
return list -> {
File file = null;
try {
String rules = String.join("\n", list);
String fileContent = "*" + table + "\n" + rules + "\nCOMMIT\n";
file = writeTempFile(table, fileContent);
dockerOperations.executeCommandInNetworkNamespace(containerName, ipVersion.iptablesRestore(), file.getAbsolutePath());
testInterceptor.accept(fileContent);
} catch (Exception e) {
if (flush) {
log.error("Exception occurred while syncing iptable " + table + " for " + containerName.asString() + ", attempting rollback", e);
try {
dockerOperations.executeCommandInNetworkNamespace(containerName, ipVersion.iptablesCmd(), "-F", "-t", table);
} catch (Exception ne) {
log.error("Rollback of table " + table + " for " + containerName.asString() + " failed, giving up", ne);
}
} else {
log.warning("Unable to sync iptables for " + table, e);
}
} finally {
if (file != null) {
file.delete();
}
}
};
}
private File writeTempFile(String table, String content) {
try {
Path path = Files.createTempFile("iptables-restore", "." + table);
File file = path.toFile();
Files.write(path, content.getBytes(StandardCharsets.UTF_8));
file.deleteOnExit();
return file;
} catch (IOException e) {
throw new RuntimeException("Unable to write restore file for iptables.", e);
}
}
}
|