1
1
using System . Diagnostics ;
2
- using System . Runtime . InteropServices ;
3
- using McMaster . Extensions . CommandLineUtils ;
4
2
5
3
namespace KubeOps . Operator . Commands . CommandHelpers ;
6
4
7
5
internal class CertificateGenerator : IDisposable
8
6
{
7
+ /* Suggested URLs for downloading the executables into the docker image
9
8
private const string CfsslUrlWindows =
10
9
"https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssl_1.5.0_windows_amd64.exe";
11
10
@@ -23,6 +22,7 @@ internal class CertificateGenerator : IDisposable
23
22
24
23
private const string CfsslJsonUrlMacOs =
25
24
"https://github.com/cloudflare/cfssl/releases/download/v1.5.0/cfssljson_1.5.0_darwin_amd64";
25
+ */
26
26
27
27
private const string CaConfig =
28
28
@"{""signing"":{""default"":{""expiry"":""43800h""},""profiles"":{""server"":{""expiry"":""43800h"",""usages"":[""signing"",""key encipherment"",""server auth""]}}}}" ;
@@ -34,21 +34,17 @@ internal class CertificateGenerator : IDisposable
34
34
private readonly string _tempDirectory = Path . Combine ( Path . GetTempPath ( ) , Path . GetRandomFileName ( ) ) ;
35
35
36
36
private bool _initialized ;
37
- private string ? _cfssl ;
38
- private string ? _cfssljson ;
39
37
private string ? _caconfig ;
40
38
private string ? _cacsr ;
41
39
private string ? _servercsr ;
40
+ private string _cfssl = "cfssl" ;
41
+ private string _cfssljson = "cfssljson" ;
42
42
43
43
public CertificateGenerator ( TextWriter appOut )
44
44
{
45
45
_appOut = appOut ;
46
46
}
47
47
48
- private static string ShellExecutor => RuntimeInformation . IsOSPlatform ( OSPlatform . Windows )
49
- ? "cmd.exe"
50
- : "/bin/sh" ;
51
-
52
48
public void Dispose ( )
53
49
{
54
50
Delete ( _caconfig ) ;
@@ -62,6 +58,9 @@ public async Task CreateCaCertificateAsync(string outputFolder)
62
58
{
63
59
if ( ! _initialized )
64
60
{
61
+ var cfsslFolder = Environment . GetEnvironmentVariable ( "CFSSL_EXECUTABLES_PATH" ) ?? "/operator" ;
62
+ _cfssl = $ "{ cfsslFolder } /{ _cfssl } ";
63
+ _cfssljson = $ "{ cfsslFolder } /{ _cfssljson } ";
65
64
await PrepareExecutables ( ) ;
66
65
_initialized = true ;
67
66
}
@@ -70,18 +69,8 @@ public async Task CreateCaCertificateAsync(string outputFolder)
70
69
71
70
await _appOut . WriteLineAsync ( $@ "Generating certificates to ""{ outputFolder } "".") ;
72
71
await _appOut . WriteLineAsync ( "Generating CA certificate." ) ;
73
- await ExecuteProcess (
74
- new Process
75
- {
76
- StartInfo = new ProcessStartInfo
77
- {
78
- WorkingDirectory = outputFolder ,
79
- FileName = ShellExecutor ,
80
- Arguments = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows )
81
- ? $ "/c { _cfssl } gencert -initca { _cacsr } | { _cfssljson } -bare ca -"
82
- : $@ "-c ""{ _cfssl } gencert -initca { _cacsr } | { _cfssljson } -bare ca -""",
83
- } ,
84
- } ) ;
72
+
73
+ await GenCertAsync ( $ "-initca { _cacsr } ", outputFolder ) ;
85
74
86
75
await ListDir ( outputFolder ) ;
87
76
}
@@ -110,33 +99,15 @@ await serverCsrStream.WriteLineAsync(
110
99
111
100
await _appOut . WriteLineAsync (
112
101
$@ "Generating server certificate for ""{ name } "" in namespace ""{ @namespace } "".") ;
113
- await ExecuteProcess (
114
- new Process
115
- {
116
- StartInfo = new ProcessStartInfo
117
- {
118
- WorkingDirectory = outputFolder ,
119
- FileName = ShellExecutor ,
120
- Arguments = RuntimeInformation . IsOSPlatform ( OSPlatform . Windows )
121
- ? $ "/c { _cfssl } gencert -ca=file:{ caPath . Replace ( '\\ ' , '/' ) } -ca-key=file:{ caKeyPath . Replace ( '\\ ' , '/' ) } -config={ _caconfig } -profile=server { _servercsr } | { _cfssljson } -bare server -"
122
- : $@ "-c ""{ _cfssl } gencert -ca={ caPath } -ca-key={ caKeyPath } -config={ _caconfig } -profile=server { _servercsr } | { _cfssljson } -bare server -""",
123
- } ,
124
- } ) ;
125
-
102
+ var arguments =
103
+ $ "-ca=file:{ caPath . Replace ( '\\ ' , '/' ) } -ca-key=file:{ caKeyPath . Replace ( '\\ ' , '/' ) } -config={ _caconfig } -profile=server { _servercsr } ";
104
+ await GenCertAsync ( arguments , outputFolder ) ;
126
105
await ListDir ( outputFolder ) ;
127
106
}
128
107
129
108
private static string ServerCsr ( params string [ ] serverNames ) =>
130
109
$@ "{{""CN"":""Operator Service"",""hosts"":[""{ string . Join ( @""",""" , serverNames ) } ""],""key"":{{""algo"":""ecdsa"",""size"":256}},""names"":[{{""C"":""DEV"",""L"":""Kubernetes""}}]}}";
131
110
132
- private static Task ExecuteProcess ( Process process )
133
- => Task . Run (
134
- ( ) =>
135
- {
136
- process . Start ( ) ;
137
- process . WaitForExit ( 2000 ) ;
138
- } ) ;
139
-
140
111
private static void Delete ( string ? file )
141
112
{
142
113
if ( file == null )
@@ -150,66 +121,50 @@ private static void Delete(string? file)
150
121
}
151
122
}
152
123
124
+ private async Task GenCertAsync (
125
+ string arguments ,
126
+ string outputFolder ,
127
+ CancellationToken cancellationToken = default )
128
+ {
129
+ var genCertPsi = new ProcessStartInfo
130
+ {
131
+ WorkingDirectory = outputFolder ,
132
+ FileName = _cfssl ,
133
+ Arguments = $ "gencert { arguments } ",
134
+ RedirectStandardOutput = true ,
135
+ UseShellExecute = false ,
136
+ } ;
137
+ var writeJsonPsi = new ProcessStartInfo
138
+ {
139
+ WorkingDirectory = outputFolder ,
140
+ FileName = _cfssljson ,
141
+ Arguments = "-bare ca -" ,
142
+ RedirectStandardInput = true ,
143
+ UseShellExecute = false ,
144
+ } ;
145
+ using var genCert = new Process { StartInfo = genCertPsi } ;
146
+ using var writeJson = new Process { StartInfo = writeJsonPsi } ;
147
+
148
+ genCert . Start ( ) ;
149
+ await genCert . WaitForExitAsync ( cancellationToken ) ;
150
+
151
+ writeJson . Start ( ) ;
152
+ await writeJson . StandardInput . WriteAsync ( await genCert . StandardOutput . ReadToEndAsync ( ) ) ;
153
+ await writeJson . StandardInput . FlushAsync ( ) ;
154
+ writeJson . StandardInput . Close ( ) ;
155
+ await writeJson . WaitForExitAsync ( cancellationToken ) ;
156
+ }
157
+
153
158
private async Task PrepareExecutables ( )
154
159
{
155
160
Directory . CreateDirectory ( _tempDirectory ) ;
156
- _cfssl = Path . Join ( _tempDirectory , Path . GetRandomFileName ( ) ) ;
157
- _cfssljson = Path . Join ( _tempDirectory , Path . GetRandomFileName ( ) ) ;
158
161
_caconfig = Path . Join ( _tempDirectory , Path . GetRandomFileName ( ) ) ;
159
162
_cacsr = Path . Join ( _tempDirectory , Path . GetRandomFileName ( ) ) ;
160
163
161
- using ( var client = new HttpClient ( ) )
162
- await using ( var cfsslStream = new FileStream ( _cfssl , FileMode . CreateNew ) )
163
- await using ( var cfsslJsonStream = new FileStream ( _cfssljson , FileMode . CreateNew ) )
164
- await using ( var caConfigStream = new StreamWriter ( new FileStream ( _caconfig , FileMode . CreateNew ) ) )
165
- await using ( var caCsrStream = new StreamWriter ( new FileStream ( _cacsr , FileMode . CreateNew ) ) )
166
- {
167
- await caConfigStream . WriteLineAsync ( CaConfig ) ;
168
- await caCsrStream . WriteLineAsync ( CaCsr ) ;
169
-
170
- if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
171
- {
172
- await _appOut . WriteLineAsync ( "Download cfssl / cfssljson for windows." ) ;
173
- await using var cfsslDl = await client . GetStreamAsync ( CfsslUrlWindows ) ;
174
- await using var cfsslJsonDl = await client . GetStreamAsync ( CfsslJsonUrlWindows ) ;
175
-
176
- await cfsslDl . CopyToAsync ( cfsslStream ) ;
177
- await cfsslJsonDl . CopyToAsync ( cfsslJsonStream ) ;
178
- }
179
- else if ( RuntimeInformation . IsOSPlatform ( OSPlatform . Linux ) )
180
- {
181
- await _appOut . WriteLineAsync ( "Download cfssl / cfssljson for linux." ) ;
182
- await using var cfsslDl = await client . GetStreamAsync ( CfsslUrlLinux ) ;
183
- await using var cfsslJsonDl = await client . GetStreamAsync ( CfsslJsonUrlLinux ) ;
184
-
185
- await cfsslDl . CopyToAsync ( cfsslStream ) ;
186
- await cfsslJsonDl . CopyToAsync ( cfsslJsonStream ) ;
187
- }
188
- else
189
- {
190
- await _appOut . WriteLineAsync ( "Download cfssl / cfssljson for macos." ) ;
191
- await using var cfsslDl = await client . GetStreamAsync ( CfsslUrlMacOs ) ;
192
- await using var cfsslJsonDl = await client . GetStreamAsync ( CfsslJsonUrlMacOs ) ;
193
-
194
- await cfsslDl . CopyToAsync ( cfsslStream ) ;
195
- await cfsslJsonDl . CopyToAsync ( cfsslJsonStream ) ;
196
- }
197
- }
198
-
199
- if ( ! RuntimeInformation . IsOSPlatform ( OSPlatform . Windows ) )
200
- {
201
- await _appOut . WriteLineAsync ( "Make unix binaries executable." ) ;
202
- await ExecuteProcess (
203
- new Process
204
- {
205
- StartInfo = new ProcessStartInfo
206
- {
207
- FileName = "chmod" ,
208
- Arguments = ArgumentEscaper . EscapeAndConcatenate (
209
- new [ ] { "+x" , _cfssl , _cfssljson } ) ,
210
- } ,
211
- } ) ;
212
- }
164
+ await using var caConfigStream = new StreamWriter ( new FileStream ( _caconfig , FileMode . CreateNew ) ) ;
165
+ await using var caCsrStream = new StreamWriter ( new FileStream ( _cacsr , FileMode . CreateNew ) ) ;
166
+ await caConfigStream . WriteLineAsync ( CaConfig ) ;
167
+ await caCsrStream . WriteLineAsync ( CaCsr ) ;
213
168
}
214
169
215
170
private async Task ListDir ( string directory )
0 commit comments