Question .NET 4.8 C#. DockerDotNet SSL

Joined
Nov 17, 2021
Messages
5
Programming Experience
Beginner
DISCLAIMER: I am not a full-time dev, I'm likely to say/do stupid things in code. So bear with me.

So I've been working on Integrating the DockerDotNet library into a project, initially, I did it all over ssh, just connect run the commands, and be done, but now I want to exploit some other features I'm stuck.

I followed this guide to generating the certs Protect the Docker daemon socket

And after a considerable amount of google and other things, I got the certs Generated, Installed, and WORKING on the localhost, okay so it's not perfect but it all works up to here, and here I'm stuck on

eRROR:
I | http: TLS handshake error from 192.168.0.50:60716: tls: failed to verify client's certificate: x509: certificate signed by unknown authority (possibly because of "crypto/rsa: verification error" while trying to verify candidate authority certificate "MYDOMAIN.local")

Can someone point me in the right direction? If you need to see what I call the code that generates the certificates I don't mind sharing it.

Thanks
 
Joined
Nov 17, 2021
Messages
5
Programming Experience
Beginner
Code to generate certs, forgive the mess:
internal class InstallManager
    {
        #region Return Models
        internal class InstallManagerResult
        {
            public bool Success { get; set; }
            public string Error { get; set; }
            public string Message { get; set; }
        }
        #endregion Return Models

        internal InstallManagerResult SetupNewServer(Servers servers)
        {
            InstallManagerResult result = new InstallManagerResult();
            using (ApplicationDbContext db = new ApplicationDbContext())
            {
                // Check the username, if its blank assume root
                if (string.IsNullOrEmpty(servers.SSHUser))
                {
                    servers.SSHUser = "root";
                }

                try
                {
                    // Construct the ssh session.
                    using (SshClient ssh = new SshClient(servers.Address, servers.SSHUser, servers.SSHPassword))
                    {

                        // Connect to the server
                        ssh.Connect();
                        if (!ssh.IsConnected)
                        {
                            throw new Exception("The host connection failed. Check the server details and try again");
                        }

                        // Update system and install docker.io
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " apt-get update -y");
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " apt-get install -y docker.io");
                        // System is updated and docker is installed - It may or may not have started already but
                        // dont get hung on that right now, its present state is not relevant

                        // Cleanup APT
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " apt-get clean -y");

                        // Configure DOCKER for SSL

                        // NOTE: Since all certificates are generated on the command line we have to pass passwords to and from make sure you
                        // clear the historys at the end.


                        // The SSL Certs are the be all and end all, you should guard them
                        ssh.RunCommand("openssl genrsa -aes256 -passout pass:" + servers.SSHPassword + " -out ca-key.pem 4096");
                        ssh.RunCommand(@"openssl req -new -x509 -days 365 -key ca-key.pem -sha256 -passin pass:" + servers.SSHPassword + @" -out ca.pem -subj ""/C=GB/ST=London/L=London/O=Global Security/OU=IT Department/CN=domain.local"" ");
                        ssh.RunCommand("openssl genrsa -out server-key.pem 4096");

                        // Construct a string we can modify, since the CN=HOST should be CN=host.domain.local Still with me?
                        string a = @"openssl req -subj ""/CN=" + servers.Host + "." + servers.DNSAddress + @""" -sha256 -new -key server-key.pem -out server.csr";

                        // Run the replace
                        ssh.RunCommand(a);

                        // As earlier - construct the string so we can inject / replace the correct value
                        string b = "echo subjectAltName = DNS:" + servers.Host + "." + servers.DNSAddress + ",IP:" + servers.Address + " >> extfile.cnf";
                        ssh.RunCommand(b);
                        ssh.RunCommand("echo extendedKeyUsage = serverAuth >> extfile.cnf");
                        ssh.RunCommand("openssl x509 -req -days 365 -sha256 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -passin pass:" + servers.SSHPassword + " -out server-cert.pem -extfile extfile.cnf");
                        ssh.RunCommand("openssl genrsa -passout pass:" + servers.SSHPassword + " -out key.pem 4096");
                        ssh.RunCommand("openssl req -subj '/CN=client' -new -key key.pem -out client.csr");
                        ssh.RunCommand("echo extendedKeyUsage = clientAuth > extfile-client.cnf");
                        ssh.RunCommand("openssl x509 -req -days 1000 -sha256 -in client.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -passin pass:" + servers.SSHPassword + " -out cert.pem -extfile extfile-client.cnf");

                        // Generate the PFX
                        ssh.RunCommand("openssl pkcs12 -export -inkey key.pem -in cert.pem -passout=pass:" + servers.CertificateKey + " -out " + servers.CertificateFile + ".pfx");
                        ssh.RunCommand("cp *pfx .docker/");

                        // Make certs available to docker
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " mkdir /etc/docker/ssl");
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " cp /home/mark/ca.pem /etc/docker/ssl/ca.pem");
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " cp /home/mark/server-cert.pem /etc/docker/ssl/server-cert.pem");
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " cp /home/mark/server-key.pem /etc/docker/ssl/server-key.pem");
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " cp /home/mark/key.pem /etc/docker/ssl/key.pem");
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " chown mark /etc/docker/ssl/*");

                        // Secure the files
                        ssh.RunCommand("chmod -v 0400 ca-key.pem key.pem server-key.pem");
                        ssh.RunCommand("chmod -v 0444 ca.pem server-cert.pem cert.pem");

                        // Configure for docker use
                        ssh.RunCommand("mkdir -pv ~/.docker");
                        ssh.RunCommand("cp client.csr server.csr extfile.cnf extfile-client.cnf ~/.docker");
                        ssh.RunCommand("cp " + servers.Host + ".pfx ~/.docker");


                        //// Put the files where dockers told to find them
                        ssh.RunCommand("cp -v {ca,cert,key}.pem ~/.docker");
                        //// Tell docker where to find trhem, again as above a injection / replacement will correct the value
                        string c = "export DOCKER_HOST=tcp://" + servers.Address + ":2376 DOCKER_TLS_VERIFY=1";
                        ssh.RunCommand(c);

                        var upload = new FileStream(@"D:\Certificates\base3.txt", System.IO.FileMode.Open);

                        using (SftpClient ftp = new SftpClient(servers.Address, servers.SSHUser, servers.SSHPassword))
                        {
                            // Connect
                            ftp.Connect();
                            ftp.UploadFile(upload, "/home/mark/docker22.json");


                            ftp.Dispose();
                        }
                        upload.Dispose();
                        ssh.RunCommand("sed -i 's/{HOST}/" + servers.Address + "/g' /home/mark/docker22.json");
                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " cp /home/mark/docker22.json /etc/docker/daemon.json");

                        ssh.RunCommand("sudo -S <<< " + servers.SSHPassword + " service docker stop");
                        ssh.RunCommand("sudo dockerd &");


                        System.Threading.Thread.Sleep(5000);


                        // Docker is now configured

                        // Were done with the SSH Connection
                        ssh.Disconnect();
                        // Dispose it, we dont need it at all
                        ssh.Dispose();
                    }
                }
                catch (SshException sEx)
                {
                    result.Success = false;
                    result.Error = sEx.Message;
                    return result;
                }
                catch (Exception ex)
                {
                    result.Success = false;
                    result.Error = ex.Message;
                    return result;
                }
                try
                {
                    // Establish scp session for file transfer
                    using (SftpClient ftp = new SftpClient(servers.Address, servers.SSHUser, servers.SSHPassword))
                    {
                        // Connect
                        ftp.Connect();
                        if (ftp.IsConnected)
                        {
                            // If we connect, we need to check the local cert store is available, and if not create it
                            // couldnt be bothered with anything complex
                            if (!Directory.Exists(@"D:\Certificates"))
                            {
                                Directory.CreateDirectory(@"D:\Certificates");
                            }

                            // This is wrong, but the methods valid.
                            // Foreach file in the remote .docker directory, we need to get each certificate
                            foreach (Renci.SshNet.Sftp.SftpFile file in ftp.ListDirectory("/home/mark/.docker"))
                            {
                                // Make sure the certstore for this SERVER exists
                                if (!Directory.Exists(@"D:\Certificates\" + servers.Host))
                                {
                                    Directory.CreateDirectory(@"D:\Certificates\" + servers.Host);
                                }

                                // Download the file
                                try
                                {
                                    if (file.Name == ".." || file.Name == ".")
                                    {
                                    }
                                    else
                                    {
                                        using (Stream fileStream = System.IO.File.Create(@"D:\Certificates\" + servers.Host + @"\" + file.Name))
                                        {
                                            ftp.DownloadFile(file.FullName, fileStream);
                                        }
                                    }



                                }
                                catch (Exception ex)
                                {
                                    result.Success = false;
                                    result.Error = file.Name;
                                    return result;
                                }


                                // upload


                            }
                        }
                        // We dont need the ftp, at all so disconnect and dispose

                        ftp.Disconnect();
                        ftp.Dispose();


                    }
                }
                catch (Exception ex)
                {
                    result.Success = false;
                    result.Error = ex.Message;
                    return result;
                }

                try
                {
                    ServicePointManager.ServerCertificateValidationCallback += (o, c, ch, er) => true;

                    // Todo: Move this to class & Task
                    CertificateCredentials credentials = new CertificateCredentials(new X509Certificate2(@"D:\Certificates\docker\docker55.pfx", "password."));
                    credentials.ServerCertificateValidationCallback += (o, c, ch, er) => true;


                    //DockerClientConfiguration config = new DockerClientConfiguration(new Uri("tcp://" + servers.Host + "." + servers.DNSAddress + ":2376"), credentials, default);
                    DockerClientConfiguration config = new DockerClientConfiguration(new Uri("tcp://" + servers.Address + ":2376"), credentials, default);

                    // Because its not immediatly aparant what this line does, it tells the server not to validate the certificates, since the CSR are all
                    // self signed and thus normally invalid
                    
                    DockerClient client = config.CreateClient();


                    // Attempt to talk to docker
                    VersionResponse chk = client.System.GetVersionAsync().GetAwaiter().GetResult();

                    if (chk == null)
                    {
                        throw new Exception("Connection failed");
                    }
                    // if no error, all good.


                }
                catch (Exception ex)
                {
                    result.Success = false;
                    result.Error = ex.Message;
                    return result;
                }

                db.Servers.Add(servers);
                db.SaveChanges();

                result.Success = true;
                result.Error = "";
                result.Message = "Ok";
                return result;
            }
        }
        
    }
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,251
Location
Chesapeake, VA
Programming Experience
10+
Who signed the certificate? If it's self-signed, did you update the certificate store used by SSL to also include the root certificate of your self-signed certificate as a trusted CA?
 
Joined
Nov 17, 2021
Messages
5
Programming Experience
Beginner
Who signed the certificate? If it's self-signed, did you update the certificate store used by SSL to also include the root certificate of your self-signed certificate as a trusted CA?

Errr, do i do that on the host or the client? There was a number of things i tryed on the host, one of which was to copy certs to the ssl folder and then run update-ca-certificates but that didnt seem to work, ive been focusing host side because its the host reporting the error. Thanks for that ill go back and look into how to do it correctly and make sure there installed. ANd yes, they are Self-Signed (at least for this, eventually theyll be signed by my federation servers :)
 
Joined
Nov 17, 2021
Messages
5
Programming Experience
Beginner
Well no closer to solving it, what i can find online tells me i simply need to move the CRT's to a folder and update the CA, and thats fine but i dont produce any CRT files, only PEM, am i missing a step (presumably right from the start)

So i looked into PEM to CRTs tryed a few things, reset it, tryed again, and still no further, stuck at that error message for TLS

Anyone point me in the right direction again?
 

Skydiver

Staff member
Joined
Apr 6, 2019
Messages
5,251
Location
Chesapeake, VA
Programming Experience
10+
Sounds more like a *nix or Java certificate management question than a Windows or C# question.
 
Top Bottom