/
RekeySecretAdminMonitor.java
156 lines (132 loc) · 5.14 KB
/
RekeySecretAdminMonitor.java
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
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
package jenkins.security;
import hudson.Extension;
import hudson.Util;
import hudson.init.InitMilestone;
import hudson.init.Initializer;
import hudson.model.TaskListener;
import hudson.util.HttpResponses;
import hudson.util.SecretRewriter;
import hudson.util.VersionNumber;
import jenkins.management.AsynchronousAdministrativeMonitor;
import jenkins.model.Jenkins;
import jenkins.util.io.FileBoolean;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.interceptor.RequirePOST;
import java.io.File;
import java.io.IOException;
import java.io.PrintStream;
import java.security.GeneralSecurityException;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Warns the administrator to run {@link SecretRewriter}
*
* @author Kohsuke Kawaguchi
*/
@Extension
public class RekeySecretAdminMonitor extends AsynchronousAdministrativeMonitor {
/**
* Whether we detected a need to run the rewrite program.
* Once we set it to true, we'll never turn it off.
*
* If the admin decides to dismiss this warning, we use {@link #isEnabled()} for that.
*
* In this way we can correctly differentiate all the different states.
*/
private final FileBoolean needed = state("needed");
/**
* If the scanning process has run to the completion, we set to this true.
*/
private final FileBoolean done = state("done");
/**
* If the rewrite process is scheduled upon the next boot.
*/
private final FileBoolean scanOnBoot = state("scanOnBoot");
@SuppressWarnings("OverridableMethodCallInConstructor") // should have been final
public RekeySecretAdminMonitor() throws IOException {
// if JENKINS_HOME existed <1.497, we need to offer rewrite
// this computation needs to be done and the value be captured,
// since $JENKINS_HOME/config.xml can be saved later before the user has
// actually rewritten XML files.
Jenkins j = Jenkins.getInstance();
if (j.isUpgradedFromBefore(new VersionNumber("1.496.*"))
&& new FileBoolean(new File(j.getRootDir(),"secret.key.not-so-secret")).isOff())
needed.on();
Util.deleteRecursive(new File(getBaseDir(), "backups")); // SECURITY-376: no longer used
}
@Override
public boolean isActivated() {
return needed.isOn();
}
/**
* Indicates that the re-keying has run to the completion.
*/
public boolean isDone() {
return done.isOn();
}
public void setNeeded() {
needed.on();
}
public boolean isScanOnBoot() {
return scanOnBoot.isOn();
}
@RequirePOST
public HttpResponse doScan(StaplerRequest req) throws IOException, GeneralSecurityException {
if(req.hasParameter("background")) {
start(false);
} else
if(req.hasParameter("schedule")) {
scanOnBoot.on();
} else
if(req.hasParameter("dismiss")) {
disable(true);
} else
throw HttpResponses.error(400,"Invalid request submission: " + req.getParameterMap());
return HttpResponses.redirectViaContextPath("/manage");
}
private FileBoolean state(String name) {
return new FileBoolean(new File(getBaseDir(),name));
}
@Initializer(fatal=false,after=InitMilestone.PLUGINS_STARTED,before=InitMilestone.EXTENSIONS_AUGMENTED)
// as early as possible, but this needs to be late enough that the ConfidentialStore is available
public static void scanOnReboot() throws InterruptedException, IOException, GeneralSecurityException {
RekeySecretAdminMonitor m = new RekeySecretAdminMonitor(); // throw-away instance
FileBoolean flag = m.scanOnBoot;
if (flag.isOn()) {
flag.off();
m.start(false).join();
// block the boot until the rewrite process is complete
// don't let the failure in RekeyThread block Jenkins boot.
}
}
@Override
public String getDisplayName() {
return Messages.RekeySecretAdminMonitor_DisplayName();
}
/**
* Rewrite log file.
*/
@Override
protected File getLogFile() {
return new File(getBaseDir(),"rekey.log");
}
@Override
protected void fix(TaskListener listener) throws Exception {
LOGGER.info("Initiating a re-keying of secrets. See "+getLogFile());
SecretRewriter rewriter = new SecretRewriter();
try {
PrintStream log = listener.getLogger();
log.println("Started re-keying " + new Date());
int count = rewriter.rewriteRecursive(Jenkins.getInstance().getRootDir(), listener);
log.printf("Completed re-keying %d files on %s\n",count,new Date());
new RekeySecretAdminMonitor().done.on();
LOGGER.info("Secret re-keying completed");
} catch (Exception e) {
LOGGER.log(Level.SEVERE, "Fatal failure in re-keying secrets",e);
e.printStackTrace(listener.error("Fatal failure in rewriting secrets"));
}
}
private static final Logger LOGGER = Logger.getLogger(RekeySecretAdminMonitor.class.getName());
}