-
Notifications
You must be signed in to change notification settings - Fork 0
/
cert-manager.html
1447 lines (603 loc) · 64 KB
/
cert-manager.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html class="theme-next pisces use-motion" lang="en">
<head>
<meta charset="UTF-8"/>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2"/>
<meta name="theme-color" content="#222"/>
<link rel="stylesheet" href="/lib/font-awesome/css/font-awesome.min.css?v=4.6.2"/>
<link rel="stylesheet" href="/css/main.css?v=7.0.1"/>
<link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=7.0.1">
<link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=7.0.1">
<link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=7.0.1">
<link rel="mask-icon" href="/images/logo.svg?v=7.0.1" color="#222">
<script id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Pisces',
version: '7.0.1',
sidebar: {"position":"left","display":"post","offset":12,"onmobile":false,"dimmer":false},
back2top: true,
back2top_sidebar: false,
fancybox: false,
fastclick: false,
lazyload: false,
tabs: true,
motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
</script>
<meta name="description" content="在這個資訊安全意識稍微抬頭的世代,網站配有 HTTPS 可說是個基本標配。同時因為 Let's Encrypt 的出現,讓 TLS 憑證的申請變得簡單且容易上手。然而使用 Let's Encrypt 本身還是有一些限制要處理,譬如需要定期更新憑證,以及如何驗證申請者目標網域的擁有者,這部分的操作都有對應的腳本來處理。然而在 Kubernetes 叢集之中,除了手動去運行這些腳本之外,有沒有更方便的">
<meta name="keywords" content="kubernetes,tls,ssl,certificated,lets encrypt,cert-manager">
<meta property="og:type" content="article">
<meta property="og:title" content="Automatically Renew Your Certificated in kubernetes by Cert-Manager">
<meta property="og:url" content="https://www.hwchiu.com/cert-manager.html">
<meta property="og:site_name" content="Hwchiu Learning Note">
<meta property="og:description" content="在這個資訊安全意識稍微抬頭的世代,網站配有 HTTPS 可說是個基本標配。同時因為 Let's Encrypt 的出現,讓 TLS 憑證的申請變得簡單且容易上手。然而使用 Let's Encrypt 本身還是有一些限制要處理,譬如需要定期更新憑證,以及如何驗證申請者目標網域的擁有者,這部分的操作都有對應的腳本來處理。然而在 Kubernetes 叢集之中,除了手動去運行這些腳本之外,有沒有更方便的">
<meta property="og:locale" content="en">
<meta property="og:image" content="https://i.imgur.com/kMj495o.png">
<meta property="og:image" content="https://i.imgur.com/niybCcQ.png">
<meta property="og:updated_time" content="2018-10-03T09:31:46.789Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Automatically Renew Your Certificated in kubernetes by Cert-Manager">
<meta name="twitter:description" content="在這個資訊安全意識稍微抬頭的世代,網站配有 HTTPS 可說是個基本標配。同時因為 Let's Encrypt 的出現,讓 TLS 憑證的申請變得簡單且容易上手。然而使用 Let's Encrypt 本身還是有一些限制要處理,譬如需要定期更新憑證,以及如何驗證申請者目標網域的擁有者,這部分的操作都有對應的腳本來處理。然而在 Kubernetes 叢集之中,除了手動去運行這些腳本之外,有沒有更方便的">
<meta name="twitter:image" content="https://i.imgur.com/kMj495o.png">
<link rel="alternate" href="/atom.xml" title="Hwchiu Learning Note" type="application/atom+xml"/>
<link rel="canonical" href="https://www.hwchiu.com/cert-manager.html"/>
<script id="page.configurations">
CONFIG.page = {
sidebar: "",
};
</script>
<title>Automatically Renew Your Certificated in kubernetes by Cert-Manager | Hwchiu Learning Note</title>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '',
xfbml : true,
version: 'v2.10'
});
};
(function(d, s, id) {
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en/sdk.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>
<script async src="//www.googletagmanager.com/gtag/js?id=UA-54006186-1"></script>
<script>
var host = window.location.hostname;
if (host !== "localhost" || !true) {
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-54006186-1');
}
</script>
<noscript>
<style>
.use-motion .motion-element,
.use-motion .brand,
.use-motion .menu-item,
.sidebar-inner,
.use-motion .post-block,
.use-motion .pagination,
.use-motion .comments,
.use-motion .post-header,
.use-motion .post-body,
.use-motion .collection-title { opacity: initial; }
.use-motion .logo,
.use-motion .site-title,
.use-motion .site-subtitle {
opacity: initial;
top: initial;
}
.use-motion .logo-line-before i { left: initial; }
.use-motion .logo-line-after i { right: initial; }
</style>
</noscript>
<script async src="//pagead2.googlesyndication.com/pagead/js/adsbygoogle.js"></script>
<script>
(adsbygoogle = window.adsbygoogle || []).push({
google_ad_client: "ca-pub-1100711228720990",
enable_page_level_ads: true
});
</script>
</head>
<body itemscope itemtype="http://schema.org/WebPage" lang="en">
<div class="container sidebar-position-left page-post-detail">
<div class="headband"></div>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-wrapper">
<div class="site-meta">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">Hwchiu Learning Note</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<h1 class="site-subtitle" itemprop="description">kubernetes, sdn, linux,devops</h1>
</div>
<div class="site-nav-toggle">
<button aria-label="Toggle navigation bar">
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" rel="section"><i class="menu-item-icon fa fa-fw fa-home"></i> <br/>Home</a>
</li>
<li class="menu-item menu-item-about">
<a href="/about/" rel="section"><i class="menu-item-icon fa fa-fw fa-user"></i> <br/>About</a>
</li>
<li class="menu-item menu-item-tags">
<a href="/tags/" rel="section"><i class="menu-item-icon fa fa-fw fa-tags"></i> <br/>Tags</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" rel="section"><i class="menu-item-icon fa fa-fw fa-archive"></i> <br/>Archives</a>
</li>
<li class="menu-item menu-item-sitemap">
<a href="/sitemap.xml" rel="section"><i class="menu-item-icon fa fa-fw fa-sitemap"></i> <br/>Sitemap</a>
</li>
</ul>
</nav>
</div>
</header>
<a href="https://github.com/hwchiu" class="github-corner" title="Follow me on GitHub" aria-label="Follow me on GitHub" rel="noopener" target="_blank"><svg width="80" height="80" viewBox="0 0 250 250" style="fill: #222; color: #fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<div id="posts" class="posts-expand">
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-block">
<link itemprop="mainEntityOfPage" href="https://www.hwchiu.com/cert-manager.html"/>
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="Hwchiu"/>
<meta itemprop="description" content="kubernetes/SDN/DevOps"/>
<meta itemprop="image" content="/images/avatar.jpg"/>
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="Hwchiu Learning Note"/>
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">Automatically Renew Your Certificated in kubernetes by Cert-Manager
</h2>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">Posted on</span>
<time title="Created: 2018-10-03 02:48:04 / Modified: 09:31:46" itemprop="dateCreated datePublished" datetime="2018-10-03T02:48:04+00:00">2018-10-03</time>
</span>
<span class="post-comments-count">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-comment-o"></i>
</span>
<span class="post-meta-item-text">Comments: </span>
<a href="/cert-manager.html#comments" itemprop="discussionUrl">
<span class="post-comments-count disqus-comment-count" data-disqus-identifier="cert-manager.html" itemprop="commentCount"></span>
</a>
</span>
<div class="post-description">在這個資訊安全意識稍微抬頭的世代,網站配有 HTTPS 可說是個基本標配。同時因為 Let's Encrypt 的出現,讓 TLS 憑證的申請變得簡單且容易上手。然而使用 Let's Encrypt 本身還是有一些限制要處理,譬如需要定期更新憑證,以及如何驗證申請者目標網域的擁有者,這部分的操作都有對應的腳本來處理。然而在 Kubernetes 叢集之中,除了手動去運行這些腳本之外,有沒有更方便的方式可以整合這一切。本文要介紹一個叫做 `Cert-Manager` 的解決方案,透過其原理的理解,以及實際操作的步驟來學習如何更方便的在 kubernetes 叢集內管理憑證</div>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<h1 id="Preface"><a href="#Preface" class="headerlink" title="Preface"></a>Preface</h1><p>之前於 <a href="https://www.hwchiu.com/k8s-security-11tips-i.html">11個保護你 Kubernetes 集群的技巧與觀念(上)</a> 中有不斷的強調加密連線 <code>SSL</code> 的重要性。最簡單的範例就是如果今天有一個需求需要於 <code>kubernetes</code> 叢集中部屬一個網頁服務供外部存取時,預設情況下我們都會走 <code>HTTP(80)</code>, 然而在安全意識崛起的時代,基本上一個網頁服務沒有 <code>HTTPS(443)</code> 的存取方式時,大家對於是否要在該網站輸入一些機密資料都會變得小心翼翼甚至拒絕使用。</p>
<p>為了讓該網頁伺服器提供 <code>HTTPS</code> 的連線方式,我們必須要提供一組憑證,且該憑證的簽署方是要被信任的。<br>在這個情況下,我們可以像第三方廠商購買憑證的方式來取得一個合法的憑證,並且將該憑證部屬在我們的網頁伺服器上,這樣就可以順利的提供 <code>HTTPS</code> 安全連線方式。</p>
<p>但是更多時候,很多人都只有買域名卻沒有買憑證,解決方法都是透過免費的 <code>Let's Encrypt</code> 去簽署憑證,然而 <code>Let's Encrypt</code> 本身期限很短,需要定期更新來確保憑證的有效性。</p>
<p>這個過程其實不會太難,基本上就是申請 <code>Let's Encrypt</code> 服務,然後想辦法定期更新該憑證即可。<br>然而這個步驟有沒有辦法變得很簡單? 可以讓叢集部屬人員可以用非常簡單的方式來達成這件事情?</p>
<p>本文會跟大家介紹 <code>Cert-Manager</code> 這套用來管理憑證相關的服務,如何透過這個服務可以輕鬆的在 <code>kubernetes</code> 內把上述的事情自動處理完畢,讓你只需要專心處理自己的應用程式即可。</p>
<h1 id="Component"><a href="#Component" class="headerlink" title="Component"></a>Component</h1><p>如同前言所述,接下來的文章都會針對 <code>Cert-Manager</code> 這個套件作介紹,大致上會講述如果要完成前述所需要的自動更新憑證等相關事情,於 <code>Cert-Manager</code> 中實際上會是怎麼完成的。同時身為系統管理員,需要準備哪些相關的資訊來處理。</p>
<h2 id="Issuers"><a href="#Issuers" class="headerlink" title="Issuers"></a>Issuers</h2><p><code>Cert-Manager</code> 裡面總共有兩種類型的 <code>Issuers</code>, 分別是 <code>Issuer</code> 以及 <code>ClusterIssuers</code>. 這兩個的差異只有在於其影響的 <code>kubernetes namespace</code> 的範圍而以,其餘的功能都一樣. 所以接下來的文章將會使用 <code>Issuers</code> 來當做範例介紹.</p>
<p><code>Issuers</code> 代表的是一個能夠從會簽署 <code>x509</code> 憑證的授權單位所取得憑證的一個資源,最<br>目前 <code>Issuers</code> 支援的 <code>DNS Challenge</code> 有兩種,分別是 <code>HTTP-01</code> 以及 <code>DNS-01</code>.<br>常見的後端代表就是想辦法從 <code>Let's Encrypt</code> 取得憑證. </p>
<ol>
<li>Issuers 是透過 <code>Kubernetes CustomResourceDefinition(CRD)</code> 所自定義的新資源<ul>
<li>這意味你可以透過 <code>kubectl get issuers</code> 來檢視相關的設定</li>
</ul>
</li>
<li>對於每個 <code>kubernetes</code> 叢集來說,系統內至少要部屬一個 <code>Issuers</code> 來完成憑證的功能</li>
<li>Issuers 目前提供了四種後端接口, 這邊會稍微介紹一下彼此的差異,最後說明本文會基於哪種後端接口來展示</li>
</ol>
<h3 id="ACME"><a href="#ACME" class="headerlink" title="ACME"></a>ACME</h3><p>這個類型應該是最常見也是最多人在使用的,透過 <a href="https://en.wikipedia.org/wiki/Automated_Certificate_Management_Environment" target="_blank" rel="noopener">Automated Certificate Management Environment(ACME)</a><br>這個協定與已知的 <code>CA</code> 溝通來取得憑證。</p>
<p>與 <code>CA</code> 的溝通過程中,最困難也是最核心的部份就是 <code>DNS Challenge</code>, 使用者必須要向 <code>CA</code> 證明自己擁有想要簽署憑證的域名。<br>目前 <code>Issuers</code> 支援的 <code>DNS Challenge</code> 有兩種,分別是 <code>HTTP-01</code> 以及 <code>DNS-01</code>.</p>
<h4 id="HTTP-01"><a href="#HTTP-01" class="headerlink" title="HTTP-01"></a>HTTP-01</h4><p>顧名思義,就是透過 <code>HTTP</code> 的方式來驗證是否擁有該網域,要採用這種方式基本上要滿足幾個條件</p>
<ol>
<li>首先針對想要簽署的網域進行設定,譬如將 <code>test.hwchiu.com</code> 指向到一個 <code>IP</code> 地址</li>
<li>該 <code>CA</code> 會嘗試透過 <code>HTTP</code> 的方式去連線 <code>test.hwchiu.com</code>. 其會預期得到一個對應的 <code>HTTP</code> 回應,該回應的格式可以參考 <a href="https://tools.ietf.org/html/draft-ietf-acme-acme-07#section-8.3" target="_blank" rel="noopener"> HTTP Challenge</a></li>
</ol>
<p>透過上述兩個條件,我們可以觀察到幾個現象</p>
<ol>
<li><code>HTTP-01</code> 並不支援 <code>wildcard</code> 的 <code>domain</code>, 因為要透過 <code>DNS</code> 查詢對應的 IP 來取得 HTTP 回應</li>
<li>為了讓 <code>HTTP</code> 封包可以順利的連線,通常客戶端都會需要有一個可存取的對外 IP 地址<ul>
<li>當然這部分你若熟悉網路,要透過 <code>DNAT</code> 的方式將封包導入到私有 IP 的網頁伺服器上也是沒有問題</li>
</ul>
</li>
</ol>
<p>大部分人的人在使用 <code>Let's Encrypt</code> 時基本上都是透過 <code>HTTP-01</code> 這個方式來驗證,因為使用上其實相對簡單,基本的那些網頁伺服器以及對應的 HTTP 格式都有相關的軟體幫忙處理,大幅度簡化了管理員的設定。</p>
<p>本篇文章並不打算使用這個方式,因為過於簡單沒有什麼好設定的,所以我們來看看另外一種挑戰, <code>DNS-01</code>.</p>
<h4 id="DNS-01"><a href="#DNS-01" class="headerlink" title="DNS-01"></a>DNS-01</h4><p>相對於 <code>HTTP-01</code> 透過 <code>HTTP</code> 連線來驗證是否有該網域的擁有權, <code>DNS-01</code> 則是透過創造一個預期內容的 <code>TXT</code> 紀錄來證明你擁有該網域的擁有權。為了達成上述的事項,<code>Issuers</code> 目前有支援下列的 <code>DNS</code> 供應商來幫忙完成這個自動創建 <code>TXT</code> 紀錄的需求</p>
<ul>
<li>Google CloudDNS</li>
<li>Amazon Route53</li>
<li>Cloudflare</li>
<li>Akamai FastDNS</li>
<li>RFC2136</li>
<li>ACME-DNS</li>
</ul>
<p>透過 <code>DNS-01</code> 的機制就可以打破 <code>HTTP-01</code> 的限制,客戶端本身即使位於私有網路內也是沒有問題的。</p>
<p>而因為我自己的網域目前是放在 <code>Cloudflare</code> 上,因此本文就會採取 <code>DNS-01</code> 的方式並且使用 <code>Cloudflare</code> 作為測試的 <code>DNS</code> 提供者。</p>
<h3 id="CA"><a href="#CA" class="headerlink" title="CA"></a>CA</h3><p>相對於上述的 <code>ACME</code> 的使用方式, <code>CA</code> 類型的 <code>Issuers</code> 則是會使用一組事先準備好的簽署金鑰來發行憑證。這組簽署憑證必須要以 <code>kubernetes secret</code> 的形式存於 <code>kubernetes cluster</code> 中。<br>舉例來說,你可以先透過 <code>openssl</code> 的方式產生對應的檔案,並且把該檔案加入到 <code>kubernetes secret</code> 內,最後透過 <code>CA</code> 類型的 <code>Issuers</code> 來使用。</p>
<h3 id="Valut"><a href="#Valut" class="headerlink" title="Valut"></a>Valut</h3><p>如果你系統內有安裝 <a href="https://www.vaultproject.io/" target="_blank" rel="noopener">HashCrop Valut</a> 的話,可以透過這個類型的 <code>Issuers</code> 來與 <code>Valut</code> 溝通。</p>
<h3 id="Self-Signed"><a href="#Self-Signed" class="headerlink" title="Self-Signed"></a>Self-Signed</h3><p>這個選項目前會用到的機會不多,大致上有兩種情境</p>
<ol>
<li>想要透過 <code>kubernetes</code> 打造一個 <code>Public Key Infrastructur (PKI)</code> 架構</li>
<li>搭配後續會介紹的 <code>Certificate</code> 作為一個 <code>root CA</code> 供 <code>CA</code> 型態的 <code>Issuers</code> 使用<br> -. 可以參考 <a href="/https://github.com/jetstack/cert-manager/blob/834fda15a1160b3e75d92ab7d84b1402eac9be0a/docs/admin/resource-validation-webhook.rst">resource-validation-webhook</a> 的使用範例來瞭解如何搭配使用</li>
</ol>
<p>想要更加深入的瞭解這些選項可以直接到<a href="http://docs.cert-manager.io/en/latest/reference/issuers.html" target="_blank" rel="noopener">官網</a>閱讀相關的資訊以及範例使用</p>
<h2 id="Certificate"><a href="#Certificate" class="headerlink" title="Certificate"></a>Certificate</h2><p>看完了 <code>Issuers</code> 之後,我們來看看第二個也是最後一個透過 <code>kubernetes CRD</code> 所建立的物件,<code>Certificate</code>.</p>
<p><code>Certificate</code> 就如同其名稱一樣,代表的是一個憑證,其會嘗試使用 <code>Issuers</code> 來獲得對應的憑證與金鑰,當成功取得對應的資源後,就會將該對憑證與金鑰的資訊放到 <code>kubernetes secret</code> 裡面。<br><code>Certificate</code> 以及產生的 <code>kubernetes secret</code> 都跟 <code>namespace</code> 有綁定關係,所以對於不同的 <code>namespace</code> 的應用程式若想要擁有這張簽署的憑證,則都需要創立一個對應的 <code>certificate</code> 來使用。</p>
<h2 id="Summary"><a href="#Summary" class="headerlink" title="Summary"></a>Summary</h2><p>基本上整個 <code>cert-manager</code> 的關係都是由 <code>Issuers/Certificate</code> 所組成的,當然也有部屬安裝 <code>cert-manager</code> 時會佈署的 <code>kubernetes deployment</code>, 該 <code>deployment</code> 作為 <code>Issuers/Certificate</code> 之間的橋樑, 定期確認 <code>Certificate</code> 的合法性並且當憑證快要到期時還會嘗試更新。</p>
<p>透過這些資源的整合,<code>Cert-Manager</code> 就能夠於 <code>kubernetes</code> 叢集內提供可以從 <code>Let's Encrypt</code> 取得憑證並且定期自動更新以確保憑證不會過期的憑證供我們其他的應用程式使用。</p>
<h1 id="Setup"><a href="#Setup" class="headerlink" title="Setup"></a>Setup</h1><p>看了上述的基本概念介紹後,我們接下來就要手把手的嘗試透過 <code>Cert-Manager</code> 來取得一個憑證,這中間的過程大概如下</p>
<ol>
<li>部屬 <code>cert-manager</code> 相關的服務到 <code>kubernetes</code> 叢集中</li>
<li>部署 <code>Issuers</code></li>
<li>部屬 <code>Certificate</code></li>
<li>透過檢查 <code>Kubernetes Secret</code>, 確認是否有拿到可用的憑證</li>
</ol>
<p>整個系統的架構圖如下,基本上我們會有 <code>Issuers/Certificate</code> 基本元件,同時為了滿足 <code>DNS-01</code>, 這邊會需要一個額外的 <code>kubernetes secret</code> 供 <code>Issuers</code> 使用。此外 <code>Certificate</code> 最後則會產生一個 <code>kubernetes secret</code> 的合法憑證。</p>
<p><img src="https://i.imgur.com/kMj495o.png" alt="Imgur"></p>
<h2 id="Cert-Manager"><a href="#Cert-Manager" class="headerlink" title="Cert-Manager"></a>Cert-Manager</h2><p>安裝 <code>Cert-Manager</code> 的部分有兩種安裝方式,第一種就是最基本也是最熟悉的,根據自己的環境去部屬事先提供好了 <code>Yaml</code> 檔案。<br>這部分可以參考<a href="https://github.com/jetstack/cert-manager/tree/master/contrib/manifests/cert-manager" target="_blank" rel="noopener">官方 Github </a> 上面的檔案,基本上會根據叢集是否有啟用 <code>RBAC (Role-Based Access Control)</code> 來決定要部屬那個檔案。</p>
<p>另外一個安裝方式則是透過 <code>Package</code> 的概念來安裝,這邊採用的是 <code>Helm</code> 的方式來安裝,透過 <code>Helm</code> 的打包及管理,套件管理員將該服務所需要的 <code>Yaml</code> 都收集完畢並且打包,使用者只需要透過 <code>Helm</code> 的指令很簡單輕鬆的直接安裝完畢。</p>
<p>這邊我就直接使用 <code>Helm</code> 的方式來安裝 <code>Cert-Manager</code>.<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">hwchiu➜~» helm install \ </span><br><span class="line"> --name cert-manager \</span><br><span class="line"> --namespace kube-system \</span><br><span class="line"> stable/cert-manager</span><br></pre></td></tr></table></figure></p>
<div class="note info">
<p>想要安裝 <code>Helm</code> 可以參考<a href="https://docs.helm.sh/using_helm/" target="_blank" rel="noopener">官網的教學</a></p>
</div>
<p>安裝完畢後,可以透過 <code>kubectl</code> 的指令去確認是否需要的 <code>deployment</code> 有運行起來<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">hwchiu➜~» kubectl -n kube-system get deployment/cert-manager</span><br><span class="line">NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE</span><br><span class="line">cert-manager 1 1 1 1 5d</span><br></pre></td></tr></table></figure></p>
<h2 id="Issuers-1"><a href="#Issuers-1" class="headerlink" title="Issuers"></a>Issuers</h2><p>當 <code>cert-manager</code> 正確安裝後,我們接下來要開始處理 <code>Issuers</code>, 再我的範例之中,我採用的是 <code>clusterIssuers</code> 來建立一個供所有 <code>namespace</code> 都可以使用的 <code>Issuers</code>.</p>
<p>此外,由於我要採用的是 <code>DNS-01</code> 的方式來驗證網域的擁有權,同時我的 <code>DNS</code> 提供商是 <code>CloudFlare</code>. 因此我必須要先創立一個 <code>kubernetes sercet</code> 讓我的 <code>Issuers</code> 有能力操控我的 <code>CloudFlar</code>.</p>
<h3 id="CloudFlare"><a href="#CloudFlare" class="headerlink" title="CloudFlare"></a>CloudFlare</h3><p>這部分會依據你的 <code>DNS</code> 供應商有不同的做法,詳細的可以參考 <a href="http://docs.cert-manager.io/en/latest/reference/issuers/acme/dns01.html#supported-dns01-providers" target="_blank" rel="noopener">Cert-Manager Supported DNS01 Providers</a></p>
<ol>
<li>以 <code>CloudFlare</code> 爲範例,首先到你網域的控制台中去取得你的 <code>Public Key</code>. 可參考下圖</li>
<li>得到該 <code>Public Key</code> 後,接下來要轉成 <code>Base64</code> 並且放到 <code>kubernetes sercet</code>.</li>
</ol>
<p><img src="https://i.imgur.com/niybCcQ.png" alt="Imgur"></p>
<p>假設該 <code>Public Key</code> 是 <code>12345678910</code>, 執行下列函式得到對應的 <code>Base64</code> 編碼結果<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> -n <span class="string">"12345678910"</span> | base64</span><br></pre></td></tr></table></figure></p>
<p>接下來我們要透過 <code>yaml</code> 的方式創建 <code>kubernetes secret</code>.<br>打開文件 <code>sercet.yml</code> 並且貼上下列內容<br><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">v1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Secret</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line"><span class="attr"> name:</span> <span class="string">cloudflare</span></span><br><span class="line"><span class="attr"> namespace:</span> <span class="string">kube-system</span></span><br><span class="line"><span class="attr">data:</span></span><br><span class="line"><span class="attr"> api:</span> <span class="string">MTIzNDU2Nzg5MTA=</span></span><br></pre></td></tr></table></figure></p>
<p>創建好上述的 <code>yaml</code> 檔案後,我們就透過 <code>kubectl create -f secret.yml</code></p>
<h3 id="ClusterIssuers"><a href="#ClusterIssuers" class="headerlink" title="ClusterIssuers"></a>ClusterIssuers</h3><p>接下來我們就要來創建我們的 <code>ClusterIssuers</code> 了,</p>
<p>這邊我們直接來看相關的 <code>yaml</code> 內容,有一些東西要注意</p>
<ol>
<li>我們在 <code>Issuer</code> 內目前使用了 <code>acme</code> 的方式來認證</li>
<li>server 的部分,一般來說會先用 <code>staging</code> 的內容來確認所有的資訊都正常且可以運作,正式環境中就會切換成 <code>v02</code> 的版本來使用</li>
<li>於 <code>acme</code> 裡面,我們使用 <code>dns01</code> 的方式來驗證<ul>
<li>我們採用的是 <code>cloudflare</code> 的方式, 這邊就是填寫你擁有網域的信箱</li>
<li>接下來要使用剛剛創立的 <code>kubernetes secret</code>, <code>name/key</code> 就對應到 <code>kubernetes secret</code> 內的 <code>metadata:name</code> 以及 <code>data/api</code>.</li>
</ul>
</li>
</ol>
<figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">certmanager.k8s.io/v1alpha1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">ClusterIssuer</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line"><span class="attr"> name:</span> <span class="string">cert-demo</span></span><br><span class="line"><span class="attr"> namespace:</span> <span class="string">default</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line"><span class="attr"> acme:</span></span><br><span class="line"> <span class="comment">#server: https://acme-v02.api.letsencrypt.org/directory</span></span><br><span class="line"><span class="attr"> server:</span> <span class="attr">https://acme-staging-v02.api.letsencrypt.org/directory</span></span><br><span class="line"><span class="attr"> email:</span> <span class="string">your@mail.com</span></span><br><span class="line"> <span class="comment"># Name of a secret used to store the ACME account private key</span></span><br><span class="line"><span class="attr"> privateKeySecretRef:</span></span><br><span class="line"><span class="attr"> name:</span> <span class="string">cert-demo</span></span><br><span class="line"> <span class="comment"># ACME DNS-01 provider configurations</span></span><br><span class="line"><span class="attr"> dns01:</span></span><br><span class="line"> <span class="comment"># Here we define a list of DNS-01 providers that can solve DNS challenges </span></span><br><span class="line"><span class="attr"> providers:</span></span><br><span class="line"><span class="attr"> - name:</span> <span class="string">cf-dns</span></span><br><span class="line"><span class="attr"> cloudflare:</span></span><br><span class="line"><span class="attr"> email:</span> <span class="string">your@mail.com</span></span><br><span class="line"> <span class="comment"># A secretKeyRef to a cloudflare api key</span></span><br><span class="line"><span class="attr"> apiKeySecretRef:</span></span><br><span class="line"><span class="attr"> name:</span> <span class="string">cloudflare</span></span><br><span class="line"><span class="attr"> key:</span> <span class="string">api</span></span><br></pre></td></tr></table></figure>
<p>創建上述 <code>ClusterIssuers</code> 的檔案之後,可以透過 <code>kubectl describe clusgerissuer/cert-demo</code> 去確認狀態,譬如<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">.....</span><br><span class="line">Status:</span><br><span class="line"> Acme:</span><br><span class="line"> Uri: https://acme-staging-v02.api.letsencrypt.org/acme/acct/7037688</span><br><span class="line"> Conditions:</span><br><span class="line"> Last Transition Time: 2018-09-30T16:51:03Z</span><br><span class="line"> Message: The ACME account was registered with the ACME server </span><br><span class="line"> Reason: ACMEAccountRegistered</span><br><span class="line"> Status: True</span><br><span class="line"> Type: Ready</span><br><span class="line">Events: <none></span><br></pre></td></tr></table></figure></p>
<p>確認這邊是 <code>True/Ready</code> 之後, 對於 <code>Issuers</code> 的事情就告一段落了</p>
<h2 id="Certificates"><a href="#Certificates" class="headerlink" title="Certificates"></a>Certificates</h2><p>正式進入 <code>Yaml</code> 之前,有幾個重要的事情要先注意</p>
<ol>
<li>每個 <code>Certificate</code> 可以設定 <code>Subject Alternative Names(SANs)</code>, 亦即可以設定多個 <code>domain name</code>.</li>
<li>針對設定的所有 <code>domain name</code>, <code>Issuers</code> 都必須要去處理。實際上在 <code>Issuers</code> 的設定中,可以設定多種方法,甚至是同時支援多個 <code>DNS</code> 供應商。</li>
<li>因此對應<code>Certificae</code> 中的每個 <code>domain name</code>, 都要指派對應 <code>Issuers</code> 的可以用來處理該 <code>Certificate</code> 的物件,譬如 <code>dns-01</code> 內的某種 <code>dns</code> 供應商。</li>
<li>每個 <code>Certificate</code> 最後會把得到的憑證資訊都寫入到 <code>Kubernetes Secret</code> 的資源內,所以我們必須要設定我們期望的名稱</li>
</ol>
<p>有了上述的概念後,我們就可以來看一下 <code>Yaml</code> 的內容了<br><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">apiVersion:</span> <span class="string">certmanager.k8s.io/v1alpha1</span></span><br><span class="line"><span class="attr">kind:</span> <span class="string">Certificate</span></span><br><span class="line"><span class="attr">metadata:</span></span><br><span class="line"><span class="attr"> name:</span> <span class="string">demo-certi</span></span><br><span class="line"><span class="attr">spec:</span></span><br><span class="line"><span class="attr"> secretName:</span> <span class="string">demo-certi-tls</span></span><br><span class="line"><span class="attr"> issuerRef:</span></span><br><span class="line"><span class="attr"> name:</span> <span class="string">cert-demo</span></span><br><span class="line"><span class="attr"> kind:</span> <span class="string">ClusterIssuer</span></span><br><span class="line"><span class="attr"> dnsNames:</span></span><br><span class="line"><span class="bullet"> -</span> <span class="string">test.hwchiu.com</span></span><br><span class="line"><span class="attr"> acme:</span></span><br><span class="line"><span class="attr"> config:</span></span><br><span class="line"><span class="attr"> - dns01:</span></span><br><span class="line"><span class="attr"> provider:</span> <span class="string">cf-dns</span></span><br><span class="line"><span class="attr"> domains:</span></span><br><span class="line"><span class="bullet"> -</span> <span class="string">test.hwchiu.com</span></span><br></pre></td></tr></table></figure></p>
<p>這個範例中,我們要先指定該 <code>Certificate</code> 要使用的 <code>Issuers</code>. 這部分要透過 <code>issuerRef</code> 來指定剛剛創立的 <code>ClusterIssuers</code>.<br>接下來透過 <code>secretName</code> 去指定之後創立的 <code>kubernetes secret</code> 的名稱</p>
<p>最後要處理子域名的問題,在這個範例中只有一個,也就是 <code>test.hwchiu.com</code>.<br>所以在 <code>acme</code> 的部分就直接使用先前創立的 <code>ClusterIssuers</code> 內所描述的 <code>DNS01</code> 方式去進行認證</p>
<p>當這一切都準備就緒後就透過 <code>kubectl apply</code> 給部屬到叢集內。</p>
<p>接下來我們可以觀察整體運行的狀況,我們透過 <code>kubctl descriBe certificate demo-creti</code> 可以看一下 <code>certificae</code> 當前的狀態。</p>
<p>一個運行的範例如下<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">Events:</span><br><span class="line"> Type Reason Age From Message</span><br><span class="line"> ---- ------ ---- ---- -------</span><br><span class="line"> Normal CreateOrder 16s cert-manager Created new ACME order, attempting validation... </span><br><span class="line"> Normal IssueCert 15s cert-manager Issuing certificate...</span><br><span class="line"> Normal CertObtained 13s cert-manager Obtained certificate from ACME server </span><br><span class="line"> Normal CertIssued 13s cert-manager Certificate issued successfully</span><br></pre></td></tr></table></figure></p>
<p>當一切都完畢後,就可以透過 <code>kubernetes get secret</code> 看到 <code>cert-manager</code> 已經幫忙創立了一個 <code>kubernetes secret</code>,裡面已經包含了你需要的憑證。<br>如果有使用 <code>ingress</code> 相關的,可以直接在 <code>Ingress</code> 那邊透過 <code>tls</code> 的設定直接使用該 <code>secret</code> 幫你的 <code>Ingress</code> 套上這個憑證囉</p>
<h1 id="Summary-1"><a href="#Summary-1" class="headerlink" title="Summary"></a>Summary</h1><p>使用任何的解決方案前,都要先思考一下到底自己遇到的是什麼問題,再從這個問題延伸去尋找方法來處理。接下來為了要更能夠熟悉該方法所帶來的好處以及壞處,就必須要花時間去瞭解其架構以及原理,最後透過動手操作的方式來實際體驗是否真的能夠解決問題。</p>
<p>本文的出發點很簡單,就是想要有一個解決方案能夠幫忙處理 <code>Ingress</code> 的憑證,然後因為沒錢所以希望使用 <code>Let's Encrypt</code> 的服務,但是礙於該服務所簽署的憑證有效期限很短,所以需要一直更新。</p>
<p>基於這個問題的情況下,我開始研究了 <code>Cert-Manager</code>,並且透過一系列的操作與體驗來理解如何使用 <code>Cert-Manager</code> 解決我的問題。</p>
<p>最後也希望大家都能夠保持類似的態度與想法來解決每一個問題,藉由這些步驟能夠更深刻的體會每個解決方案背後的原理以及設計思緒,將這些概念融入自己的思想之中以不停的成長學習。</p>
</div>
<div class="popular-posts-header">Related Posts</div>
<ul class="popular-posts">
<li class="popular-posts-item">
<div class="popular-posts-title"><a href="/k8s-security-11tips-i.html" rel="bookmark">11個保護你 Kubernetes 集群的技巧與觀念(上)</a></div>
</li>
<li class="popular-posts-item">
<div class="popular-posts-title"><a href="/k8s-security-11tips-ii.html" rel="bookmark">11個保護你 Kubernetes 集群的技巧與觀念(下)</a></div>
</li>
<li class="popular-posts-item">
<div class="popular-posts-title"><a href="/kubernetes-dns-iii.html" rel="bookmark">[Kubernetes] DNS Setting with Dockerd(原始碼分析上)</a></div>
</li>
<li class="popular-posts-item">
<div class="popular-posts-title"><a href="/ingress-1.html" rel="bookmark">Introduction to Kubernetes Ingress (Nginx)</a></div>
</li>
<li class="popular-posts-item">
<div class="popular-posts-title"><a href="/introduce-cni-i.html" rel="bookmark">[Container Network Interface] Bridge Network In Docker</a></div>
</li>
</ul>
<footer class="post-footer">
<div class="post-tags">
<a href="/tags/Kubernetes/" rel="tag"># Kubernetes</a>
<a href="/tags/Security/" rel="tag"># Security</a>
</div>
<div class="post-nav">
<div class="post-nav-next post-nav-item">
<a href="/kubernetes-fedora.html" rel="next" title="Install the kubernetes as single node in Fedora 28">
<i class="fa fa-chevron-left"></i> Install the kubernetes as single node in Fedora 28
</a>
</div>
<span class="post-nav-divider"></span>
<div class="post-nav-prev post-nav-item">
<a href="/tools-ncdu.html" rel="prev" title="NCurses Disk Usage(ncdu)">
NCurses Disk Usage(ncdu) <i class="fa fa-chevron-right"></i>
</a>
</div>
</div>
</footer>
</div>
</article>
</div>
</div>
<div class="comments" id="comments">
<div id="disqus_thread">
<noscript>Please enable JavaScript to view the comments powered by Disqus.</noscript>
</div>
</div>
</div>
<div class="sidebar-toggle">
<div class="sidebar-toggle-line-wrap">
<span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
<span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
</div>
</div>
<aside id="sidebar" class="sidebar">
<div class="sidebar-inner">
<ul class="sidebar-nav motion-element">
<li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
Table of Contents
</li>
<li class="sidebar-nav-overview" data-target="site-overview-wrap">
Overview
</li>
</ul>
<div class="site-overview-wrap sidebar-panel">
<div class="site-overview">
<div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
<img class="site-author-image" itemprop="image"
src="/images/avatar.jpg"
alt="Hwchiu"/>
<p class="site-author-name" itemprop="name">Hwchiu</p>
<div class="site-description motion-element" itemprop="description">kubernetes/SDN/DevOps</div>
</div>
<nav class="site-state motion-element">
<div class="site-state-item site-state-posts">
<a href="/archives/">
<span class="site-state-item-count">157</span>
<span class="site-state-item-name">posts</span>
</a>
</div>
<div class="site-state-item site-state-tags">
<a href="/tags/">
<span class="site-state-item-count">91</span>
<span class="site-state-item-name">tags</span>
</a>
</div>
</nav>
<div class="feed-link motion-element">
<a href="/atom.xml" rel="alternate">
<i class="fa fa-rss"></i>
RSS
</a>
</div>