How to automatically start supervisord on Linux (Ubuntu)

refer: http://serverfault.com/questions/96499/how-to-automatically-start-supervisord-on-linux-ubuntu

sudo chmod +x /etc/init.d/supervisord
and to automatically schedule it, do

sudo update-rc.d supervisord defaults
Make ensure correct pid in /etc/supervisord.conf which is mapped in /etc/init.d/supervisord

example: pidfile=/var/run/supervisord.pid
Stop and Start work properly

service supervisord stop
service supervisord start

Ubuntu 14.04上安装smokeping

Smokeping是网络服务器监控的利器。在运维中,如果利用smokeping给出统计数据,那么结果将令人信服。无奈smokeping上手并不十分容易,网络上的知识库都是只言片语。下面是我的安装记录,仅供参考。

安装smokeping及其依赖包
$ sudo apt-get install smokeping curl libauthen-radius-perl libnet-ldap-perl libnet-dns-perl libio-socket-ssl-perl libnet-telnet-perl libsocket6-perl libio-socket-inet6-perl apache2

  •  MASTER模式

配置apache

cd /etc/apache2/conf-available
sudo ln -s ../../smokeping/apache2.conf smokeping.conf

请注意apache配置

$ cd /etc/smokeping
$ cat apache2.conf

ScriptAlias /smokeping/smokeping.cgi /usr/lib/cgi-bin/smokeping.cgi
Alias /smokeping /usr/share/smokeping/www
Directory "/usr/share/smokeping/www">
Options FollowSymLinks

sudo a2enconf smokeping
sudo a2enmod cgid

编辑/etc/smokeping/slavesecrets.conf 和 /etc/smokeping/slave-secrets。其中key是密码,slave-server是”被监控者”的名字。

$ cd /etc/smokeping/
$ echo key > slavesecrets.conf
$ echo slave-server:key >> smokeping_secrets

编辑/etc/smokeping/config.d/Slaves

+slave-server
display_names=slave-server
color=ff0000

编辑/etc/smokeping/config.d/Targets

++ Site
menu = Corp
title = CORP – slave server
host = slave-server.com # slave server的地址
slaves = slave-server

编辑/etc/default/smokeping

$ cat /etc/default/smokeping
MODE=master

编辑权限(此步骤特别注意,否则会出现莫名的提示)

$ ls -la /etc/smokeping
total 48
drwxr-xr-x 3 root root 4096 Jul 6 23:57 .
drwxr-xr-x 128 root root 12288 Jul 6 15:45 ..
-rw-r–r– 1 root root 191 Jul 6 17:21 apache2.conf
-rw-r–r– 1 root root 2031 Jan 29 2014 basepage.html
-rw-r–r– 1 root root 290 Jan 29 2014 config
drwxr-xr-x 2 root root 4096 Jul 7 00:16 config.d
-rw——- 1 smokeping root 21 Jul 6 23:56 slavesecrets.conf
-rw-r–r– 1 root root 1564 Jan 29 2014 smokemail
-rw——- 1 www-data www-data 87 Jul 6 22:21 smokeping_secrets
-rw-r–r– 1 root root 3815 Jan 29 2014 tmail
以上文件中smokeping_secrets是给web server用的(所有者应该是:www-data),因为slave端是通过web server取得密匙。

  • SLAVE模式

编辑/etc/default/smokeping

$ cat /etc/default/smokeping
#

select master or slave mode

MODE=slave

in master mode, the rest of the configuration is in

/etc/smokeping/config

#

in slave mode, uncomment and set the following variables too

see smokeping(1)

#

Mandatory configuration

MASTER_URL=http://slave-server.com/smokeping/smokeping.cgi ## 该url对照master的apache conf档配置
SHARED_SECRET=/etc/smokeping/slavesecrets.conf
#

Optional configuration

SLAVE_NAME=slave-server

同样的,修改slavesecrets.conf

$ echo key > slavesecrets.conf

同样的,编辑权限(此步骤也要特别注意,否则会出现莫名的提示)

$ ls -la /etc/smokeping
yiwang@localhost:/etc/smokeping$ ls -la
total 48
drwxr-xr-x 3 root root 4096 Jul 6 23:40 .
drwxr-xr-x 140 root root 12288 Jul 6 17:05 ..
-rw-r–r– 1 root root 191 Jan 29 2014 apache2.conf
-rw-r–r– 1 root root 2031 Jan 29 2014 basepage.html
-rw-r–r– 1 root root 290 Jan 29 2014 config
drwxr-xr-x 2 root root 4096 Jul 6 22:31 config.d
-rw——- 1 smokeping smokeping 21 Jul 6 23:40 slavesecrets.conf
-rw-r–r– 1 root root 1564 Jan 29 2014 smokemail
-rw-rw-rw- 1 smokeping smokeping 21 Jul 6 23:40 smokeping_secrets
-rw-r–r– 1 root root 3815 Jan 29 2014 tmail

Cloud Contacts

https://itunes.apple.com/us/app/cloud-contacts/id477970381?ls=1&mt=8

Make your iPhone support Google contacts, right now!

Google Contacts is the default address book provider for all of Google software and devices(Gmail, Android,etc.).
(The website of Google Contacts: http://www.google.com/contacts.)

Feature List:
1.It is an independent address book App on iOS, and it has a convenient UI likes Apple\’s contacts.
2.It supports multi-account. You can hold multiple Google accounts (unlimited count) in the App.
3.It can migrate contacts content to your iOS devices.
4.It does not gather any personal informations. All of content data only stores in your service provider(Google) and devices.
5.It was developed base on Google Data Protocol (Google native API), and provides more compatibility.

Other Features:
1.Direct call your contacts from iPhone.
2.Synchronous data arranged in groups, do not have to worry about the address book classification confusion.
3.You can edit your contact content locally, and also synchronise to iOS contacts(by using migration assistant).
4.On \”Summary\” page, you can send email and make phone calls.
5.Address book data on the service providers is a read-only access by default, delete the account and group does not affect the original content.

Possible Issues:
1.Google updated the password authentication methods, provides the \”2-step verification”. In order to avoid the password validation fail, you should set an application special password for this App if to use this function. Refer to:
https://www.google.com/settings/security
https://accounts.google.com/b/1/IssuedAuthSubTokens
2.[For Chinese mainland user] Google contacts are downloaded for too long time and timeout. Google’s servers are in the United States, subject to the environment of the Chinese network, the Google’s servers are often blocked. The solution: When a timeout occurs, retry more times.
3.I have to remove the Facebook contacts supporting, because Facebook does not permit other Apps to access contacts.

git subtree vs submodule

Alternatives To Git Submodule: Git Subtree

\"\"

The Internet is full of articles on why you should not use Git submodules. I mostly agree, although I am not so harsh in my evaluation. As I explained in a previous postsubmodules are useful for a few use cases but have several drawbacks.

Are there alternatives? The answer is: yes! There are (at least) two tools that can help track the history of software dependencies in your project while allowing you to keep using git:

In this post I will be looking at git subtree and show why it is an improvement – albeit not perfect – over git submodule.

As a working example I run to my usual use case. How do I easily store and keep up to date the vim plugins used in my dotfiles?

Why use subtree instead of submodule?

There are several reasons why you might find subtree better to use:

  • Management of a simple workflow is easy.
  • Older version of git are supported (even before v1.5.2).
  • The sub-project’s code is available right after the clone of the super project is done.
  • subtree does not require users of your repository to learn anything new, they can ignore the fact that you are using subtree to manage dependencies.
  • subtree does not add new metadata files like submodules doe (i.e. .gitmodule).
  • Contents of the module can be modified without having a separate repository copy of the dependency somewhere else.

In my opinion the drawbacks are acceptable:

  • You must learn about a new merge strategy (i.e. subtree).
  • Contributing code back upstream for the sub-projects is slightly more complicated.
  • The responsibility of not mixing super and sub-project code in commits lies with you.

How to use git subtree?

git subtree is available in stock version of git available since May 2012 – 1.7.11+. The version installed by homebrew on OSX already has subtree properly wired but on some platforms you might need to follow the installation instructions.

Let me show you the canonical example of tracking a vim plug-in using git subtree.

The quick and dirty way without remote tracking

If you just want a couple of one liners to cut and paste just read this paragraph.

First add the subtree at a specified prefix folder:

1
git subtree add –prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git master –squash

(The common practice is to not store the entire history of the sub-project in your main repository, but If you want to preserve it just omit the –squash flag.)

The above command produces this output:

1
2
3
4
5
6
7
8
9
10
git fetch https://bitbucket.org/vim-plugins-mirror/vim-surround.git master
warning: no common commits
remote: Counting objects: 338, done.
remote: Compressing objects: 100% (145/145), done.
remote: Total 338 (delta 101), reused 323 (delta 89)
Receiving objects: 100% (338/338), 71.46 KiB, done.
Resolving deltas: 100% (101/101), done.
From https://bitbucket.org/vim-plugins-mirror/vim-surround.git
* branch            master     -} FETCH_HEAD
Added dir \’.vim/bundle/tpope-vim-surround\’

As you can see this records a merge commit by squashing the whole history of the vim-surroundrepository into a single one:

1
2
1bda0bd [3 minutes ago] (HEAD, stree) Merge commit \’ca1f4da9f0b93346bba9a430c889a95f75dc0a83\’ as \’.vim/bundle/tpope-vim-surround\’ [Nicola Paolucci]
ca1f4da [3 minutes ago] Squashed \’.vim/bundle/tpope-vim-surround/\’ content from commit 02199ea [Nicola Paolucci]

If after a while you want to update the code of the plugin from the upstream repository you can just subtree pull:

1
git subtree pull –prefix .vim/bundle/tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git master –squash

This is very quick and painless but the commands are slightly lengthy and hard to remember. We can make the commands shorter by adding the sub-project as a remote.

Adding the sub-project as a remote

Adding the subtree as a remote allows us to refer to it in shorter form:

1
git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

Now we can add the subtree (as before), but now we can refer to the remote in short form:

1
git subtree add –prefix .vim/bundle/tpope-vim-surround tpope-vim-surround master –squash

The command to update the sub-project at a later date becomes:

1
2
git fetch tpope-vim-surround master
git subtree pull –prefix .vim/bundle/tpope-vim-surround tpope-vim-surround master –squash

Contributing back to upstream

We can freely commit our fixes to the sub-project in our local working directory now.

When it’s time to contribute back to the upstream project we need to fork the project and add it as another remote:

1
git remote add durdn-vim-surround ssh://git@bitbucket.org/durdn/vim-surround.git

Now we can use the subtree push command like the following:

1
2
3
4
5
6
7
8
9
10
git subtree push –prefix=.vim/bundle/tpope-vim-surround/ durdn-vim-surround mastergit push using:  durdn-vim-surround master
Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 308 bytes, done.
Total 3 (delta 2), reused 0 (delta 0)
To ssh://git@bitbucket.org/durdn/vim-surround.git
02199ea..dcacd4b  dcacd4b21fe51c9b5824370b3b224c440b3470cb -} master

After this we’re ready and we can open a pull-request to the maintainer of the package.

Without using the subtree command

git subtree is different from the subtree merge strategy. You can still use the merge strategy even if for some reason git subtree is not available. Here is how you would go about it:

Add the dependency as a simple git remote:

1
git remote add -f tpope-vim-surround https://bitbucket.org/vim-plugins-mirror/vim-surround.git

Before reading the contents of the dependency into the repository it’s important to record a merge so that we can track the entire tree history of the plug-in up to this point:

1
git merge -s ours –no-commit tpope-vim-surround/master

Which outputs:

1
Automatic merge went well; stopped before committing as requested

We then read the content of the latest tree-object in the plugin repository into our working directory ready to be committed:

1
git read-tree –prefix=.vim/bundle/tpope-vim-surround/ -u tpope-vim-surround/master

Now we can commit (and it will be a merge commit that will preserve the history of the tree we read):

1
2
3
git ci -m\”[subtree] adding tpope-vim-surround\”[stree 779b094] [subtree] adding tpope-vim-surround

When we want to update the project we can now pull using the subtree merge strategy:

1
git pull -s subtree tpope-vim-surround master

Conclusions

After having used submodule for a while I appreciate git subtree much more, lots of submodule problems are superseded and solved by subtree. As usual, with all things git, there is a learning curve to make the most of the feature.

Follow me @durdn and the awesome @AtlDevtools team for more Git rocking.

Android 安全机制

1 Android 安全机制概述

Android 是一个权限分离的系统 。 这是利用 Linux 已有的权限管理机制,通过为每一个 Application 分配不同的 uid 和 gid , 从而使得不同的 Application 之间的私有数据和访问( native 以及 java 层通过这种 sandbox 机制,都可以)达到隔离的目的 。 与此 同时, Android 还 在此基础上进行扩展,提供了 permission 机制,它主要是用来对 Application 可以执行的某些具体操作进行权限细分和访问控制,同时提供了 per-URI permission 机制,用来提供对某些特定的数据块进行 ad-hoc 方式的访问。

1.1 uid 、 gid 、 gids

Android 的权限分离的基础是建立在 Linux 已有的 uid 、 gid 、 gids 基础上的 。

  • UID 。 Android 在 安装一个应用程序,就会为 它 分配一个 uid (参考 PackageManagerService 中的 newUserLP 实现)。其中普通 A ndroid 应用程序的 uid 是从 10000 开始分配 (参见 Process.FIRST_APPLICATION_UID ), 10000 以下是系统进程的 uid 。
  • GID 。对 于普通应用程序来说, gid 等于 uid 。由于每个应用程序的 uid 和 gid 都不相同, 因此不管是 native 层还是 java 层都能够达到保护私有数据的作用 。
  • GIDS 。 gids 是由框架在 Application 安装过程中生成,与 Application 申请的具体权限相关。 如果 Application 申请的相应的 permission 被 granted ,而且 中有对应的 gid s , 那么 这个 Application 的 gids 中将 包含这个 gid s 。

uid gid gids 的 详细 设置过程: 请参考 ActivityManagerService 中的 startProcessLocked 。在通过 zygote 来启动一个 process 时,直接将 uid 传给 给了 gid 。再通过 zygote 来 fork 出新的进程( zygote.java 中的forkAndSpecialize ),最终在 native 层( dalvik_system_zygote.c )中的 forkAndSpecializeCommon 中通过 linux 系统调用来进行 gid 和 uid 和 gids 的设置。

1.2 permission

一个权限主要包含三个方面的信息:权限的名称;属于的权限组;保护级别。一个权限组是指把权限按照功能分成的不同的集合。每一个权限组包含若干具体权限,例如在 COST_MONEY 组中包含 android.permission.SEND_SMS , android.permission.CALL_PHONE 等和费用相关的权限。

每个权限通过 protectionLevel 来标识保护级别: normal , dangerous , signature , signatureorsystem 。不同的保护级别代表了程序要使用此权限时的认证方式。 normal 的权限只要申请了就可以使用; dangerous 的权限在安装时需要用户确认才可以使用; signature 和 signatureorsystem 的权限需要使用者的 app 和系统使用同一个数字证书。

Package 的权限信息主要 通过在 AndroidManifest.xml 中通过一些标签来指定。如 <permission> 标签, <permission-group> 标签 <permission-tree> 等标签。如果 package 需要申请使用某个权限,那么需要使用 <use-permission> 标签来指定。

Android permission 管理机制

2.1 Framework permission 机制

2.1.1 安装入口

permission 的初始化,是指 permission 的向系统申请,系统进行检测并授权,并建立相应的数据结构。绝大多数的情况下 permission 都是从一个 package 中扫描所得,而这发生在 package 安装和升级的时候。一般有如下几种 安装入口:

  • packageInstaller , package 被下载安装时会触发使用。 packageInstaller 会通过 AppSecurityPermissions 来检查 dangerous 的权限,并对用户给出提示。
  • pm 命令 。
  • adb install 。最终还是 调用 pm install 来安装 apk 包。
  • 拷贝即安装。 PackageManagerService 中使用 AppDirObserver 对 /data/app/ 进行监视 ,如果有拷贝即触发安装。

这些安装方式 最终都会通过调用 PackageManagerService 中的函数来完成程序的安装。

2.1.2 permission 创建

第一步,从 AndroidManifest.xml 中提取 permission 信息。主要提取如下信息:

² shared uid 指定与其它 package 共享同一个 uid  ² permission 提取 permissions 标签指定属性。它使用 permissionInfo 来描述一个权限的基本信息。需要指定 protectedLevel 信息,并指定所属 group 信息。它将被添加到这个 package 的 permissions这个 list 结构中。 ² permission-tree 提取 permissions-tree 标签属性。 permissions-tree 也通过 permissionInfo 来描述,并被添加到 package 的 permissions 这个 list 结构中。 permission-tree 只是一个名字空间,用来向其中动态添加一些所谓 Dynamic 的 permission ,这些 permission 可以动态修改。这些 permission 名称要以 permission-tree 的名称开头。它本身不是一种权限,没有 protectedLevel 和所属group 。只是保存了所属的 packge 和权限名(带有 package 前缀的)。 ² permission-group 定义 permission 组信息,用 PermissionGroup 表示。本身不代表一个权限,会添加进入 package 的 permissionGroups 这个 list 中。 ² uses-permission 定义了 package 需要申请的权限名。将权限名添加到 package 的 requestedPermissions 这个 list 中。 ² adopt-permissions 将该标签指定的 name 存入 package 的 mAdoptPermissions 这个 list 中。 Name 指定了这个 package 需要从 name 指定的 package 进行权限领养。在 system package 进行升级时使用。 第二步。获取 Package 中的证书,验证,并将签名信息保存在 Package 结构中。 1. 如果该 package 来自 system img (系统 app ),那么只需要从该 Package 的 AndroidManifest.xml 中获取签名信息,而无需验证其完整性。但是如果这个 package 与其它 package 共享一个 uid ,那么这个共享 uid 对应的 sharedUser 中保存的签名与之不一致,那么签名验证失败。 2. 如果是普通的 package ,那么需要提取证书和签名信息,并对文件的完成性进行验证。 第三步。如果是普通的 package ,那么清除 package 的 mAdoptPermissions 字段信息(系统 package 升级才使用)。

第四步。如果在 AndroidManifest.xml 中指定了 shared user ,那么先查看全局 list 中( mSharedUsers )是否该 uid 对应的 SharedUserSetting 数据结构,若没有则新分配一个 uid ,创建 SharedUserSetting 并保存到全局全局 list ( mSharedUsers )中。 mUserIds 保存了系统中已经分配的 uid 对应的 SharedUserSetting 结构。每次分配时总是从第一个开始轮询,找到第一个空闲的位置 ,然后加上 FIRST_APPLICATION_UID 即可。 第五步。创建 PackageSettings 数据结构。并将 PackageSettings 与 SharedUserSetting 进行绑定。其中 PackageSettings 保存了 SharedUserSetting 结构;而 SharedUserSetting 中会使用 PackageSettings 中的签名信息填充自己内部的签名信息,并将 PackageSettings 添加到一个队列中,表示 PackageSettings 为其中的共享者之一。 在创建时,首先会以 packageName 去全局数据结构 mPackages 中查询是否已经有对应的 PackageSettings 数据结构存在。如果已经存在 PackageSettings 数据结构(比如这个 package 已经被 uninstall ,但是还没有删除数据,此时 package 结构已经被释放)。那么比较该 package 中的签名信息(从 AndroidManifest 中扫描得到)与 PackageSettings 中的签名信息是否匹配。如果不匹配但是为 system package ,那么信任此 package ,并将 package 中的签名信息更新到已有的 PackageSettings 中去,同时如果这个 package 与其它 package 共享了 uid ,而且 shared uid 中保存的签名信息与当前 package 不符,那么签名也验证失败。 第六步。如果 mAdoptPermissions 字段不为空,那么处理 permission 的领养(从指定的 package 对应的 PackageSettings 中,将权限的拥有者修改为当前 package ,一般在 system app 升级的时候才发生,在此之前需要验证当被领养的 package 已经被卸载,即检查 package 数据结构是否存在)。

第七步。添加自定义权限。将 package 中定义的 permissionGroup 添加到全局的列表 mPermissionGroups 中去;将 package 中定义的 permissions 添加到全局的列表中去(如果是 permission-tree 类型,那么添加到 mSettings.mPermissionTrees ,如果是一般的 permission 添加到 mSettings.mPermissions 中)。

第八步。清除不一致的 permission 信息。 1. 清除不一致的 permission-tree 信息。如果该 permission-tree 的 packageSettings 字段为空,说明还未对该 package 进行过解析(若代码执行到此处时 packageSettings 肯定已经被创建过),将其 remove 掉。如果 packageSettings 不为空,但是对应的 package 数据结构为空(说明该 package 已经被卸载,但数据还有保留),或者 package 数据结构中根本不含有这个permission-tree ,那么将这个 permission-tree 清除。 2. 清除不一致的 permission 信息。如果 packageSettings 或者 package 结构为空(未解析该 package 或者被卸载,但数据有保留),或者 package 中根本没有定义该 permission ,那么将该 permission 清除。 第九步。对每一个 package 进行轮询,并进行 permission 授权。 1. 对申请的权限进行检查,并更新 grantedPermissions 列表 2. 如果其没有设置 shared user id ,那么将其 gids 初始化为 mGlobalGids ,它从 permission.xml 中读取。 3. 遍历所有申请的权限,进行如下检查 )如果是该权限是 normal 或者 dangerous 的。通过检查。 )如果权限需要签名验证。如果签名验证通过。还需要进行如下检查  如果程序升级,而且是 system package 。那么是否授予该权限要看原来的 package 是否被授予了该权限。如果被授予了,那么通过检查,否则不通过。  如果是新安装的。那么检查通过。 4. 如果 中检查通过,那么将这个 permission 添加到 package 的 grantedPermissions 列表中,表示这个 permission 申请成功( granted )。申请成功的同时会将这个申请到的 permission的 gids 添加到这个 package 的 gids 中去。 5. 将 permissionsFixed 字段标准为 ture ,表示这个 packge 的 permission 进行过修正。后续将禁止对非 system 的 app 的权限进行再次修正。

2.1.3 Dynamic permission 的管理

PackageManagerService 提供了 addPermission/ removePermission 接口用来动态添加和删除一些权限。但是这些权限必须是所谓的动态权限( BasePermission.TYPE_DYNAMIC )。

一个 Package 如果要添加 Dynamic permissions ,首先必须要在 manifest 中申明 <permission-tree> 标签,它实际上是一个权限的名字空间(例如,“ com.foo.far ”这个权限就是 permission-tree “com.foo ”的成员),本身不是一个权限。一个 Package 只能为自己的 permission-tree 或者拥有相同的 uid 的 package 添加或者删除权限。

Package 不能够通过这种接口去修改在 manifest 中静态申请的权限,否则抛出异常。 首先查找这个 permission 在全局 permission 列表 mSettings.mPermissions 中是否存在。如果存在,而且类型为 BasePermission.TYPE_DYNAMIC 那么根据传入的权限信息修改全局表中的权限信息,并触发 permissions.xml 的持久化。 如果在全局的 permission 列表 mSettings.mPermissions 中没有找到,先找到这个 permission 所在 permissionTree ,然后添加到全局 permission 列表 mSettings.mPermissions 中去,并触发 permissions.xml 的持久化。

2.1.4 Uri permission 的管理

下面两个 接口 主要用于 Uri permission 的管理 (其实现在 ActivityManagerService 中)。 // 为指定的 uid 和 targetPkg 添加对某个 content Uri 的读或者写权限。 public void grantUriPermission(IApplicationThread caller, String targetPkg, Uri uri, int mode) throws RemoteException; // 清除所有通过 grantUriPermission 对某个 Uri 授予的权限。 public void revokeUriPermission(IApplicationThread caller, Uri uri, int mode) throws RemoteException; grantUriPermission 主要的实现过程分析。 grantUriPermission 分析: 1. 验证 caller 的 ProcessRecord 和 targetPkg 不为空。否则检测不通过。 2. 验证所请求的 mode 为 Intent.FLAG_GRANT_READ_URI_PERMISSION 或者为 Intent.FLAG_GRANT_WRITE_URI_PERMISSION ,否则不通过。 3. 确保参数 Uri 是一个 content Uri 。否则,则检测不通过。 4. 通过 Uri 得到目标 ContentProvider ,如果不存在,则检测不通过。 5. 从 PackageManagerService 中获得 targetPkg 对应的 uid  6. 检查 target uid 所对应的 package 是否真正需要这个权限? 先判断要申请的是读还是写权限,然后查看对应的 ContentProvider 中对应的 readPermission writePermission 字段是否保存了权限名称。 如果该字段不为空,则以 target uid 和该权限名去PackageManagerService 中去查找该 uid 是否被 granted 了该权限。如果已经获得了该权限,那么无需再去为这个 Activity 去申请这个 Uri 权限了,返回。否者继续执行如下操作。 7. 检查这个 ContentProvider 的 grantUriPermissions 开关变量,是否允许对其它 package 进行权限的 grant 操作。如果禁止,那么抛出异常。 8. 检查这个 ContentProvider 是否设置了 Uri 的过滤类型 uriPermissionPatterns ,如果设置了过滤类型,则将需要申请权限的 Uri 与之匹配。匹配不同过,则抛出异常。 9. 检查调用者自己是否有权限访问这个 Uri 。如果没有,抛出异常。 10. 从 mGrantedUriPermissions 中取得 target uid 对应的 HashMap<Uri, UriPermission> 数据结构。用 target uid 和 Uri 生成 UriPermission 并保存在 mGrantedUriPermissions 中。 revokeUriPermission 实现分析。 找到该 Uri 对应的 ContentProvider ,然后删除 mGrantedUriPermissions 中与 Uri 对应的所有权限。

2.2 permission 的动态检查

这里的动态检查是指是 package 在程序运行过程中进行某些操作或者数据访问时才进行的 check ,与之对应的是应用程序安装或者升级时 PackageManagerService 通过扫描包中的静态权限信息相对应。

系统与权限 检查 相关的机制的实现主要集中在 PackageManagerService 和 ActivityManagerService 中。 ActivityManagerService 主要负责的是底层的 uid 层次的身份检查; PackageManagerService 则维护了 uid 到自己拥有的和被授予的权限的一张表。在通过 ActivityManagerService 的身份检查后, PackageManagerService 根据请求者的 uid 来查看这张表,判断其是否具有相应的权限。

除此之外, per-URI permission 机制的实现也需要一张表,它维护在 ActivityManagerService 中,它建立了从 content URI 到被授权访问这个 URI 的 component 之间的映射。但是它也需要借助 PackageManagerService 的机制来辅助实现。

2.2.1 framework 提供的接口

Android framework 中提供了一些接口用来对外来的访问(包括自己)进行权限检查 。 这些接口 主要通过 ContextWrapper 提供,具体实现在 ContextImpl 中 。如果 package 接受到外来访问者的操作请求,那么可以调用这些接口进行权限检查。一般情况下可以把这些接口的检查接口分为两种,一种是返回错误,另一种是抛出异常。

主要包含如下几组:

n permission 和 uid 检查 API

下面这一组接口主要用来检查某个调用(或者是其它 package 或者是自己)是否拥有访问某个 permission 的权限。参数中 pid 和 uid 可以指定,如果没有指定,那么 framework 会通过 Binder 来获取调用者的 uid 和 pid 信息,加以填充。返回值为 PackageManager.PERMISSION_GRANTED 或者 PackageManager.PERMISSION_DENIED 。 public int checkPermission(String permission, int pid, int uid) // 检查某个 uid 和 pid 是否有 permission 权限 public int checkCallingPermission(String permission) // 检查调用者是否有 permission 权限,如果调用者是自己那么返回 PackageManager.PERMISSION_DENIED public int checkCallingOrSelfPermission(String permission) // 检查自己或者其它调用者是否有 permission 权限 下面这一组和上面类似,如果遇到检查不通过时,会抛出异常,打印消息 。 public void enforcePermission(String permission, int pid, int uid, String message) public void enforceCallingPermission(String permission, String message) public void enforceCallingOrSelfPermission(String permission, String message) n per-URI 检查 API

为某个 package 添加访问 content Uri 的读或者写权限。 public void grantUriPermission(String toPackage, Uri uri, int modeFlags) public void revokeUriPermission(Uri uri, int modeFlags) 检查某个 pid 和 uid 的 package 是否拥有 uri 的读写权限,返回值表示是否被 granted 。 public int checkUriPermission(Uri uri, int pid, int uid, int modeFlags) public int checkCallingUriPermission(Uri uri, int modeFlags) public int checkCallingOrSelfUriPermission(Uri uri, int modeFlags) public int checkUriPermission(Uri uri, String readPermission,String writePermission, int pid, int uid, int modeFlags) 检查某个 pid 和 uid 的 package 是否拥有 uri 的读写权限,如果失败则抛出异常,打印消息 。 public void enforceUriPermission(Uri uri, int pid, int uid, int modeFlags, String message) public void enforceCallingUriPermission(Uri uri, int modeFlags, String message) public void enforceCallingOrSelfUriPermission(Uri uri, int modeFlags, String message) public void enforceUriPermission(Uri uri, String readPermission, String writePermission,int pid, int uid, int modeFlags, String message)

2.2.2 实现分析

ContextImpl.java 中提供的 API ,其实都是由 ActivityManagerService 中的如下几个接口进行的封装。 public int checkPermission(String permission, int pid, int uid) throws RemoteException; // 主要用于一般的 permission 检查 public int checkUriPermission(Uri uri, int pid, int uid, int mode) throws RemoteException; // 主要用于 Content Uri 的 permission 检查 n checkPermission 的实现分析 1. 如果传入的 permission 名称为 null ,那么返回 PackageManager.PERMISSION_DENIED  2. 判断调用者 uid 是否符合要求  ) 如果 uid 为 ,说明是 root 权限的进程,对权限不作控制。 ) 如果 uid 为 system server 进程的 uid ,说明是 system server ,对权限不作控制。 ) 如果是 ActivityManager 进程本身,对权限不作控制。 )如果调用者 uid 与参数传入的 req uid 不一致,那么返回 PackageManager.PERMISSION_DENIED  3. 如果通过 的检查后,再 调用 PackageManagerService.checkUidPermission ,判断 这个 uid 是否拥有相应的权限,分析如下  ) 首先它通过调用 getUserIdLP ,去 PackageManagerService.Setting.mUserIds 数组中,根据 uid 查找 uid (也就是 package )的权限列表。一旦找到,就表示有相应的权限。 ) 如果没有找到,那么再去 PackageManagerService.mSystemPermissions 中找。这些信息是启动时,从 /system/etc/permissions/platform.xml 中读取的。这里记录了一些系统级的应用的 uid 对应的 permission  )返回结果  n 同样 checkUriPermission 的实现 主要在 ActivityManagerService 中,分析如下: 1. 如果 uid 为 ,说明是 root 用户,那么不控制权限。 2. 否则,在 ActivityManagerService 维护的 mGrantedUriPermissions 这个表中查找这个 uid 是否含有这个权限,如果有再检查其请求的是读还是写权限。

3 Android 签名机制

关于签名机制,其实分两个阶段。

包扫描阶段需要进行完整性和证书的验证。普通 package 的签名和证书是必须要先经过验证的。具体做法是对 manifest 下面的几个文件进行完整性检查。完整性检查包括这个 jar 包中的所有文件。如果是系统 package 的话,只需要使用 AndroidMenifest.xml 这个文件去提取签名和验证信息就可以了。

在权限创建阶段。如果该 package 来自 system img (系统 app ),那么 trust it ,而且使用新的签名信息去替换就的信息。前提是如果这个 package 与其它 package 共享一个 uid ,那么这个共享 uid 对应的 sharedUser 中保存的签名与之不一致,那么签名验证失败。有些时候系卸载一个 app ,但是不删除数据,那么其 PackageSettings 信息会保留,其中会保存签名信息。这样再安装是就会出现不一致。

3.1 Android Package 签名原理

android 中系统和 app 都是需要签名的。可以自己通过 development/tools/make_key 来生成公钥和私钥。

android 源代码中提供了工具 ./out/host/linux-x86/framework/signapk.jar 来进行手动签名。签名的主要作用在于限制对于程序的修改仅限于同一来源。系统中主要有两个地方会检查。如果是程序升级的安装,则要检查新旧程序的签名证书是否一致,如果不一致则会安装失败;对于申请权限的 protectedlevel 为 signature 或者 signatureorsystem 的,会检查权限申请者和权限声明者的证书是否是一致的。签名相关文件可以从 apk 包中的 META-INF 目录下找到。

signapk.jar 的源代码在 build/tools/signapk ,签名主要有以下几步:

l 将除去 CERT.RSA , CERT.SF , MANIFEST.MF 的所有文件生成 SHA1 签名 首先将除了 CERT.RSA , CERT.SF , MANIFEST.MF 之外的所有非目录文件分别用 SHA-1 计算摘要信息,然后使用 base64 进行编码,存入 MANIFEST.MF 中。 如果 MANIFEST.MF 不存在,则需要创建。存放格式是 entry name 以及对应的摘要 l 根据 之前计算的 SHA1 摘要信息,以及 私钥生成 一系列的 signature 并写入 CERT.SF 对 整个 MANIFEST.MF 进行 SHA1 计算,并将摘要信息存入 CERT.SF 中 。然后对之前计算的所有摘要信息使用 SHA1 再次计算数字签名,并写入 CERT.SF 中。 l 把公钥和签名信息写入 CERT.RST 把之前整个的签名输出文件 使用私有密钥计算签名。同时将签名结果,以及之前声称的公钥信息写入 CERT.RSA 中保存。

3.2 Package 的签名验证

安装时对一个 package 的签名验证的主要逻辑在 JarVerifier.java 文件的 verifyCertificate 函数中实现。 其主要的思路是通过提取 cert.rsa 中的证书和签名信息,获取签名算法等信息,然后按照之前对 apk 签名的方法进行计算,比较得到的签名和摘要信息与 apk 中保存的匹配。

第一步。提取证书信息,并对 cert.sf 进行完整性验证。 1. 先找到是否有 DSA 和 RSA 文件 ,如果找到则对其进行 decode ,然后读取其中的所有的证书列表(这些证书会被保存在 Package 信息中,供后续使用)。 2. 读取这个文件中的签名数据信息块列表,只取第一个签名数据块。读取其中的发布者和证书序列号。 3. 根据证书序列号,去匹配之前得到的所有证书,找到与之匹配的证书。 4. 从之前得到的签名数据块中读取签名算法和编码方式等信息 5. 读取 cert.sf 文件,并计算整个的签名,与数据块中的签名(编码格式的)进行比较,如果相同则完整性校验成功。 第二步。使用 cert.sf 中的摘要信息,验证 MANIFEST.MF 的完整性。 在 cert.sf 中提取 SHA1-Digest-Manifest 或者 SHA1-Digest 开头的签名 数据块 ( -Digest-Manifest 这个是整个 MANIFEST.MF 的摘要 信息,其它的是 jar 包中其它文件的摘要信息 ), 并逐个对这些数据块 进行验证。验证的方法是,现将 cert.sf 看做是很多的 entries ,每个 entries 包含了一些基本信息,如这个 entry 中使用的摘要算法( SHA1 等),对 jar 包中的哪个文件计算了摘要,摘要结果是什么。 处理时先找到每个摘要数据开中的文件信息,然后从 jar 包中读取,然后使用 -Digest 之前的摘要算法进行计算,如果计算结果与摘要数据块中保存的信息的相匹配,那么就完成验证。 作者:GangWang 出处:http://www.cnblogs.com/GnagWang/ 本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

Openvpn在linux上的完美实现

一,OpenVPN的工作原理

  VPN技术通过密钥交换、封装、认证、加密手段在公共网络上建立起私密的隧道,保障传输数据的完整性、私密性和有效性。OpenVPN是近年来新出现的开放源码项目,实现了SSLVPN的一种解决方案。

  传统SSL VPN通过端口代理的方法实现,代理服务器根据应用协议的类型(如http,telnet等)做相应的端口代理,客户端与代理服务器之间建立SSL安全连接,客户端与应用服务器之间的所有数据传输通过代理服务器转发。这种实现方式烦琐,应用范围也比较窄:仅适用于用TCP固定端口进行通信的应用系统,且对每个需要代理的端口进行单独配置;对于每个需要用到动态端口的协议都必须重新开发,且在代理中解析应用协议才能实现代理,如FTP协议;不能对TCP以外的其它网络通信协议进行代理;代理服务器前端的防火墙也要根据代理端口的配置变化进行相应调整。

  OpenVPN以一种全新的方式实现了SSLVPN的功能,克服了传统SSLVPN的一些缺陷,扩展了应用领域,并且防火墙上只需开放TCP或UDP协议的一个端口。

  1,虚拟网卡

  在Linux2.4版本以上,操作系统支持一个名为tun的设备,tun设备的驱动程序中包含两个部分,一部分是字符设备驱动,一部分是网卡驱动。网卡的驱动把从TCP/IP协议栈收到的数据包结构skb放于tun设备的读取队列,用户进程通过调用字符设备接口read获得完整的IP数据包,字符驱动read函数的功能是从设备的读取队列读取数据,将核心态的skb传递给用户;反过来字符驱动write函数给用户提供了把用户态的数据写入核心态的接口,write函数把用户数据写入核心空间并穿入TCP/IP协议栈。该设备既能以字符设备的方式被读写,作为系统的虚拟网卡,也具有和物理网卡相同的特点:能够配置IP地址和路由。对虚拟网卡的使用是OpenVPN实现其SSLVPN功能的关键。

  2,地址池以及路由

  OpenVPN服务器一般需要配置一个虚拟IP地址池和一个自用的静态虚拟IP地址(静态地址和地址池必须在同一个子网中),然后为每一个成功建立SSL连接的客户端动态分配一个虚拟IP地址池中未分配的地址。这样,物理网络中的客户端和OpenVPN服务器就连接成一个虚拟网络上的星型结构局域网,OpenVPN服务器成为每个客户端在虚拟网络上的网关。OpenVPN服务器同时提供对客户端虚拟网卡的路由管理。当客户端对OpenVPN服务器后端的应用服务器的任何访问时,数据包都会经过路由流经虚拟网卡,OpenVPN程序在虚拟网卡上截获数据IP报文,然后使用SSL协议将这些IP报文封装起来,再经过物理网卡发送出去。OpenVPN的服务器和客户端在虚拟网卡之上建立起一个虚拟的局域网络,这个虚拟的局域网对系统的用户来说是透明的。

  3,客户端与服务端安全连接的建立

  OpenVPN的服务器和客户端支持tcp和udp两种连接方式,只需在服务端和客户端预先定义好使用的连接方式(tcp或udp)和端口号,客户端和服务端在这个连接的基础上进行SSL握手。连接过程包括SSL的握手以及虚拟网络上的管理信息,OpenVPN将虚拟网上的网段、地址、路由发送给客户端。连接成功后,客户端和服务端建立起SSL安全连接,客户端和服务端的数据都流入虚拟网卡做SSL的处理,再在tcp或udp的连接上从物理网卡发送出去。

  4,数据包的处理过程

  4.1发送数据流程

  应用层的外出数据,经过系统调用接口传入核心TCP/IP层做处理,在TCP/IP经过路由到虚拟网卡,虚拟网卡的网卡驱动发送处理程序hard_start_xmit()将数据包加入skb表并完成数据包从核心区到用户区的复制,OpenVPN调用虚拟网卡的字符处理程序tun_read(),读取到设备上的数据包,对读取的数据包使用SSL协议做封装处理后,通过socket系统调用发送出去。

  4.2接收数据流程
物理网卡接收数据包,经过核心TCP/IP上传到OpenVPN,OpenVPN通过link_socket_read()接收数据包,使用SSL协议进行解包处理,经过处理的数据包OpenVPN调用虚拟网卡的字符处理程序tun_write()写入虚拟网卡的字符设备,设备驱动程序完成数据从用户区到核心区的复制,并将数据写入skb链表,然后调用网卡netif_rx()接收程序,数据包再次进入系统TCP/IP协议栈,传到上层应用程序。

  \"\"

  5,数据包的封装

  OpenVPN提供tun和tap两种工作模式。在tun模式下,从虚拟网卡上收到的是不含物理帧头IP数据包,SSL处理模块对IP包进行SSL封装;在tap模式下,从虚拟网卡上收到的是包含物理帧头的数据包,SSL处理模块对整个物理帧进行SSL封装。Tap模式称为网桥模式,整个虚拟的网络就像网桥方式连接的物理网络。这种模式可以传输以太网帧、IPX、NETBIOS等数据包,应用范围更广。

  6,OpenVPN与Openssl

  OpenVPN软件包需要和openssl软件一起安装,因为OpenVPN调用了Openssl函数库,OpenVPN的客户端和服务端建立SSL链接的过程是通过调用Openssl来实现的。通过bio_write()/函数把数据写入Openssl的状态机通道,bio_read()从Openssl读取结果。OpenVPN还调用Openssl的加解密函数处理转发的数据包。

  二,OpenVPN的应用

  OpenVPN能够运行于大多常见的系统平台,是一个基于SSL/TLS的VPN解决方案。OpenVPN能够基于TAP/TUN虚拟设备驱动实现2层及3层的链接,提供基于SSL/TLS的工业级别加密功能,并且实现了大多数常见VPN解决方案的全部特性。但目前提供整合了OpenVPN的VPN硬件厂商还不太多。

  \"\"

  1,安装openvpn软件包

  lzo提供了一种数据加密的算法,这里openvpn会用到lzo算法,所以要安装lzo

  解压及安装 # tar xvf lzo-2.04.tar.gz # cd lzo-2.04 # ./configure –prefix=/usr/local/lzo # make && make install # cd ../ # tar xvf openvpn-2.1.4.tar.gz # cd openvpn-2.1.4 # ./configure –prefix=/usr/local/openvpn –with-lzo-headers=/usr/local/lzo/include/ –with-lzo-lib=/usr/local/lzo/lib/ –with-ssl-headers=/usr/include/openssl/ –with-ssl-lib=/usr/lib # make && make install # cd ../

  2,openvpn设置

  2.1用easy-rsa生成服务器证书客户端证书

  [root@test1 ~]# mkdir /etc/openvpn [root@test1 ~]# cp /root/openvpn-2.1.4/easy-rsa/ /etc/openvpn/ -r [root@test1 ~]# cd /etc/openvpn/easy-rsa/2.0/ [root@test1 2.0]# cat vars |grep -v \’^$\’|grep -v \’^#\’ export EASY_RSA=\”`pwd`\” RSA在当前目录 export OPENSSL=\”openssl\” export PKCS11TOOL=\”pkcs11-tool\” export GREP=\”grep\” export KEY_CONFIG=`$EASY_RSA/whichopensslcnf $EASY_RSA` 这个是一个很特别的参数没有“”, openssl的配置文件 export KEY_DIR=\”$EASY_RSA/keys\” 密钥的位置 echo NOTE: If you run ./clean-all, I will be doing a rm -rf on $KEY_DIR export PKCS11_MODULE_PATH=\”dummy\” export PKCS11_PIN=\”dummy\” export KEY_SIZE=1024 密钥的大小长度 export CA_EXPIRE=3650 CA证书的过期时间天数 export KEY_EXPIRE=3650 密钥的时间过期天数 export KEY_COUNTRY=\”CN\” 国家 export KEY_PROVINCE=\”HB\” 省份 export KEY_CITY=\”WH\” 城市 export KEY_ORG=\”\” 组织 export KEY_EMAIL=\”root@localhost\” 邮箱

  2.2下面这个命令在第一次安装时可以运行,以后在添加客户端时千万别运行,这个命令 会清除所有已经生成的证书密钥。
# ./clean-all

  2.3建立根证书

  [root@test1 2.0]# ./build-ca Generating a 1024 bit RSA private key ..++++++ ……………………………………………..++++++ writing new private key to \’ca.key\’ —– You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter \’.\’, the field will be left blank. —– Country Name (2 letter code) [CN]:CN State or Province Name (full name) [HB]:HUB Locality Name (eg, city) [WH]:WH Organization Name (eg, company) []: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server\’s hostname) [ CA]:test Name []: Email Address [root@localhost]:

  2.4生成服务器端密钥key, 后面这个server-name就是服务器名,可以自定义

  [root@test1 2.0]# ./build-key-server test Generating a 1024 bit RSA private key …………………………………++++++ ……………………..++++++ writing new private key to \’test.key\’ —– You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter \’.\’, the field will be left blank. —– Country Name (2 letter code) [CN]:CN State or Province Name (full name) [HB]:HB Locality Name (eg, city) [WH]:WH Organization Name (eg, company) []: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server\’s hostname) [test]: Name []: Email Address [root@localhost]: Please enter the following \’extra\’ attributes to be sent with your certificate request A challenge password []:123456 An optional company name []: Using configuration from /etc/openvpn/easy-rsa/f Check that the request matches the signature Signature ok The Subject\’s Distinguished Name is as follows countryName :PRINTABLE:\’CN\’ stateOrProvinceName :PRINTABLE:\’HB\’ localityName :PRINTABLE:\’WH\’ organizationName :PRINTABLE:\’\’ commonName :PRINTABLE:\’test\’ emailAddress :IA5STRING:\’root@localhost\’ Certificate is to be certified until Oct 6 03:30:01 2023 GMT (3650 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated

  2.5生成客户端 key

  注意在进入 Common Name (eg, your name or your server\’s hostname) []: 的输入时, 每个证书输入的名字必须不同.

  [root@test1 2.0]# ./build-key test Generating a 1024 bit RSA private key …………………………………………………………………………………………………………….++++++ ………………….++++++ writing new private key to \’test.key\’ —– You are about to be asked to enter information that will be incorporated into your certificate request. What you are about to enter is what is called a Distinguished Name or a DN. There are quite a few fields but you can leave some blank For some fields there will be a default value, If you enter \’.\’, the field will be left blank. —– Country Name (2 letter code) [CN]: State or Province Name (full name) [HB]: Locality Name (eg, city) [WH]: Organization Name (eg, company) []: Organizational Unit Name (eg, section) []: Common Name (eg, your name or your server\’s hostname) [test]: 每个client的hostname都不能一样 Name []: Email Address [root@localhost]: Please enter the following \’extra\’ attributes to be sent with your certificate request A challenge password []:123456 An optional company name []: Using configuration from /etc/openvpn/easy-rsa/f Check that the request matches the signature Signature ok The Subject\’s Distinguished Name is as follows countryName :PRINTABLE:\’CN\’ stateOrProvinceName :PRINTABLE:\’HB\’ localityName :PRINTABLE:\’WH\’ organizationName :PRINTABLE:\’\’ commonName :PRINTABLE:\’test\’ emailAddress :IA5STRING:\’root@localhost\’ Certificate is to be certified until Oct 6 03:36:48 2023 GMT (3650 days) Sign the certificate? [y/n]:y 1 out of 1 certificate requests certified, commit? [y/n]y Write out database with 1 new entries Data Base Updated

  2.6生成Diffie Hellman参数,加强安全

[root@test1 2.0]# ./build-dh
Generating DH parameters, 1024 bit long safe prime, generator 2
This is going to take a long time
…………..+……………+..+……………………………………….+..+…………..+…….+………….

  2.7创建服务端配置文件

  local 192.168.55.25 #换成服务端的IP

  [root@test1 2.0]# mkdir /etc/openvpn/easy-rsa/2.0/conf [root@test1 2.0]# cp /root/openvpn-2.1.4/sample-config-files/nf /etc/openvpn/easy-rsa/2.0/conf/ [root@test1 2.0]# cat /etc/openvpn/easy-rsa/nf |grep -v \’^$\’|grep -v \’^;\’|grep -v \’^#\’ port 1194 proto udp dev tun ca /etc/openvpn/easy-rsa/2.0/keys/ca.crt cert /etc/openvpn/easy-rsa/2.0/keys/test.crt key /etc/openvpn/easy-rsa/2.0/keys/test.key # This file should be kept secret dh /etc/openvpn/easy-rsa/2.0/keys/dh1024.pem server 10.8.0.0 255.255.255.0 ifconfig-pool-persist ipp.txt push \”dhcp-option DNS 192.168.55.25\” push \”dhcp-option DNS 8.8.8.8\” client-to-client keepalive 10 120 comp-lzo user nobody group nobody persist-key persist-tun status openvpn-status.log log openvpn.log verb 3

  2.8启动openvpn

[root@test1 2.0]# /usr/local/openvpn/sbin/openvpn  –config  /etc/openvpn/easy-rsa/nf &
[3] 11924

  2.9打开服务器的路由功能

vim /etc/nf

修改以下内容:
net.ipv4.ip_forward = 1

  2.10使nf配置文件生效并添加iptables转发规则:

sysctl –p

iptables -t nat -A POSTROUTING -o eth0 -s 10.8.0.0/24 -j MASQUERADE

/etc/init.d/iptables save

  四,测试openvpn的功能

  4.1在windows7客户端上安装openvpn-2.1_rc22-install.exe

  4.2将服务器上的/etc/openvpn/easy-rsa/2.0/keys下的文件拷贝到C:Program Files (x86)OpenVPNconfig

  4.3在windows7客户端上将C:Program Files (x86)OpenVPNsample-configclient.ovpn拷贝到C:Program Files (x86)OpenVPNconfig下

  4.4编辑client.ovpn 修改如下

  remote 服务器IP 1194

  4.5 运行client.ovpn测试是否可以ping通10.8.0.1,,也就是服务器的tun接口IP。。

  五,openvpn排错

  5.1连接openvpn时出现错误提示:

  TLS_ERROR: BIO read tls_read_plaintext error: error:140890B2:SSL routines:SSL3_GET_CLIENT_CERTIFICATE:no certificate returned

  TLS Error: TLS object -> incoming plaintext read error

  TLS Error: TLS handshake failed

  这个似乎是提示系统时间和证书时间不一致,具体解决措施为:

  1.修改vps时间与本地时间一致

  2.重启vps

  3.重新连接openvpn试试

  4.如果依旧不能连接openvpn,可以在vps上重新生成一个新的证书。

  5.2今天,部署了下VPN,部署完成连接的时候,总是提示连接失败,很郁闷,贴出日志:
Sun Mar 18 20:25:54 2012 [jesse] Peer Connection Initiated with 10.0.0.200:5000
Sun Mar 18 20:25:55 2012 SENT CONTROL [jesse]: \’PUSH_REQUEST\’ (status=1)
Sun Mar 18 20:25:55 2012 PUSH: Received control message: \’PUSH_REPLY,route 172.16.1.0 255.255.255.0,route 10.0.1.0 255.255.255.0,topology net30,ping 10,ping-restart 120,ifconfig 10.0.1.6 10.0.1.5\’
Sun Mar 18 20:25:55 2012 Options error: Unrecognized option or missing parameter(s) in [PUSH-OPTIONS]:3: topology (2.0.9)
Sun Mar 18 20:25:55 2012 OPTIONS IMPORT: timers and/or timeouts modified
Sun Mar 18 20:25:55 2012 OPTIONS IMPORT: –ifconfig/up options modified
Sun Mar 18 20:25:55 2012 OPTIONS IMPORT: route options modified
Sun Mar 18 20:25:55 2012 TAP-WIN32 device [本地连接 3] opened: .Global{2893A584-9C99-43FE-B17C-E1F7160BE530}.tap

  解决方法

  There are no TAP-Win32 adapters on this system. You should be able to create a TAP-Win32 adapter by going to Start -> All Programs -> OpenVPN -> Add a new TAP-Win32 virtual ethernet adapter.

  5.3 All TAP-Win32 adapters on this system are currently in use

  那么很有可能是你的TAP虚拟网卡没有打好驱动,那么只能换其他的openvpn的gui软件了

  \"\"

  5.5官方的openvpn排错

  如果遇到OpenVPN初始化失败,或者ping失败,下面有一些共同的问题症状和解决办法:

  1.你得到如下错误信息: TLS Error: TLS key negotiation failed to occur within 60 seconds (check your network connectivity). 这个错误指出客户端不能跟服务器建立网络链接.

  解决办法:

  a.请确认客户端访问的服务器的机器名/IP和端口是正确的.
b.如果你的OpenVPN服务器是单网卡,并处在受保护的局域网中,请确认你你的网关防火墙使用了正确的端口转发规则。比如:你的OpenVPN机器的地址是192.168.4.4,但处在防火墙保护下,时刻监听着UDP协议1194的连接请求,那么负责维护192.168.4.x子网的网关就会有一个端口转发策略,即所有访问UDP协议1194端口的请求都被转发到192.168.4.4 。
c.打开服务器的防火墙允许UDP协议1194端口连接进来,(或者不管是TCP还是UDP协议在服务器的配置文件中配置了)。

  2.你得到如下错误信息: Initialization Sequence Completed with errors – 这个错误可能发生在windows下(a)你没有启用DHCP客户端服务(b)你的XP SP2使用了某个第三方的个人防火墙。

  解决办法: 启动DHCP客户端服务或者你确认你的XP SP2正确使用了个人防火墙.

  3.你虽然获得了Initialization Sequence Completed 的信息,但ping测试还是失败了,那就通常是在服务器或者客户端的防火墙阻止过滤了在TUN/TAP设备结构上的网络流量。

  解决办法: 关闭客户端的防火墙,如果防火墙过滤了TUN/TAP设备端口的流量。比如在Windows XP SP2系统,你可以到Windows 安全中心 -> Windows 防火墙 -> 高级 然后不要选择TAP-Win32 adapter设备 (即禁止TUN/TAP设备使用防火墙过滤 ,实质上就是告诉防火墙不要阻止VPN认证信息)。 同样在服务器端也要确认TUN/TAP设备不实用防火墙过滤 (也就是说在TUN/TAP接口上选择过滤是有一定的安全保障的. 具体请看下面一节的访问策略).

  4.当以udp协议的配置文件启动的时候连接停止,服务器的日志文件显示如下一行信息:

TLS: Initial packet from x.x.x.x:x, sid=xxxxxxxx xxxxxxxx

  不管怎么样,这信息只在服务器端显示,在客户端是不会显示相同的信息。

  解决办法: 你只拥有单向连接从客户端到服务器,从服务器到客户端的连接被防火墙挡住, 通常在客户端这边,防火墙(a)可能是个运行在客户端的个人防火墙软件(b)或者服务客户端的NAT路由 网关被设置为从服务器端访问客户端的UDP协议包被阻挡返回。

  查看FAQ能得到更多故障解决的信息.

云计算技术背后的那些天才程序员:KVM之父Avi Kivity

摘要:KVM以其精简的架构,清晰的定位获得Linux社区多数开发人员的支持并快速被合并入主干,从而成为x86全虚拟化技术的一颗新星,其提出者Avi Kivity对于计算机体系架构和Linux内核有深入理解,并且精通汇编和C。

提到以色列,自然会使人联想到犹太人和犹太教及其在二十世纪前半叶所遭受的种种苦难,作为这个世界上最古老的民族之一,除了其沉重的历史之外,更使人惊叹的是,这个面积仅有2.2万平方公里的弹丸之地,在短短几十年中转变成为了高科技研发和创新的“全球性大国”。或许,时刻处于强敌环伺的危险境地能够激发人类最原始的创造力,使得以色列在诸多领域出类拔萃,尤其在信息科技领域尤为突出,世界上最大的科技公司不约而同都选择在以色列设立研发中心,而且成果卓著,例如,Intel x86家族中比较重要的两代CPU,Sandy Bridge和Ivy Bridge都是由Intel以色列的研发中心研发的,蓝色巨人IBM也早在上世纪七十年代就在以色列设立了庞大的研发中心,且成果斐然。

但是,值得称道的是科技巨头在以色列的成功并不能掩盖个体的创新力量所散发出的光芒,尤其在云计算领域,当崇尚人格自由,同时又具有坚强性格这两种特质结合在一起所爆发出的创新的力量,使得草根创业公司和个体也敢于在前沿领域与科技巨头一争长短,其中的代表性人物就是Avi Kivity和他所在的以色列初创公司Qumranet,Avi Kivity提出并推动KVM(基于Linux内核的全虚拟化方案),以其精简的架构,清晰的定位很快获得Linux社区多数开发人员的支持得以快速被合并进入主干,从而成为x86全虚拟化技术的一颗新星。

虚拟化技术的由来与所面临的挑战

信息技术领域本不应是一个缺乏远见的领域,但事实正好相反,蓝色巨人IBM发明了PC,但失去了整个桌面,Intel发明并主导x86架构的处理器,提出了摩尔定律,但是失去了整个掌上,而且,即使是处于鼎盛时期的Intel,即使有摩尔定律指路,也没有人能料到,有朝一日x86家族的处理器几乎快要统治整个数据中心,而且x86家族处理器计算能力的提升速度快到可以在原本为桌面环境设计的处理器架构上采用“分时”来为多个虚拟环境提供运算能力的地步。

要知道虚拟化技术并不新,早在1967年,第一代的硬件虚拟化技术就由IBM提出,蓝色巨人在他们的大型机中实现了以CP/CMS为代表的虚拟化技术。但直到2006年,Intel和AMD才分别在各自的处理器中加入了“有限”的硬件虚拟化特性,实际上是给CPU打了一个补丁:),分别称为,Intel VT-x和AMD-V。与大型机所采用的专为虚拟化设计的处理器不同,从PC机起家的以Intel为代表的x86家族的处理器生来就并不是为虚拟化设计的。要在x86家族处理器上完全向后兼容的同时加入硬件虚拟化特性,无疑成为一个挑战,硬件层面实现较为困难,导致软件层面的实现复杂度也随之水涨船高。据估算,Linux内核中,虚拟化部分代码,x86架构部分的代码行是IA64架构的1倍,S390架构的7倍,PPC架构的8倍。即使和ARM架构相比,x86架构的复杂度依然是其4倍,其复杂度之高从中可见一斑。

Talk is cheap, show me the code
硬件层面较为复杂的局面意味着在系统层面需要一个极为精简的设计从而确保x86的硬件虚拟化特性能够在Linux内核层面有较好的可维护性,良好的性能,同时保持一定程度的可扩展性以便为未来新的特性(补丁)的加入留有余地,另外,在那个时候,Xen和Vmware依靠自己独有的技术分别在不同领域如日中天,尤其值得一提的是,Xen在开源领域几乎成为了虚拟化的事实标准,而且开源领域一向奉行Don’t reinvent the wheel这一金科玉律,这一切无疑都为提出新的虚拟化实现增加了巨大的不确定性,但是当时Linux内核中还没有任何虚拟化实现,这一现状又使得提出新的设计成为可能,唯一的问题就是谁能够在足够短的时间内另辟蹊径提出新的方案并且获得多数人的支持,这一点难度之大,恐怕只有深入了解开源社区的人才会感受到,要知道Linux社区恐怕是这个星球上最为“独裁”的组织,没有之一,而且语言“暴力”与人身“攻击”司空见惯(Alan Cox就是无法容忍这一点才选择退出Linux社区),要想在其中被认可并取得一席之地,除了超强的技术实力之外,更要有谦逊的品格,非凡的耐心和绝佳的沟通技巧。

Do one thing and do it well
Avi Kivity提出的方案非常清晰而且巧妙,充分遵循Linux源自Unix的设计思想,把方案的关注点聚焦于Linux内核部分,User space部分交给了稳定可靠的Qemu,采用Qemu作为其用户空间的入口,而且KVM仅仅实现HVM功能。遵循惯例,Linux内核中的KVM模块通过设备文件暴露系统呼叫给User space的Qemu。这一切现在看起来似乎平淡无奇,但是,Avi Kivity方案的背后是要在极短时间内通过代码实现来证实并获得社区认可这一几乎不可能实现的任务,这有赖于Avi Kivity对于计算机体系架构的深入理解,不单单是x86架构的Intel VT-x和AMD-V,还有后续陆续支持的其它架构,并且深入了解Linux内核,精通汇编和C。
以Avi wholesale jerseys Kivity为主的工程师仅仅花了不到一年时间就让Linux社区接受KVM的设计方案并且通过了代码Review,最终于2006年10月合并进入2.6.20主干,时至今日,人们依然对于KVM合并进入Linux主干的速度之快感到不可思议,从中也产生了一些不同的声音,但是多数人还是倾向于最简单的一种答案,那就是KVM良好的设计和实现是其最终被社区快速接受的主要原因。几年以后,Avi Kivity接受采访,最经常被问到的仍然是这个问题,他的回答也总是轻描淡写,说只不过提前花了半年时间在Linux社区学习如何更好的与社区开发人员协作,但是我们知道,这背后实际是其投入的大量心血和超强实力。
KVM的出现震撼了整个了Linux社区和业界,人们惊异于其出众的性能,更加折服于其精简的设计和良好实现(在SPEC virt2013 Benchmark中基于同样的处理器、内存和存储,KVM单在VM Density指标上以37%的优势超越对手),以至于,在极短的时间内,这个世界上最重要的科技公司纷纷宣称他们将把KVM作为他们x86虚拟化的解决方案,要知道,这极为不同寻常,在这之前以蓝色巨人IBM为代表的科技巨头已经在Xen上投入了大量的财力物力和人力,如果没有决定性的因素,科技巨头的Strategic dicision是不可能轻易改变的,时至今日,IBM在准备推出Power KVM,打算把KVM移植到Power Linux上。就连以Google Glass、无人驾驶汽车和Project Loon著称的跟现代科技格格不入的前沿科技巨头Google也在其Google Compute Engine中采用KVM技术作为x86的虚拟化方案来和Amazon对抗。这一切只能说明KVM凭借其优异的性能和精简架构当仁不让成为了一项无法忽视的虚拟化技术屹立于云计算之巅。

云计算的新征程OSv

在KVM合并进入Linux主干之后不久Avi Kivity很快成为内核KVM子模块的Maintainer,2012年11月21日一封标题为“KVM: Retire as maintainer”的简短邮件出现在Linux社区邮件列表中,在以KVM模块Maintainer身份贡献Linux社区6年零6个月之后,Avi Kivity选择开启了新的征程,发起并担任云计算创业公司Cloudius的CTO,这是一个不同寻常的选择,要知道在这个世界上,在虚拟化和云计算领域恐怕没有多少人能够比Avi Kivity更有发言权,这一次,Avi Kivity敏锐的意识到除了x86家族的处理器架构之外,已经成为云计算基石的Linux同样天生不是为云计算设计的,因此具有极大的性能提升空间,大胆提出并设计了专为云计算设计的操作系统OSv,当然,其依然是建立在Linux基础上,但是其无需Context switch,真正的Zero Copy以及Lock Free等特性使得其Less One second boot相对于众多宣称秒级响应的厂商来说更加具有说服力。尤其重要的是,KVM之父通过OSv项目表达了对以Linux CGroup技术为核心的Containner技术的看法。
Avi Kivity是如此低调,以至于你很难在网上找到太多的关于他的信息,只有寥寥几篇报道和访谈,但是KVM在整个云计算领域中又是如此的炙手可热,虽然今年已经是KVM进入内核的第八个年头,但是KVM依然活跃,我们依然难以估量其在云计算领域的真正价值,而这一切都源自于这个地球上最危险的那个角落中的那名天才程序员Avi Kivity。
注:仅以此文表达对Avi Kivity、Yaniv Kamay、Dor Laor、Uri Lublin还有Anthony Liguori还有其他KVM以及Qemu开发人员的敬意。(责编:周小璐)

原文链接:不朽传奇-云计算技术背后的那些天才程序员:KVM之父Avi Kivity