From 23c68886ff5844b089692a2ecf68e6393ba425bc Mon Sep 17 00:00:00 2001 From: Sota Date: Tue, 27 Oct 2015 17:57:03 +0200 Subject: [PATCH 1/3] Test commit --- pom.xml | 9 +- .../sergeev/controlpanel/ssh/SshService.java | 87 +++++++++++++++++++ 2 files changed, 94 insertions(+), 2 deletions(-) create mode 100644 src/main/java/com/sergeev/controlpanel/ssh/SshService.java diff --git a/pom.xml b/pom.xml index d2c3cba..cd32673 100644 --- a/pom.xml +++ b/pom.xml @@ -1,6 +1,6 @@ - 4.0.0 @@ -16,6 +16,11 @@ + + com.hierynomus + sshj + 0.13.0 + org.hibernate diff --git a/src/main/java/com/sergeev/controlpanel/ssh/SshService.java b/src/main/java/com/sergeev/controlpanel/ssh/SshService.java new file mode 100644 index 0000000..20af876 --- /dev/null +++ b/src/main/java/com/sergeev/controlpanel/ssh/SshService.java @@ -0,0 +1,87 @@ +package com.sergeev.controlpanel.ssh; + +import com.sergeev.controlpanel.model.Node; +import net.schmizz.sshj.SSHClient; +import net.schmizz.sshj.common.IOUtils; +import net.schmizz.sshj.connection.channel.direct.Session; +import net.schmizz.sshj.connection.channel.direct.Session.Command; + +import java.io.IOException; +import java.security.*; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.concurrent.TimeUnit; + + +public class SshService { + private static final String KEY_ALIAS = "controlpanel"; + private static final char[] KEY_PASSWORD = "121212".toCharArray(); + + private SSHClient ssh; + private ArrayList sessions; + + // TODO: Handle exceptions; + public SshService() throws Exception { + ssh = new SSHClient(); + ssh.loadKeys(SshService.loadKeyPair()); + } + + + public static String test() + throws IOException { + String res = ""; + final SSHClient ssh = new SSHClient(); + ssh.addHostKeyVerifier("a6:90:99:9c:c5:15:d8:07:b5:fa:c5:79:77:93:9b:b6"); + + ssh.connect("192.168.1.169"); + try { + ssh.authPassword("root", "nQXeRxl2eogCVPYEQsUD"); + final Session session = ssh.startSession(); + try { + final Command cmd = session.exec("ping -c 1 google.com"); + res += IOUtils.readFully(cmd.getInputStream()).toString(); + cmd.join(5, TimeUnit.SECONDS); + res += "\n** exit status: " + cmd.getExitStatus(); + } finally { + session.close(); + } + } finally { + ssh.disconnect(); + } + + return res; + } + + + // TODO: Handle exceptions + // TODO: Should be private? + private static KeyPair loadKeyPair() throws Exception { + KeyStore keystore = KeyStore.getInstance("jks"); + keystore.load(SshService.class.getResourceAsStream("/resources/controlpanel.jks"), KEY_PASSWORD); + Key key = keystore.getKey(KEY_ALIAS, KEY_PASSWORD); + + if (key instanceof PrivateKey) { + Certificate cert = keystore.getCertificate(KEY_ALIAS); + PublicKey publicKey = cert.getPublicKey(); + return new KeyPair(publicKey, (PrivateKey) key); + } else { + // TODO: Throw exception + throw new Exception(); + } + } + + public int connectToNode(Node node) throws Exception { + //ssh.addHostKeyVerifier(node.getSshId()); + ssh.connect(node.getInetAddress()); + ssh.authPublickey("root"); + + Session session = ssh.startSession(); + sessions.add(session); + + // It's possible to just get size of the array, but that might result race conditions + // Giving away Session is a bad idea, as that brakes OOP principles + return sessions.lastIndexOf(session); + } + + +} \ No newline at end of file From 392e983db4656b32411f9ebf23801dfe96a9b5ba Mon Sep 17 00:00:00 2001 From: Sota Date: Tue, 27 Oct 2015 17:59:44 +0200 Subject: [PATCH 2/3] Added JKS file --- src/main/resources/controlpanel.jks | Bin 0 -> 1302 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 src/main/resources/controlpanel.jks diff --git a/src/main/resources/controlpanel.jks b/src/main/resources/controlpanel.jks new file mode 100644 index 0000000000000000000000000000000000000000..abfee5b5c211456397089f663444aeeefeade1e9 GIT binary patch literal 1302 zcmezO_TO6u1_mY|W&~rN-EYvu*loba#-+{1$ik?_ zB*@6f%D~dZSpLOm{;r8<>X=Qmp0Q1R|24!;vi!%}jrET6pIY8wc=fD0W^HxVKY`uX z&G+bTOTQl-R%X6?^_`xZm%JKgJhKg$7PeyHhVD--*PeHsotCHh_#tP_wsS`dPrVV9 zy>T~E*zfN9vyXjtJu*9e!om2#+n4%ni?+1u1O{j;?&{}RdgL%)_NwJm1@`_r|6K7| zZ=w8&N|z2Lwr7jpYz$bjcTUTNrD2(?*BNR0>@>P?#_%D3ZsS$K=&u%&OT~nR-f%9z zFuV6bxTmlEh1K$b=bt=SKk2Q|xpM_Gew6Ir==|^j!_w#mvguX?w-LJ zH~XfZ;@kyNoDZEh3s`1UHKqOgqipp4^}3@on%3}Ljj^gyFm7U>;j#Vm&m;49zVl-f z2n(IXVDE79mQ7FC29v4VWgoDvbXwB7R`WLhq3N;;@!Q|O(~o7?H`)5{#FR3gqLpep z@@}zytADZB+Kxy0aO>I+OO+SAJ~z$%TAjw^m{fzk3r_m~`e*P|-|nX%II>tH^h^yb z85o$Y44RnD44RleFJNY3WMX2`HJ|Biz|F?4)#h=|f`yrt!63&_02q7Bp)72|?4fzt zdHLmeFcA(65q1m_E({TF3=w9yh=H6quOZN7Mn;AfhL(nwQ6R1%kO$?`*)>g!JAkQI z2NoQRK#n|9VldV&3hMy!*ccw+XB@xU1J@T0)I!^ z_D1~LfAs4vgKwv2hvr;wv~G|!$^Vp=uswTbz|tl2=f7a77En6mzkB8Nf7_2q|F8~! zer&}uQGS-+&EGB`l{?I28X_DeHqq!?>F0IYiPyE>e^O$WoO{uO@i>zx!}NrJeohdTqAtz0-SE$C&L1-&r|j{qA2@ubO^_`*7Q`H>C$g zhWG8d66yb}kX@j$VT-3&q4EnK4Kd;E=Q)jfPAq$9BUaTNQq!yPVDUOR?aj+dC+01# z=l-Q=n)LB^QWc|$^_e9f_pJz=(7B@d{==xJ^VueKr^Pi?hpMJ9`dI%v@2SPy*vbHM zM=Qg{pVp5pk0rj4lwymS6C}E+sz3GO3f=>!UWTSraom4>$M}=>lFYbE3-9))9=?A2 z%Ueb(&%aTNk`H>+sW`?-9uNvko~*mCFKxjb2%`3dlGj6L(9(cvu^|XGPPs8t* z|JHneP;`o!JASoQ)amofXB>Wbu>0t-z8Rkm$9{L zYjZdz>KK0(dTCp{^wSFilyuH)z+j*Y4A&_FX|t{LXS(yH@?TI_eX?X}U->#F5niFv rZ*%)5F3QhMee;onrM5gVI{56350j4F*?8jDVsoq7y!&CXUYWlE0Sge9 literal 0 HcmV?d00001 From 83ea6bc3e6e630acb1d038a765f78c8e960db7af Mon Sep 17 00:00:00 2001 From: Sota Date: Sun, 13 Dec 2015 19:25:11 +0200 Subject: [PATCH 3/3] SshService is done. Added sshPublicKey to Node class. Fixed dependency issue with slf4j --- pom.xml | 2 +- .../com/sergeev/controlpanel/model/Node.java | 32 +++-- .../sergeev/controlpanel/ssh/SshService.java | 125 +++++++++++++----- .../com/sergeev/controlpanel/utils/Utils.java | 48 +++++-- src/main/resources/controlpanel.jks | Bin 1302 -> 0 bytes src/main/resources/cp.der | Bin 0 -> 294 bytes src/main/resources/cp.id_rsa.pub | 1 + src/main/resources/cp.pem | 27 ++++ 8 files changed, 179 insertions(+), 56 deletions(-) delete mode 100644 src/main/resources/controlpanel.jks create mode 100644 src/main/resources/cp.der create mode 100644 src/main/resources/cp.id_rsa.pub create mode 100644 src/main/resources/cp.pem diff --git a/pom.xml b/pom.xml index 59115c6..97ae62c 100644 --- a/pom.xml +++ b/pom.xml @@ -100,7 +100,7 @@ org.slf4j slf4j-log4j12 - 1.5.6 + 1.6.2 com.google.code.gson diff --git a/src/main/java/com/sergeev/controlpanel/model/Node.java b/src/main/java/com/sergeev/controlpanel/model/Node.java index 1c20036..73279ab 100644 --- a/src/main/java/com/sergeev/controlpanel/model/Node.java +++ b/src/main/java/com/sergeev/controlpanel/model/Node.java @@ -1,16 +1,12 @@ package com.sergeev.controlpanel.model; -import com.google.gson.Gson; -import com.google.gson.JsonArray; -import com.google.gson.JsonObject; import com.sergeev.controlpanel.model.user.User; import com.sun.istack.internal.NotNull; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; import javax.persistence.*; import java.net.InetAddress; -import java.util.*; +import java.util.HashSet; +import java.util.Set; /** * Created by dmitry-sergeev on 22.09.15. @@ -35,6 +31,9 @@ public class Node extends AbstractModel{ @Column(name = "osVersion") private String osVersion; + @Column(name = "publicKey", unique = true, nullable = false) + private String publicKey; // Looks like "a6:90:99:9c:c5:15:d8:07:b5:fa:c5:79:77:93:9b:b6" + @ElementCollection(targetClass = Component.class) private Set components = null; @@ -44,12 +43,21 @@ public class Node extends AbstractModel{ public Node() { } - public Node(String name, InetAddress inetAddress, String osName, String osVersion) { + public Node(String name, InetAddress inetAddress, String osName, String osVersion, String publicKey) { this.name = name; this.inetAddress = inetAddress; this.osName = osName; this.osVersion = osVersion; users = new HashSet<>(); + this.publicKey = publicKey; + } + + public String getPublicKey() { + return publicKey; + } + + public void setPublicKey(String publicKey) { + this.publicKey = publicKey; } public long getId() { @@ -92,15 +100,15 @@ public void setOsVersion(String osVersion) { this.osVersion = osVersion; } - public void setComponents(Set components) { - this.components = components; - } - @OneToMany(mappedBy = "node", cascade = CascadeType.ALL) - public Set getComponents(){ + public Set getComponents() { return components; } + public void setComponents(Set components) { + this.components = components; + } + public Set addComponent(Component component){ components.add(component); component.setNode(this); diff --git a/src/main/java/com/sergeev/controlpanel/ssh/SshService.java b/src/main/java/com/sergeev/controlpanel/ssh/SshService.java index 20af876..77d22e2 100644 --- a/src/main/java/com/sergeev/controlpanel/ssh/SshService.java +++ b/src/main/java/com/sergeev/controlpanel/ssh/SshService.java @@ -3,42 +3,115 @@ import com.sergeev.controlpanel.model.Node; import net.schmizz.sshj.SSHClient; import net.schmizz.sshj.common.IOUtils; +import net.schmizz.sshj.connection.ConnectionException; import net.schmizz.sshj.connection.channel.direct.Session; import net.schmizz.sshj.connection.channel.direct.Session.Command; +import net.schmizz.sshj.transport.TransportException; +import net.schmizz.sshj.userauth.UserAuthException; +import java.io.File; import java.io.IOException; -import java.security.*; -import java.security.cert.Certificate; -import java.util.ArrayList; import java.util.concurrent.TimeUnit; public class SshService { - private static final String KEY_ALIAS = "controlpanel"; - private static final char[] KEY_PASSWORD = "121212".toCharArray(); + private static final String KEY_PATH; - private SSHClient ssh; - private ArrayList sessions; + static { + KEY_PATH = (new File("src/main/resources/cp.pem")).getAbsolutePath(); + } + + private final SSHClient ssh; + private Node node; + private Session session; - // TODO: Handle exceptions; - public SshService() throws Exception { + public SshService(Node node) { ssh = new SSHClient(); - ssh.loadKeys(SshService.loadKeyPair()); + this.node = node; + } + + public void updatePublicKey() { + ssh.addHostKeyVerifier(node.getPublicKey()); + } + + private void connect() throws IOException { + ssh.connect(node.getInetAddress()); + } + + private void disconnect() throws IOException { + ssh.disconnect(); + } + + private void logIn() throws UserAuthException, TransportException { + ssh.authPublickey("root", KEY_PATH); + } + + private void logIn(String password) throws UserAuthException, TransportException { + ssh.authPassword("root", password); + } + + private void startSession() throws ConnectionException, TransportException { + session = ssh.startSession(); + } + + private void stopSession() throws ConnectionException, TransportException { + session.close(); } + /* + String["Result", "Exit status"] + */ + private String[] executeCommand(String stringCommand) throws IOException { + String[] response = new String[2]; + final Command cmd = session.exec(stringCommand); + response[0] = IOUtils.readFully(cmd.getInputStream()).toString(); + cmd.join(5, TimeUnit.SECONDS); + response[1] = cmd.getExitStatus().toString(); + + return response; + } - public static String test() - throws IOException { + public String[] sendCommand(String cmd, String password) throws IOException { + this.connect(); + try { + if (password.isEmpty()) { + this.logIn(); + } else { + this.logIn(password); + } + this.startSession(); + try { + return this.executeCommand(cmd); + } finally { + this.stopSession(); + } + } finally { + this.disconnect(); + } + } + + public void initializeNode(String password) throws IOException { + // TODO: Read from resources/cp.id_rsa.pub + this.sendCommand( + "echo \"ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDYCj4uihLqTI2c+A++p0tXwWCnIh3F8m2OD7h4OtJdoF8Q4Lz3/Ziq0X+BuZ2QRmVXeJkBQMT/z8E1iOktSRGYgZrVqGDdOsAiu8g6PTbnE3BSsqa5pUJ4mdZ6xc5l0xRrGb05TN8qw/OtYbcnC0E7ya+sM1JXJBG5Xosz+QrRanTJZrtBXjjWD82yJAuvypX4g3tbl156ZZKN8PIYRVFEMvzxwz36kKLfCagfgFFfDnG+38rLPqDbKTvx5NspSupdmis2I8tg5FfKTxX8RygrTEzjPoxRVLGSoYiYEDw6V9a3COE9sxjXyGT38fqFQUqIPGNmbBFYY7IYg82IFWhf ControlPanel\" >> ~/.ssh/authorized_keys", + password); + } + + /* + public static void main(String[] args) + throws IOException, Exception { String res = ""; final SSHClient ssh = new SSHClient(); ssh.addHostKeyVerifier("a6:90:99:9c:c5:15:d8:07:b5:fa:c5:79:77:93:9b:b6"); ssh.connect("192.168.1.169"); try { - ssh.authPassword("root", "nQXeRxl2eogCVPYEQsUD"); + //ssh.authPassword("root", ""); + File file = new File("src/main/resources/cp.pem"); + ssh.authPublickey("root", file.getAbsolutePath()); final Session session = ssh.startSession(); try { - final Command cmd = session.exec("ping -c 1 google.com"); + final Command cmd = session.exec("uname -a"); res += IOUtils.readFully(cmd.getInputStream()).toString(); cmd.join(5, TimeUnit.SECONDS); res += "\n** exit status: " + cmd.getExitStatus(); @@ -49,27 +122,11 @@ public static String test() ssh.disconnect(); } - return res; - } - - // TODO: Handle exceptions - // TODO: Should be private? - private static KeyPair loadKeyPair() throws Exception { - KeyStore keystore = KeyStore.getInstance("jks"); - keystore.load(SshService.class.getResourceAsStream("/resources/controlpanel.jks"), KEY_PASSWORD); - Key key = keystore.getKey(KEY_ALIAS, KEY_PASSWORD); - - if (key instanceof PrivateKey) { - Certificate cert = keystore.getCertificate(KEY_ALIAS); - PublicKey publicKey = cert.getPublicKey(); - return new KeyPair(publicKey, (PrivateKey) key); - } else { - // TODO: Throw exception - throw new Exception(); - } - } + System.out.println(res); + }*/ + /* public int connectToNode(Node node) throws Exception { //ssh.addHostKeyVerifier(node.getSshId()); ssh.connect(node.getInetAddress()); @@ -82,6 +139,6 @@ public int connectToNode(Node node) throws Exception { // Giving away Session is a bad idea, as that brakes OOP principles return sessions.lastIndexOf(session); } - +*/ } \ No newline at end of file diff --git a/src/main/java/com/sergeev/controlpanel/utils/Utils.java b/src/main/java/com/sergeev/controlpanel/utils/Utils.java index aafa17f..86c55c2 100644 --- a/src/main/java/com/sergeev/controlpanel/utils/Utils.java +++ b/src/main/java/com/sergeev/controlpanel/utils/Utils.java @@ -1,19 +1,10 @@ package com.sergeev.controlpanel.utils; -import com.google.gson.Gson; import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.sergeev.controlpanel.controller.MainController; -import com.sergeev.controlpanel.model.AbstractModel; -import org.json.simple.JSONArray; -import org.json.simple.JSONObject; -import org.json.simple.parser.JSONParser; import org.slf4j.Logger; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import java.util.*; - /** * Created by dmitry-sergeev on 22.09.15. */ @@ -26,5 +17,44 @@ public static ResponseEntity status(int status){ jsonObject.addProperty("status", HttpStatus.valueOf(status).name()); return new ResponseEntity<>(jsonObject.toString(), HttpStatus.valueOf(status)); } +/* + public static KeyPair readKeypair(final InputStream is, final char[] password) throws IOException { + PasswordFinder passwordFinder = password != null ? new StaticPasswordFinder(password) : null; + + KeyPair kp = null; + try { + // read the stream as a PEM encoded + try { + + final PemReader pem = new PemReader(new InputStreamReader(is), passwordFinder); + try { + // Skip over entries in the file which are not KeyPairs + do { + final Object o = pem.readObject(); + if (o == null) + break; // at end of file + else if (o instanceof KeyPair) + kp = (KeyPair) o; + } while (kp == null); + } + finally { + pem.close(); + } + } + catch (EncryptionException e) { + throw new IOException("Error reading PEM stream: " + e.getMessage(), e); + } + } + finally { + is.close(); + } + + // Cast the return to a KeyPair (or, if there is no [valid] return, throw an exception) + if (kp != null) + return kp; + else + throw new IOException("Stream " + is + " did not contain a PKCS8 KeyPair"); + } +*/ } diff --git a/src/main/resources/controlpanel.jks b/src/main/resources/controlpanel.jks deleted file mode 100644 index abfee5b5c211456397089f663444aeeefeade1e9..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1302 zcmezO_TO6u1_mY|W&~rN-EYvu*loba#-+{1$ik?_ zB*@6f%D~dZSpLOm{;r8<>X=Qmp0Q1R|24!;vi!%}jrET6pIY8wc=fD0W^HxVKY`uX z&G+bTOTQl-R%X6?^_`xZm%JKgJhKg$7PeyHhVD--*PeHsotCHh_#tP_wsS`dPrVV9 zy>T~E*zfN9vyXjtJu*9e!om2#+n4%ni?+1u1O{j;?&{}RdgL%)_NwJm1@`_r|6K7| zZ=w8&N|z2Lwr7jpYz$bjcTUTNrD2(?*BNR0>@>P?#_%D3ZsS$K=&u%&OT~nR-f%9z zFuV6bxTmlEh1K$b=bt=SKk2Q|xpM_Gew6Ir==|^j!_w#mvguX?w-LJ zH~XfZ;@kyNoDZEh3s`1UHKqOgqipp4^}3@on%3}Ljj^gyFm7U>;j#Vm&m;49zVl-f z2n(IXVDE79mQ7FC29v4VWgoDvbXwB7R`WLhq3N;;@!Q|O(~o7?H`)5{#FR3gqLpep z@@}zytADZB+Kxy0aO>I+OO+SAJ~z$%TAjw^m{fzk3r_m~`e*P|-|nX%II>tH^h^yb z85o$Y44RnD44RleFJNY3WMX2`HJ|Biz|F?4)#h=|f`yrt!63&_02q7Bp)72|?4fzt zdHLmeFcA(65q1m_E({TF3=w9yh=H6quOZN7Mn;AfhL(nwQ6R1%kO$?`*)>g!JAkQI z2NoQRK#n|9VldV&3hMy!*ccw+XB@xU1J@T0)I!^ z_D1~LfAs4vgKwv2hvr;wv~G|!$^Vp=uswTbz|tl2=f7a77En6mzkB8Nf7_2q|F8~! zer&}uQGS-+&EGB`l{?I28X_DeHqq!?>F0IYiPyE>e^O$WoO{uO@i>zx!}NrJeohdTqAtz0-SE$C&L1-&r|j{qA2@ubO^_`*7Q`H>C$g zhWG8d66yb}kX@j$VT-3&q4EnK4Kd;E=Q)jfPAq$9BUaTNQq!yPVDUOR?aj+dC+01# z=l-Q=n)LB^QWc|$^_e9f_pJz=(7B@d{==xJ^VueKr^Pi?hpMJ9`dI%v@2SPy*vbHM zM=Qg{pVp5pk0rj4lwymS6C}E+sz3GO3f=>!UWTSraom4>$M}=>lFYbE3-9))9=?A2 z%Ueb(&%aTNk`H>+sW`?-9uNvko~*mCFKxjb2%`3dlGj6L(9(cvu^|XGPPs8t* z|JHneP;`o!JASoQ)amofXB>Wbu>0t-z8Rkm$9{L zYjZdz>KK0(dTCp{^wSFilyuH)z+j*Y4A&_FX|t{LXS(yH@?TI_eX?X}U->#F5niFv rZ*%)5F3QhMee;onrM5gVI{56350j4F*?8jDVsoq7y!&CXUYWlE0Sge9 diff --git a/src/main/resources/cp.der b/src/main/resources/cp.der new file mode 100644 index 0000000000000000000000000000000000000000..5a7f35197d550510611bc9fda6a8c374b37b06b7 GIT binary patch literal 294 zcmV+>0ondAf&n5h4F(A+hDe6@4FLfG1potr0S^E$f&mHwf&l>l*a|)_iW2Hfjhy%o zzNbr9!CvBD=^sJvQePa8k0SxursQnbvy6&SldSYZ<*cOy4TQ^Q~dGCksJ4$*-(4 zQdcAqxn7Gi`3li$bjfDBL0&l456!Y93$MzR_=9^}mtJ~hl8x~47)4P;GW_wwJ^GNM z-wCK6fKgu#alYTm%RZpnDLe7x+bK%wU79O4Bg