提交 17894bc1 编写于 作者: S Stephen Connolly

[FIXED JENKINS-37032] Add a UI to allow controling the agent protocol enablement

上级 389b0575
......@@ -26,6 +26,7 @@ package hudson;
import java.nio.charset.Charset;
import java.security.interfaces.RSAPublicKey;
import javax.annotation.Nullable;
import jenkins.model.Jenkins;
import jenkins.model.identity.InstanceIdentityProvider;
import jenkins.util.SystemProperties;
import hudson.slaves.OfflineCause;
......@@ -50,6 +51,7 @@ import java.util.logging.Level;
import java.util.logging.Logger;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
/**
* Listens to incoming TCP connections from JNLP agents and CLI.
......@@ -127,20 +129,7 @@ public final class TcpSlaveAgentListener extends Thread {
* @since FIXME
*/
public String getAgentProtocolNames() {
StringBuilder result = new StringBuilder();
boolean first = true;
for (AgentProtocol p : AgentProtocol.all()) {
String name = p.getName();
if (name != null) {
if (first) {
first = false;
} else {
result.append(", ");
}
result.append(name);
}
}
return result.toString();
return StringUtils.join(Jenkins.getInstance().getAgentProtocols(), ", ");
}
@Override
......@@ -218,9 +207,13 @@ public final class TcpSlaveAgentListener extends Thread {
if(s.startsWith("Protocol:")) {
String protocol = s.substring(9);
AgentProtocol p = AgentProtocol.of(protocol);
if (p!=null)
p.handle(this.s);
else
if (p!=null) {
if (Jenkins.getInstance().getAgentProtocols().contains(protocol)) {
p.handle(this.s);
} else {
error(out, "Disabled protocol:" + s);
}
} else
error(out, "Unknown protocol:" + s);
} else {
error(out, "Unrecognized protocol: "+s);
......@@ -268,6 +261,18 @@ public final class TcpSlaveAgentListener extends Thread {
}
}
/**
* Allow essential {@link AgentProtocol} implementations (basically {@link PingAgentProtocol})
* to be always enabled.
*
* @return {@code true} if the protocol can never be disbaled.
* @since FIXME
*/
@Override
public boolean isRequired() {
return true;
}
@Override
public String getName() {
return "Ping";
......
......@@ -35,6 +35,8 @@ import hudson.model.ManagementLink;
import hudson.util.FormApply;
import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
......@@ -43,6 +45,7 @@ import javax.servlet.ServletException;
import jenkins.model.GlobalConfigurationCategory;
import jenkins.model.Jenkins;
import jenkins.util.ServerTcpPort;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject;
import org.jenkinsci.Symbol;
......@@ -69,6 +72,10 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
return Jenkins.getInstance().getSlaveAgentPort();
}
public Set<String> getAgentProtocols() {
return Jenkins.getInstance().getAgentProtocols();
}
public boolean isDisableRememberMe() {
return Jenkins.getInstance().isDisableRememberMe();
}
......@@ -100,6 +107,18 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
} catch (IOException e) {
throw new hudson.model.Descriptor.FormException(e, "slaveAgentPortType");
}
Set<String> agentProtocols = new TreeSet<>();
if (security.has("agentProtocol")) {
Object protocols = security.get("agentProtocol");
if (protocols instanceof JSONArray) {
for (int i = 0; i < ((JSONArray) protocols).size(); i++) {
agentProtocols.add(((JSONArray) protocols).getString(i));
}
} else {
agentProtocols.add(protocols.toString());
}
}
j.setAgentProtocols(agentProtocols);
} else {
j.disableSecurity();
}
......
......@@ -21,6 +21,24 @@ import java.net.Socket;
* @see TcpSlaveAgentListener
*/
public abstract class AgentProtocol implements ExtensionPoint {
/**
* Allow experimental {@link AgentProtocol} implementations to declare being opt-in.
* @return {@code true} if the protocol requires explicit opt-in.
* @since FIXME
*/
public boolean isOptIn() {
return false;
}
/**
* Allow essential {@link AgentProtocol} implementations (basically {@link TcpSlaveAgentListener.PingAgentProtocol})
* to be always enabled.
*
* @return {@code true} if the protocol can never be disbaled.
* @since FIXME
*/
public boolean isRequired() {
return false;
}
/**
* Protocol name.
*
......
......@@ -50,6 +50,7 @@ import hudson.Plugin;
import hudson.PluginManager;
import hudson.PluginWrapper;
import hudson.ProxyConfiguration;
import jenkins.AgentProtocol;
import jenkins.util.SystemProperties;
import hudson.TcpSlaveAgentListener;
import hudson.UDPBroadcastThread;
......@@ -224,6 +225,7 @@ import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.acegisecurity.ui.AbstractProcessingFilter;
import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.Script;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory;
import org.jvnet.hudson.reactor.Executable;
import org.jvnet.hudson.reactor.Milestone;
......@@ -598,6 +600,28 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/
private int slaveAgentPort = SystemProperties.getInteger(Jenkins.class.getName()+".slaveAgentPort",0);
/**
* The TCP agent protocols that are explicitly disabled (we store the disabled ones so that newer protocols
* are enabled by default).
*
* @since FIXME
*/
private String disabledAgentProtocols;
/**
* The TCP agent protocols that are {@link AgentProtocol#isOptIn()} and explicitly enabled.
*
* @since FIXME
*/
private String enabledAgentProtocols;
/**
* The TCP agent protocols that are enabled. Built from {@link #disabledAgentProtocols}.
*
* @since FIXME
*/
private transient Set<String> agentProtocols;
/**
* Whitespace-separated labels assigned to the master as a {@link Node}.
*/
......@@ -1060,6 +1084,65 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
launchTcpSlaveAgentListener();
}
/**
* Returns the enabled agent protocols.
*
* @return the enabled agent protocols.
* @since FIXME
*/
public Set<String> getAgentProtocols() {
if (agentProtocols == null) {
// idempotent, so don't care if we do this concurrently, should all get same result
Set<String> result = new TreeSet<>();
Set<String> disabled = new TreeSet<>();
for (String p : StringUtils.split(StringUtils.defaultIfBlank(disabledAgentProtocols, ""), ",")) {
disabled.add(p.trim());
}
Set<String> enabled = new TreeSet<>();
for (String p : StringUtils.split(StringUtils.defaultIfBlank(enabledAgentProtocols, ""), ",")) {
enabled.add(p.trim());
}
for (AgentProtocol p : AgentProtocol.all()) {
String name = p.getName();
if (name != null && (p.isRequired()
|| (!disabled.contains(name) && (!p.isOptIn() || enabled.contains(name))))) {
result.add(name);
}
}
agentProtocols = result;
return result;
}
return agentProtocols;
}
/**
* Sets the enabled agent protocols.
*
* @param protocols the enabled agent protocols.
* @since FIXME
*/
public void setAgentProtocols(Set<String> protocols) {
Set<String> disabled = new TreeSet<>();
Set<String> enabled = new TreeSet<>();
for (AgentProtocol p : AgentProtocol.all()) {
String name = p.getName();
if (name != null && !p.isRequired()) {
if (p.isOptIn()) {
if (protocols.contains(name)) {
enabled.add(name);
}
} else {
if (!protocols.contains(name)) {
disabled.add(name);
}
}
}
}
disabledAgentProtocols = fixEmpty(StringUtils.join(disabled, ", "));
enabledAgentProtocols = fixEmpty(StringUtils.join(enabled, ", "));
agentProtocols = null;
}
private void launchTcpSlaveAgentListener() throws IOException {
synchronized(tcpSlaveAgentListenerLock) {
// shutdown previous agent if the port has changed
......
......@@ -23,7 +23,7 @@ THE SOFTWARE.
-->
<?jelly escape-by-default='true'?>
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt" trim="true">
<st:contentType value="text/plain;charset=UTF-8"/>
<!--
Publicize the TCP port number for JNLP agents so that they know where to conenct.
......
<div>
Jenkins uses a TCP port to communicate with various remote agents. This option allows control
over which agent protocols are enabled.
</div>
\ No newline at end of file
......@@ -3,6 +3,7 @@ package hudson.security.GlobalSecurityConfiguration
import hudson.security.SecurityRealm
import hudson.markup.MarkupFormatterDescriptor
import hudson.security.AuthorizationStrategy
import jenkins.AgentProtocol
import jenkins.model.GlobalConfiguration
import hudson.Functions
import hudson.model.Descriptor
......@@ -28,6 +29,20 @@ l.layout(norefresh:true, permission:app.ADMINISTER, title:my.displayName, csscla
f.entry (title:_("TCP port for JNLP agents"), field:"slaveAgentPort") {
f.serverTcpPort()
}
f.advanced(title: _("Agent protocols"), align:"left") {
f.entry(title: _("Agent protocols")) {
def agentProtocols = my.agentProtocols;
for (AgentProtocol p: AgentProtocol.all()) {
if (p.name != null && !p.required) {
f.checkbox(name: "agentProtocol",
title: p.name,
checked:agentProtocols.contains(p.name),
json: p.name);
br();
}
}
}
}
f.entry (title:_("Disable remember me"), field: "disableRememberMe") {
f.checkbox()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册