From b524f113dcc0b43362c62dc5a666bb9ab3892e2c Mon Sep 17 00:00:00 2001 From: Bandie Canis Date: Sat, 30 Sep 2017 14:51:23 +0200 Subject: [PATCH] There it is. --- 1_generateCA.sh | 43 ++++++++++++++ 2_generateServCert.sh | 73 +++++++++++++++++++++++ 3_generateClientCert.sh | 73 +++++++++++++++++++++++ README.md | 23 ++++++++ ding_client | 82 ++++++++++++++++++++++++++ ding_client.cfg | 10 ++++ ding_server | 127 ++++++++++++++++++++++++++++++++++++++++ ding_server.cfg | 19 ++++++ 8 files changed, 450 insertions(+) create mode 100755 1_generateCA.sh create mode 100755 2_generateServCert.sh create mode 100755 3_generateClientCert.sh create mode 100644 README.md create mode 100755 ding_client create mode 100644 ding_client.cfg create mode 100755 ding_server create mode 100644 ding_server.cfg diff --git a/1_generateCA.sh b/1_generateCA.sh new file mode 100755 index 0000000..2a4c634 --- /dev/null +++ b/1_generateCA.sh @@ -0,0 +1,43 @@ +#!/bin/bash +echo "###################" +echo "# CA generation #" +echo -e "###################\n" + +## CA Private key +echo -n "Where to save your CA key file? ($PWD/CA.key): " +read temp + +if [ -n "$temp" ] +then + save=$temp +else + save="$PWD/CA.key" +fi +CAkey=$save +openssl genrsa -aes256 -out $save 8192 + + +## CA Certificate +echo -n "Where to save your CA certificate? ($PWD/CA.crt): " +read temp + +if [ -n "$temp" ] +then + save=$temp +else + save="$PWD/CA.crt" +fi + +echo -n "How many days should the certificate be valid? (3650): " +read temp + +if [[ $temp =~ ^[0-9]+$ ]] +then + days=$temp +else + days=3650 +fi +echo -e "\033[01;33mPlease enter some information about the CA.\033[00m" +openssl req -new -x509 -days $days -key $CAkey -out $save + + diff --git a/2_generateServCert.sh b/2_generateServCert.sh new file mode 100755 index 0000000..705c079 --- /dev/null +++ b/2_generateServCert.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +## Server private key +echo -n "Where to save your server's key file? ($PWD/ding_server.key): " +read temp + +if [ -n "$temp" ] +then + save=$temp +else + save="$PWD/ding_server.key" +fi +key=$save +openssl genrsa -out $save 4096 + + +## CSR +echo -n "Where to save your Certificate Signing Request (CSR)? ($PWD/ding_server.csr): " +read temp + +if [ -n "$temp" ] +then + save=$temp +else + save="$PWD/ding_server.csr" +fi +csr=$save +echo -e "\033[01;33mPlease enter some information. THEY MUST BE DIFFERENT FROM THE CA's INFORMATION.\033[00m" +openssl req -new -key $key -out $save -sha512 + + +## Signing +echo -n "Path of your CA Certificate? ($PWD/CA.crt): " +read temp + +if [ -n "$temp" ] +then + loadCAcrt=$temp +else + loadCAcrt="$PWD/CA.crt" +fi + +echo -n "Path of your CA key? ($PWD/CA.key): " +read temp + +if [ -n "$temp" ] +then + loadCAkey=$temp +else + loadCAkey="$PWD/CA.key" +fi + +echo -n "Where to save your signed server certificate? ($PWD/ding_server.crt): " +read temp + +if [ -n "$temp" ] +then + save=$temp +else + save="$PWD/ding_server.crt" +fi + +echo -n "How many days should the certificate be valid? (365): " +read temp + +if [ -n "$temp" ] +then + t=$temp +else + t=365 +fi +openssl x509 -req -in $csr -CA $loadCAcrt -CAkey $loadCAkey -CAcreateserial -out $save -days $t -sha512 +rm $csr diff --git a/3_generateClientCert.sh b/3_generateClientCert.sh new file mode 100755 index 0000000..052ece6 --- /dev/null +++ b/3_generateClientCert.sh @@ -0,0 +1,73 @@ +#!/bin/bash + +## Server private key +echo -n "Where to save your client's key file? ($PWD/ding_client.key): " +read temp + +if [ -n "$temp" ] +then + save=$temp +else + save="$PWD/ding_client.key" +fi +key=$save +openssl genrsa -out $save 4096 + + +## CSR +echo -n "Where to save your Certificate Signing Request (CSR)? ($PWD/ding_client.csr): " +read temp + +if [ -n "$temp" ] +then + save=$temp +else + save="$PWD/ding_client.csr" +fi +csr=$save +echo -e "\033[01;33mPlease enter some information. THEY MUST BE DIFFERENT FROM THE CA's AND SERVER's INFORMATION.\033[00m" +openssl req -new -key $key -out $save -sha512 + + +## Signing +echo -n "Path of your CA Certificate? ($PWD/CA.crt): " +read temp + +if [ -n "$temp" ] +then + loadCAcrt=$temp +else + loadCAcrt="$PWD/CA.crt" +fi + +echo -n "Path of your CA key? ($PWD/CA.key): " +read temp + +if [ -n "$temp" ] +then + loadCAkey=$temp +else + loadCAkey="$PWD/CA.key" +fi + +echo -n "Where to save your signed client certificate? ($PWD/ding_client.crt): " +read temp + +if [ -n "$temp" ] +then + save=$temp +else + save="$PWD/ding_client.crt" +fi + +echo -n "How many days should the certificate be valid? (365): " +read temp + +if [ -n "$temp" ] +then + t=$temp +else + t=365 +fi +openssl x509 -req -in $csr -CA $loadCAcrt -CAkey $loadCAkey -CAcreateserial -out $save -days $t -sha512 +rm $csr diff --git a/README.md b/README.md new file mode 100644 index 0000000..5f771f9 --- /dev/null +++ b/README.md @@ -0,0 +1,23 @@ +# ding +## What is ding? +ding is a client-server thing written in python. Its aim is to execute a set of commands remotely which can be set in the server's config file. + +## How does it work? +The server awaits the client's commands. A command sent by the client must be defined in the server's config file, else the server won't do anything. + +## What about ding's security? +The authentication is done by an SSL Client Certificate signed by an (own generated) Certificate Authority. The scripts for generating a CA and signing a Server/Client Certificate are also in here to make it (relatively) easy. [ You need only to press enter in the most cases, type in some certificate information and entering a previously defined CA password. ] + + +## Installation +In all steps please read carefully, what the generating script want from you. + +1. Run `./1_generateCA.sh` to generate a CA. +2. Run `./2_generateServCert.sh` to generate a signed Server Certificate. +3. Run `./3_generateClientCert.sh` to generate a signed Client Certificate. +4. Move ding\_client, ding\_client.cfg, ding\_client.crt, ding\_client.key to the computer which should be able to send commands to the server. +5. Do some configuration on the server and client (ding\_server.cfg, ding\_client.cfg). +6. Start the server using `./ding_server`. You may want to put this in a tmux session ([Ctrl+B, D] ;) ). +7. Try out the client using `./ding_client `. + + diff --git a/ding_client b/ding_client new file mode 100755 index 0000000..67db8ec --- /dev/null +++ b/ding_client @@ -0,0 +1,82 @@ +#!/usr/bin/env python3 +import sys, ssl, socket +import configparser + +CONFIG = "ding_client.cfg" + +def readConfig(): + cfg = configparser.SafeConfigParser() + try: + cfg.read(CONFIG) + + global host, port, cafile, certfile, keyfile + + host = cfg.get("Client", "host") + port = int(cfg.get("Client", "port")) + + cafile = cfg.get("Client", "cafile") + certfile = cfg.get("Client", "certfile") + keyfile = cfg.get("Client", "keyfile") + except configparser.NoSectionError: + print("No suitable config found. Expecting some config in", CONFIG) + quit(3) + + +def send(conn, cmd): + + global exitcode + + conn.connect((host, port)) + buf = conn.recv(1024) + if(buf == b"OK 1337\n"): + conn.sendall(cmd) + buf = conn.recv(1024) + if(buf == b"OK CMD"): + exitcode = 0 + elif(buf == b"ERR NO_CMD"): + print("Error. Server said: The command doesn't exist/isn't set.", file=sys.stderr) + exitcode = 1 + elif(buf == b"ERR CMD_ERR"): + print("Error. Server said: The command doesn't work because the file doesn't exist on the server.") + exitcode = 2 + + else: + conn.sendall(b"NO.") + print("The server seems to be crazy. Nothing sent.", file=sys.stderr) + + conn.close() + + + +def main(): + try: + context = ssl.SSLContext(ssl.PROTOCOL_TLS) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(cafile) + context.load_cert_chain(certfile=certfile, keyfile=keyfile) + except FileNotFoundError as e: + print(e) + print("Please check your paths in the config file. (Have you forgotten to generate the Certificates?)", sep="", file=sys.stderr) + quit(2) + + conn = context.wrap_socket(socket.socket()) + + + try: + send(conn, bytes(sys.argv[1], sys.stdin.encoding)) + except IndexError: + print(sys.argv[0], ": Missing argument.\nSyntax: ", sys.argv[0], " ", file=sys.stderr) + except ConnectionRefusedError: + print("Connection refused.", file=sys.stderr) + quit(1) + except ssl.SSLError: + print("Wrong certificate.", file=sys.stderr); + quit(3) + + + +if(__name__ == "__main__"): + readConfig() + main() + quit(exitcode) + diff --git a/ding_client.cfg b/ding_client.cfg new file mode 100644 index 0000000..04efb5b --- /dev/null +++ b/ding_client.cfg @@ -0,0 +1,10 @@ +[Client] +host=localhost +port=13573 + + +cafile=CA.crt +#Client Certificate/key signed by the CA above +certfile=ding_client.crt +keyfile=ding_client.key + diff --git a/ding_server b/ding_server new file mode 100755 index 0000000..c53b84d --- /dev/null +++ b/ding_server @@ -0,0 +1,127 @@ +#!/usr/bin/env python3 +import ssl, socket, subprocess, time +import configparser + +CONFIG = "ding_server.cfg" + +def getTimestamp(): + t = "[" + time.strftime("%Y-%m-%d %H:%M:%S") + "]" + return t + +def execFromConfig(option): + cfg = configparser.SafeConfigParser() + cfg.read(CONFIG) + + try: + cmd = cfg.get("Commands", option).replace("\"", "").replace("\'", "") + cmd = cmd.split(" ") + try: + subprocess.Popen(cmd) + return 0 + + except FileNotFoundError: + print(getTimestamp(), "Can't execute", cmd, ". File not found.") + return 2 + + except configparser.NoOptionError: + print(getTimestamp(), "No execution set:", option) + return 1 + + + +def main(): + + while True: + newsocket, fromaddr = bindsocket.accept() + try: + connstream = context.wrap_socket(newsocket, server_side=True) + print(getTimestamp(), "Incoming connection:", fromaddr[0]) + connstream.send(b"OK 1337\n") + + con_loop = True + while con_loop: + try: + buf = connstream.recv(1024) + if not buf: break + buf = buf.decode("utf-8").upper() + except ssl.SSLEOFError: + print(getTimestamp(), "SSL-EOF-Error.") + con_loop = False + except ConnectionResetError: + print(getTimestamp(), "Connection reset.") + serve() + + print(getTimestamp(), " ", fromaddr[0], ": ", buf, sep="") + + retval = execFromConfig(buf) + if(retval == 0): + connstream.send(b"OK CMD") + elif(retval == 1): + connstream.send(b"ERR NO_CMD") + elif(retval == 2): + connstream.send(b"ERR CMD_ERR") + + + except ssl.SSLError as e: + print(getTimestamp(), e) + + except EOFError: + print(getTimestamp(), "EOF") + +def init(): + + global host, port, cafile, certfile, keyfile, context, bindsocket + + cfg = configparser.SafeConfigParser() + cfg.read(CONFIG) + + try: + host=cfg.get("Server", "host").replace("\"","").replace("\'","") + port=int(cfg.get("Server", "port").replace("\"","").replace("\'","")) + cafile=cfg.get("Security", "cafile").replace("\"","").replace("\'","") + certfile=cfg.get("Security", "certfile").replace("\"","").replace("\'","") + keyfile=cfg.get("Security", "keyfile").replace("\"","").replace("\'","") + except configparser.NoOptionError as e: + print("Error in configuration file:", e) + quit(1) + + + try: + context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile=cafile) + context.load_cert_chain(certfile=certfile, keyfile=keyfile) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(cafile=cafile) + + except FileNotFoundError as e: + print(e) + print("Please check your paths in the config file. (Have you forgotten to generate the Certificates?)") + quit(2) + + try: + bindsocket = socket.socket() + bindsocket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) + bindsocket.bind((host, port)) + bindsocket.listen(5) + except socket.gaierror: + print("Error: Hostname error. Name or service not known.") + quit(1) + except PermissionError: + print("Error: Can't bind for port number ", port, ". Permission denied.", sep="") + quit(1) + + print("Running ding server on ", host, ":", port, + "\nCAFile: ", cafile, + "\nCertfile: ", certfile, + "\nKeyfile: ", keyfile, + "\n===========", + sep="") + + +if(__name__ == "__main__"): + + try: + init() + main() + except KeyboardInterrupt: + print("\r\rServer stopped.") + diff --git a/ding_server.cfg b/ding_server.cfg new file mode 100644 index 0000000..9d12b2f --- /dev/null +++ b/ding_server.cfg @@ -0,0 +1,19 @@ +[Server] +host=localhost +port=13573 + +[Security] +# Certificate of the OWN CA +cafile=CA.crt + +# Server's certificate [signed by the CA above] +certfile=ding_server.crt + +# Server's private key +keyfile=ding_server.key + + +[Commands] +# Syntax: +# SERVER_COMMAND: Command --which --should_be --executed +lock: xscreensaver-command -lock