Files
davidpaulyoung/public/2018/05/23/service_2_service_auth/index.html
2026-05-14 14:06:21 -06:00

791 lines
29 KiB
HTML
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!DOCTYPE html>
<html lang="en-us">
<head><script src="/livereload.js?mindelay=10&amp;v=2&amp;port=1313&amp;path=livereload" data-no-instant defer></script>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script>
(function() {
const autoTheme = false;
if (autoTheme) {
document.documentElement.setAttribute('data-auto-theme', 'true');
}
const theme = localStorage.getItem('cleanwhite-theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
document.documentElement.setAttribute('data-theme', theme);
})();
</script>
<meta property="og:site_name" content="David Young">
<meta property="og:type" content="article">
<meta property="og:image" content="http://localhost:1313//img/2018-05-23-service_2_service_auth/background.jpg">
<meta property="twitter:image" content="http://localhost:1313//img/2018-05-23-service_2_service_auth/background.jpg" />
<meta name="title" content="微服务安全沉思录之二" />
<meta property="og:title" content="微服务安全沉思录之二" />
<meta property="twitter:title" content="微服务安全沉思录之二" />
<meta name="description" content="除来自用户的访问请求以外,微服务应用中的各个微服务相互之间还有大量的访问,根据应用系统数据敏感程度不同,对于系统内微服务的访问也需要进行相应的安全控制。">
<meta property="og:description" content="除来自用户的访问请求以外,微服务应用中的各个微服务相互之间还有大量的访问,根据应用系统数据敏感程度不同,对于系统内微服务的访问也需要进行相应的安全控制。" />
<meta property="twitter:description" content="除来自用户的访问请求以外,微服务应用中的各个微服务相互之间还有大量的访问,根据应用系统数据敏感程度不同,对于系统内微服务的访问也需要进行相应的安全控制。" />
<meta property="og:url" content="http://localhost:1313/2018/05/23/service_2_service_auth/" />
<meta property="twitter:card" content="summary" />
<meta name="keyword" content="Von Balthasar, Scripture, Gravel Riding, Ham Radio, Divine Office, Open Source">
<link rel="shortcut icon" href="/img/favicon.ico">
<title>微服务安全沉思录之二 | David Young Blog</title>
<link rel="canonical" href="/2018/05/23/service_2_service_auth/">
<link rel="stylesheet" href="/css/bootstrap.min.css">
<link rel="stylesheet" href="/css/hugo-theme-cleanwhite.css">
<link rel="stylesheet" href="/css/theme-variables.css">
<link rel="stylesheet" href="/css/zanshang.min.css">
<link rel="stylesheet" href="/css/font-awesome.all.min.css">
<script src="/js/jquery.min.js"></script>
<script src="/js/bootstrap.min.js"></script>
<script src="/js/hux-blog.min.js"></script>
<script src="/js/lazysizes.min.js"></script>
</head>
<nav class="navbar navbar-default navbar-custom navbar-fixed-top">
<div class="container-fluid">
<div class="navbar-header page-scroll">
<button type="button" class="navbar-toggle">
<span class="sr-only">Toggle navigation</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="/">David Young</a>
</div>
<div id="huxblog_navbar">
<div class="navbar-collapse">
<ul class="nav navbar-nav navbar-right">
<li>
<a href="/">All Posts</a>
</li>
<li>
<a href="/categories/life/">life</a>
</li>
<li>
<a href="/categories/tech/">tech</a>
</li>
<li>
<a href="/categories/tips/">tips</a>
</li>
<li><a href="/archive//">ARCHIVE</a></li>
<li><a href="/notes//">NOTES</a></li>
<li><a href="/about//">ABOUT</a></li>
<li>
<a href="/search"><i class="fa fa-search"></i></a>
</li>
<li>
<a href="#" id="theme-toggle" title="Toggle dark mode" style="opacity: 0;">
<i class="fa fa-moon"></i>
<i class="fa fa-sun" style="display: none;"></i>
</a>
</li>
<script>
(function() {
var theme = localStorage.getItem('cleanwhite-theme') ||
(window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light');
var toggleBtn = document.getElementById('theme-toggle');
if (toggleBtn) {
var moonIcon = toggleBtn.querySelector('.fa-moon');
var sunIcon = toggleBtn.querySelector('.fa-sun');
if (theme === 'dark') {
if (moonIcon) moonIcon.style.display = 'none';
if (sunIcon) sunIcon.style.display = 'inline';
toggleBtn.setAttribute('title', 'Switch to light mode');
} else {
if (moonIcon) moonIcon.style.display = 'inline';
if (sunIcon) sunIcon.style.display = 'none';
toggleBtn.setAttribute('title', 'Switch to dark mode');
}
requestAnimationFrame(function() {
toggleBtn.style.transition = 'opacity 0.2s ease';
toggleBtn.style.opacity = '1';
});
}
})();
</script>
</ul>
</div>
</div>
</div>
</nav>
<script>
var $body = document.body;
var $toggle = document.querySelector('.navbar-toggle');
var $navbar = document.querySelector('#huxblog_navbar');
var $collapse = document.querySelector('.navbar-collapse');
$toggle.addEventListener('click', handleMagic)
function handleMagic(e){
if ($navbar.className.indexOf('in') > 0) {
$navbar.className = " ";
setTimeout(function(){
if($navbar.className.indexOf('in') < 0) {
$collapse.style.height = "0px"
}
},400)
}else{
$collapse.style.height = "auto"
$navbar.className += " in";
}
}
document.addEventListener('DOMContentLoaded', function() {
var navLinks = document.querySelectorAll('.navbar-collapse a');
navLinks.forEach(function(link) {
link.addEventListener('click', function() {
if ($navbar.className.indexOf('in') > 0) {
$navbar.className = " ";
setTimeout(function(){
if($navbar.className.indexOf('in') < 0) {
$collapse.style.height = "0px"
}
},400)
}
});
});
});
</script>
<style type="text/css">
header.intro-header {
background-image: url('/img/2018-05-23-service_2_service_auth/background.jpg')
}
</style>
<header class="intro-header" >
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<div class="post-heading">
<div class="tags">
<a class="tag" href="/tags/microservice" title="Microservice">
Microservice
</a>
<a class="tag" href="/tags/security" title="Security">
Security
</a>
</div>
<h1>微服务安全沉思录之二</h1>
<h2 class="subheading">服务间认证与鉴权</h2>
<span class="meta">
Posted by
    &#34;赵化冰&#34;
on
Wednesday, May 23, 2018
</span>
</div>
</div>
</div>
</div>
</header>
<article>
<div class="container">
<div class="row">
<div class="
col-lg-8 col-lg-offset-2
col-md-10 col-md-offset-1
post-container">
<h2 id="服务间认证与鉴权">服务间认证与鉴权</h2>
<p>除来自用户的访问请求以外,微服务应用中的各个微服务相互之间还有大量的访问,包括下述场景:</p>
<ul>
<li>用户间接触发的微服务之间的相互访问<BR>
例如在一个网上商店应用中,用户访问购物车微服务进行结算时,购物车微服务可能需要访问用户评级微服务获取用户的会员级别,以得到用户可以享受购物折扣。</li>
<li>非用户触发的微服务之间的相互访问<BR>
例如数据同步或者后台定时任务导致的微服务之间的相互访问。</li>
</ul>
<p>根据应用系统的数据敏感程度的不同,对于系统内微服务的相互访问可能有不同的安全要求。</p>
<h3 id="对微服务之间的相互访问不进行安全控制">对微服务之间的相互访问不进行安全控制</h3>
<p>在某些场景下,可以假设同一应用中微服务之间的相互访问都是可信的。在这种情况下,应用依赖于内部网络的防火墙及其他网络安全措施来保证安全性。在这种情况对入侵者攻击进入内部网络后没有保护措施。入侵者可以对微服务间的通信进行典型的中间人攻击,例如窃听通信内容,伪造和修改通信数据,甚至假装为一个合法的微服务进行通信。</p>
<h3 id="采用service-account服务账号进行安全控制">采用Service Account(服务账号)进行安全控制</h3>
<p>“内部网络中微服务之间的所有通信都是可信的”这个假设在某些场景下是不成立的,特别是在微服务中保存有用户信息这种非常重要的数据的情况下。将敏感信息直接暴露在内部攻击下的做法是非常危险的。 解决该问题的一种方案是使用服务账号来对微服务之间的相互访问进行控制。</p>
<p>用户权限控制的一个普遍方法是使用”用户账号User Account”来标识一个系统用户并对其进行身份认证和操作鉴权。类似地可以为系统中每一个服务也创建一个账号称为”服务账号(Service Accout)“。 该服务账号表示了微服务的身份,以用于控制该微服务对系统中其它微服务的访问权限,如可以对哪些微服务的哪些资源进行何种操作。当一个微服务访问另一个微服务时,被访问的微服务需要验证访问者的服务账号,以确定其身份和资源操作权限。</p>
<h4 id="spifee标准">SPIFEE标准</h4>
<p><a href="https://spiffe.io/">Secure Production Identity Framework For Everyone (SPIFFE)</a>是一套服务之间相互进行身份识别的标准,主要包含以下内容:</p>
<ul>
<li>SPIFFE ID标准SPIFFE ID是服务的唯一标识实现为统一资源标识&quot;Uniform Resource Identifier (URI)”符。</li>
<li>SVID(SPIFFE Verifiable Identity Document)标准,将SPIFFE ID编码到一个加密的可验证文档中。</li>
<li>颁发/撤销 SVID的一套API标准。</li>
</ul>
<p>SPIFFE SVID目前支持的实现方式是X.509数字证书在x.509 SVID中采用X.509数字证书的SAN(Subject Alternative Name)扩展字段来保存SPIFFE ID。</p>
<h4 id="istio-auth开源实现">Istio Auth开源实现</h4>
<p>Istio服务网格项目的Auth组件实现了SPIFFE标准可以为网格中服务颁发符合SPIFFE SVID标准的证书并为服务提供身份认证细粒度的操作鉴权以及通信加密。Istio的架构如下图所示
<img src="/img/2018-05-23-service_2_service_auth/auth.png" alt="">
</p>
<p>Istio Auth采用了Kubernetes的service account来作为服务标识其SPIFFE ID的格式为spiffe://&lt;domain&gt;/ns/&lt;namespace&gt;/sa/&lt;serviceaccount&gt;,其中各组成部分如下:</p>
<ul>
<li>domain 域名</li>
<li>namespace kubernetes service account所在的Namespace</li>
<li>serviceaccount kubernetes中定义的service account名</li>
</ul>
<p>Istio Auth提供了一个用于颁发证书的CA。在服务部署时CA监听Kubernetes API Server, 为集群中的每一个Service Account创建一对密钥和证书。当Pod创建时Kubernetes根据该Pod关联的Service Account将密钥和证书以Kubernetes Secrets资源的方式加载为Pod的Volume以供Envoy使用。</p>
<p>在服务运行时服务间的通信被Envoy接管Envoy使用证书在服务间进行双向SSL握手验证通信双方服务的身份并提供加密的通信通道。</p>
<h3 id="采用用户身份进行安全控制">采用用户身份进行安全控制</h3>
<p>采用服务账号进行服务间交互的鉴权不能控制到用户粒度的访问权限,这在某些场景下可能出现数据泄露问题。</p>
<p>例如在网上商店应用中用户访问购物车微服务进行结算时购物车微服务需要访问另一个微服务中的用户历史购物数据。如果只采用服务账号对购物车微服务进行安全控制存在用户A通过购物车微服务向后端微服务发起一个获取用户B历史购物数据请求的风险。因为后端的微服务并不能得知发起请求的是哪一个用户因此会不加判断地返回购物车微服务请求的用户历史购物数据。</p>
<p>解决方案是将用户信息从用户直接访问的第一个微服务向后传递到调用链上的每一个微服务,调用链上的每一个微服务都使用该用户信息对用户能访问的资源进行判断。在一个大型的微服务系统中,一个调用链可能会非常长,导致该方案的实现比较复杂。</p>
<p>我们需要根据应用的使用场景,每个微服务中数据的敏感程度来决定选择哪一种服务间安全的实施方式。</p>
<div class="entry-shang text-center">
<p>「真诚赞赏,手留余香」</p>
<button class="zs show-zs btn btn-bred">赞赏支持</button>
</div>
<div class="zs-modal-bg"></div>
<div class="zs-modal-box">
<div class="zs-modal-head">
<button type="button" class="close">×</button>
<span class="author"><a href="http://localhost:1313/"><img src="/img/favicon.png" />David Young</a></span>
<p class="tip"><i></i><span>真诚赞赏,手留余香</span></p>
</div>
<div class="zs-modal-body">
<div class="zs-modal-btns">
<button class="btn btn-blink" data-num="2">2元</button>
<button class="btn btn-blink" data-num="5">5元</button>
<button class="btn btn-blink" data-num="10">10元</button>
<button class="btn btn-blink" data-num="50">50元</button>
<button class="btn btn-blink" data-num="100">100元</button>
<button class="btn btn-blink" data-num="1">任意金额</button>
</div>
<div class="zs-modal-pay">
<button class="btn btn-bred" id="pay-text">2元</button>
<p>使用<span id="pay-type">微信</span>扫描二维码完成支付</p>
<img src="/img/reward/wechat-2.png" id="pay-image"/>
</div>
</div>
<div class="zs-modal-footer">
<label><input type="radio" name="zs-type" value="wechat" class="zs-type" checked="checked"><span ><span class="zs-wechat"><img src="/img/reward/wechat-btn.png"/></span></label>
<label><input type="radio" name="zs-type" value="alipay" class="zs-type" class="zs-alipay"><img src="/img/reward/alipay-btn.png"/></span></label>
</div>
</div>
<script type="text/javascript" src="/js/reward.js"></script>
<hr>
<ul class="pager">
<li class="previous">
<a href="/2018/05/22/user_authentication_authorization/" data-toggle="tooltip" data-placement="top" title="微服务安全沉思录之一">&larr;
Previous Post</a>
</li>
<li class="next">
<a href="/2018/05/23/external_system_auth/" data-toggle="tooltip" data-placement="top" title="微服务安全沉思录之三">Next
Post &rarr;</a>
</li>
</ul>
<link href="https://xxx.xxx.com/dist/Artalk.css" rel="stylesheet" />
<script src="https://xxx.xxx.com/dist/Artalk.js"></script>
<div id="Comments"></div>
<script>
Artalk.init({
el: '#Comments',
pageKey: 'http:\/\/localhost:1313\/2018\/05\/23\/service_2_service_auth\/',
pageTitle: '微服务安全沉思录之二',
server: 'https:\/\/xxx.xxx.com',
site: 'xxx blog',
})
</script>
</div>
<div class="
col-lg-2 col-lg-offset-0
visible-lg-block
sidebar-container
catalog-container">
<div class="side-catalog">
<hr class="hidden-sm hidden-xs">
<h5>
<a class="catalog-toggle" href="#">CATALOG</a>
</h5>
<ul class="catalog-body"></ul>
</div>
</div>
<div class="
col-lg-8 col-lg-offset-2
col-md-10 col-md-offset-1
sidebar-container">
<section>
<hr class="hidden-sm hidden-xs">
<h5><a href="/tags/">FEATURED TAGS</a></h5>
<div class="tags">
<a href="/tags/docker" title="docker">
docker
</a>
<a href="/tags/istio" title="istio">
istio
</a>
<a href="/tags/kubernetes" title="kubernetes">
kubernetes
</a>
<a href="/tags/microservice" title="microservice">
microservice
</a>
<a href="/tags/security" title="security">
security
</a>
<a href="/tags/service-mesh" title="service mesh">
service mesh
</a>
<a href="/tags/tips" title="tips">
tips
</a>
</div>
</section>
<section>
<hr>
<h5>FRIENDS</h5>
<ul class="list-inline">
<li><a target="_blank" href="https://zhaozhihan.com">Linda的博客</a></li>
</ul>
</section>
</div>
</div>
</div>
</article>
<footer>
<div class="container">
<div class="row">
<div class="col-lg-8 col-lg-offset-2 col-md-10 col-md-offset-1">
<ul class="list-inline text-center">
<li>
<a href="mailto:youremail@gmail.com">
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fas fa-envelope fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li>
<a target="_blank" href="/your%20wechat%20qr%20code%20image">
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fab fa-weixin fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li>
<a target="_blank" href="https://github.com/yourgithub">
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fab fa-github fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li>
<a target="_blank" href="https://www.linkedin.com/in/yourlinkedinid">
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fab fa-linkedin fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li>
<a target="_blank" href="https://stackoverflow.com/users/yourstackoverflowid">
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fab fa-stack-overflow fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
<li>
<a href='' rel="alternate" type="application/rss+xml" title="David Young" >
<span class="fa-stack fa-lg">
<i class="fas fa-circle fa-stack-2x"></i>
<i class="fas fa-rss fa-stack-1x fa-inverse"></i>
</span>
</a>
</li>
</ul>
<p class="copyright text-muted">
Copyright &copy; David Young 2026
<br>
<a href="https://themes.gohugo.io/hugo-theme-cleanwhite">CleanWhite Hugo Theme</a> by <a href="https://zhaohuabing.com">Huabing</a> |
<iframe
style="margin-left: 2px; margin-bottom:-5px;"
frameborder="0" scrolling="0" width="100px" height="20px"
src="https://ghbtns.com/github-btn.html?user=zhaohuabing&repo=hugo-theme-cleanwhite&type=star&count=true" >
</iframe>
</p>
</div>
</div>
</div>
</footer>
<script>
function loadAsync(u, c) {
var d = document, t = 'script',
o = d.createElement(t),
s = d.getElementsByTagName(t)[0];
o.src = u;
if (c) { o.addEventListener('load', function (e) { c(null, e); }, false); }
s.parentNode.insertBefore(o, s);
}
</script>
<script>
if($('#tag_cloud').length !== 0){
loadAsync("/js/jquery.tagcloud.js",function(){
$.fn.tagcloud.defaults = {
color: {start: '#bbbbee', end: '#0085a1'},
};
$('#tag_cloud a').tagcloud();
})
}
</script>
<script>
(function() {
function updateTagcloudColors() {
const isDark = document.documentElement.getAttribute('data-theme') === 'dark';
const startColor = isDark ? '#808080' : '#bbbbee';
if($('#tag_cloud').length !== 0 && $.fn.tagcloud) {
$.fn.tagcloud.defaults = {
color: {start: startColor, end: '#0085a1'},
};
$('#tag_cloud a').tagcloud();
}
}
$(document).ready(function() {
updateTagcloudColors();
});
const observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if (mutation.attributeName === 'data-theme') {
updateTagcloudColors();
}
});
});
observer.observe(document.documentElement, {
attributes: true,
attributeFilter: ['data-theme']
});
})();
</script>
<script>
loadAsync("https://cdn.jsdelivr.net/npm/fastclick@1.0.6/lib/fastclick.min.js", function(){
var $nav = document.querySelector("nav");
if($nav) FastClick.attach($nav);
})
</script>
<script src="/js/theme-toggle.js"></script>
<script type="text/javascript">
function generateCatalog(selector) {
_containerSelector = 'div.post-container'
var P = $(_containerSelector), a, n, t, l, i, c;
a = P.find('h1,h2,h3,h4,h5,h6');
$(selector).html('')
a.each(function () {
n = $(this).prop('tagName').toLowerCase();
i = "#" + $(this).prop('id');
t = $(this).text();
c = $('<a href="' + i + '" rel="nofollow" title="' + t + '">' + t + '</a>');
l = $('<li class="' + n + '_nav"></li>').append(c);
$(selector).append(l);
});
return true;
}
generateCatalog(".catalog-body");
$(".catalog-toggle").click((function (e) {
e.preventDefault();
$('.side-catalog').toggleClass("fold")
}))
loadAsync("\/js\/jquery.nav.js", function () {
$('.catalog-body').onePageNav({
currentClass: "active",
changeHash: !1,
easing: "swing",
filter: "",
scrollSpeed: 700,
scrollOffset: 0,
scrollThreshold: .2,
begin: null,
end: null,
scrollChange: null,
padding: 80
});
});
</script>
</body>
</html>