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

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

上级 389b0575
...@@ -26,6 +26,7 @@ package hudson; ...@@ -26,6 +26,7 @@ package hudson;
import java.nio.charset.Charset; import java.nio.charset.Charset;
import java.security.interfaces.RSAPublicKey; import java.security.interfaces.RSAPublicKey;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import jenkins.model.Jenkins;
import jenkins.model.identity.InstanceIdentityProvider; import jenkins.model.identity.InstanceIdentityProvider;
import jenkins.util.SystemProperties; import jenkins.util.SystemProperties;
import hudson.slaves.OfflineCause; import hudson.slaves.OfflineCause;
...@@ -50,6 +51,7 @@ import java.util.logging.Level; ...@@ -50,6 +51,7 @@ import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.apache.commons.codec.binary.Base64; import org.apache.commons.codec.binary.Base64;
import org.apache.commons.io.IOUtils; import org.apache.commons.io.IOUtils;
import org.apache.commons.lang.StringUtils;
/** /**
* Listens to incoming TCP connections from JNLP agents and CLI. * Listens to incoming TCP connections from JNLP agents and CLI.
...@@ -127,20 +129,7 @@ public final class TcpSlaveAgentListener extends Thread { ...@@ -127,20 +129,7 @@ public final class TcpSlaveAgentListener extends Thread {
* @since FIXME * @since FIXME
*/ */
public String getAgentProtocolNames() { public String getAgentProtocolNames() {
StringBuilder result = new StringBuilder(); return StringUtils.join(Jenkins.getInstance().getAgentProtocols(), ", ");
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();
} }
@Override @Override
...@@ -218,9 +207,13 @@ public final class TcpSlaveAgentListener extends Thread { ...@@ -218,9 +207,13 @@ public final class TcpSlaveAgentListener extends Thread {
if(s.startsWith("Protocol:")) { if(s.startsWith("Protocol:")) {
String protocol = s.substring(9); String protocol = s.substring(9);
AgentProtocol p = AgentProtocol.of(protocol); AgentProtocol p = AgentProtocol.of(protocol);
if (p!=null) if (p!=null) {
if (Jenkins.getInstance().getAgentProtocols().contains(protocol)) {
p.handle(this.s); p.handle(this.s);
else } else {
error(out, "Disabled protocol:" + s);
}
} else
error(out, "Unknown protocol:" + s); error(out, "Unknown protocol:" + s);
} else { } else {
error(out, "Unrecognized protocol: "+s); error(out, "Unrecognized protocol: "+s);
...@@ -268,6 +261,18 @@ public final class TcpSlaveAgentListener extends Thread { ...@@ -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 @Override
public String getName() { public String getName() {
return "Ping"; return "Ping";
......
...@@ -35,6 +35,8 @@ import hudson.model.ManagementLink; ...@@ -35,6 +35,8 @@ import hudson.model.ManagementLink;
import hudson.util.FormApply; import hudson.util.FormApply;
import java.io.IOException; import java.io.IOException;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
...@@ -43,6 +45,7 @@ import javax.servlet.ServletException; ...@@ -43,6 +45,7 @@ import javax.servlet.ServletException;
import jenkins.model.GlobalConfigurationCategory; import jenkins.model.GlobalConfigurationCategory;
import jenkins.model.Jenkins; import jenkins.model.Jenkins;
import jenkins.util.ServerTcpPort; import jenkins.util.ServerTcpPort;
import net.sf.json.JSONArray;
import net.sf.json.JSONObject; import net.sf.json.JSONObject;
import org.jenkinsci.Symbol; import org.jenkinsci.Symbol;
...@@ -69,6 +72,10 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr ...@@ -69,6 +72,10 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
return Jenkins.getInstance().getSlaveAgentPort(); return Jenkins.getInstance().getSlaveAgentPort();
} }
public Set<String> getAgentProtocols() {
return Jenkins.getInstance().getAgentProtocols();
}
public boolean isDisableRememberMe() { public boolean isDisableRememberMe() {
return Jenkins.getInstance().isDisableRememberMe(); return Jenkins.getInstance().isDisableRememberMe();
} }
...@@ -100,6 +107,18 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr ...@@ -100,6 +107,18 @@ public class GlobalSecurityConfiguration extends ManagementLink implements Descr
} catch (IOException e) { } catch (IOException e) {
throw new hudson.model.Descriptor.FormException(e, "slaveAgentPortType"); 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 { } else {
j.disableSecurity(); j.disableSecurity();
} }
......
...@@ -21,6 +21,24 @@ import java.net.Socket; ...@@ -21,6 +21,24 @@ import java.net.Socket;
* @see TcpSlaveAgentListener * @see TcpSlaveAgentListener
*/ */
public abstract class AgentProtocol implements ExtensionPoint { 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. * Protocol name.
* *
......
...@@ -50,6 +50,7 @@ import hudson.Plugin; ...@@ -50,6 +50,7 @@ import hudson.Plugin;
import hudson.PluginManager; import hudson.PluginManager;
import hudson.PluginWrapper; import hudson.PluginWrapper;
import hudson.ProxyConfiguration; import hudson.ProxyConfiguration;
import jenkins.AgentProtocol;
import jenkins.util.SystemProperties; import jenkins.util.SystemProperties;
import hudson.TcpSlaveAgentListener; import hudson.TcpSlaveAgentListener;
import hudson.UDPBroadcastThread; import hudson.UDPBroadcastThread;
...@@ -224,6 +225,7 @@ import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken; ...@@ -224,6 +225,7 @@ import org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;
import org.acegisecurity.ui.AbstractProcessingFilter; import org.acegisecurity.ui.AbstractProcessingFilter;
import org.apache.commons.jelly.JellyException; import org.apache.commons.jelly.JellyException;
import org.apache.commons.jelly.Script; import org.apache.commons.jelly.Script;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.LogFactory; import org.apache.commons.logging.LogFactory;
import org.jvnet.hudson.reactor.Executable; import org.jvnet.hudson.reactor.Executable;
import org.jvnet.hudson.reactor.Milestone; import org.jvnet.hudson.reactor.Milestone;
...@@ -598,6 +600,28 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve ...@@ -598,6 +600,28 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
*/ */
private int slaveAgentPort = SystemProperties.getInteger(Jenkins.class.getName()+".slaveAgentPort",0); 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}. * Whitespace-separated labels assigned to the master as a {@link Node}.
*/ */
...@@ -1060,6 +1084,65 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve ...@@ -1060,6 +1084,65 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
launchTcpSlaveAgentListener(); 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 { private void launchTcpSlaveAgentListener() throws IOException {
synchronized(tcpSlaveAgentListenerLock) { synchronized(tcpSlaveAgentListenerLock) {
// shutdown previous agent if the port has changed // shutdown previous agent if the port has changed
......
...@@ -23,7 +23,7 @@ THE SOFTWARE. ...@@ -23,7 +23,7 @@ THE SOFTWARE.
--> -->
<?jelly escape-by-default='true'?> <?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"/> <st:contentType value="text/plain;charset=UTF-8"/>
<!-- <!--
Publicize the TCP port number for JNLP agents so that they know where to conenct. 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 ...@@ -3,6 +3,7 @@ package hudson.security.GlobalSecurityConfiguration
import hudson.security.SecurityRealm import hudson.security.SecurityRealm
import hudson.markup.MarkupFormatterDescriptor import hudson.markup.MarkupFormatterDescriptor
import hudson.security.AuthorizationStrategy import hudson.security.AuthorizationStrategy
import jenkins.AgentProtocol
import jenkins.model.GlobalConfiguration import jenkins.model.GlobalConfiguration
import hudson.Functions import hudson.Functions
import hudson.model.Descriptor import hudson.model.Descriptor
...@@ -28,6 +29,20 @@ l.layout(norefresh:true, permission:app.ADMINISTER, title:my.displayName, csscla ...@@ -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.entry (title:_("TCP port for JNLP agents"), field:"slaveAgentPort") {
f.serverTcpPort() 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.entry (title:_("Disable remember me"), field: "disableRememberMe") {
f.checkbox() f.checkbox()
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册