SpringBoot 接口签名校验实践[1概念][开放接口]开放接口是指不需要登录凭证就允许被第三方系统调用的接口。为了防止开放接口被恶意调用，开放接口一般都需要验签才能被调用。提供开放接口的系统下面统一简称为"原系统"。[验签]验签是指第三方系统在调用接口之前，需要按照原系统的规则根据所有请求参数生成一个签名（字符串），在调用接口时携带该签名。原系统会验证签名的有效性，只有签名验证有效才能正常调用接口，否则请求会被驳回。[2接口验签调用流程][1. 约定签名算法]第三方系统作为调用方，需要与原系统协商约定签名算法（下面以SHA256withRSA签名算法为例）。同时约定一个名称（callerID），以便在原系统中来唯一标识调用方系统。[2. 颁发非对称密钥对]签名算法约定后之后，原系统会为每一个调用方系统专门生成一个专属的非对称密钥对（RSA密钥对）。私钥颁发给调用方系统，公钥由原系统持有。注意，调用方系统需要保管好私钥（存到调用方系统的后端）。因为对于原系统而言，调用方系统是消息的发送方，其持有的私钥唯一标识了它的身份是原系统受信任的调用方。调用方系统的私钥一旦泄露，调用方对原系统毫无信任可言。[3. 生成请求参数签名]签名算法约定后之后，生成签名的原理如下（活动图）。为了确保生成签名的处理细节与原系统的验签逻辑是匹配的，原系统一般都提供jar包或者代码片段给调用方来生成签名，否则可能会因为一些处理细节不一致导致生成的签名是无效的。[4. 请求携带签名调用]路径参数中放入约定好的callerID，请求头中放入调用方自己生成的签名[3代码设计][1. 签名配置类]相关的自定义yml配置如下。RSA的公钥和私钥可以使用hutool的SecureUtil工具类来生成，注意公钥和私钥是base64编码后的字符串定义一个配置类来存储上述相关的自定义yml配置 `import cn.hutool.crypto.asymmetric.SignAlgorithm; import lombok.Data; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; import java.util.Map; /** * 签名的相关配置 */ @Data @ConditionalOnProperty(value = "secure.signature.enable", havingValue = "true") // 根据条件注入bean @Component @ConfigurationProperties("secure.signature") public class SignatureProps { private Boolean enable; private Map<String, KeyPairProps> keyPair; @Data public static class KeyPairProps { private SignAlgorithm algorithm; private String publicKeyPath; private String publicKey; private String privateKeyPath; private String privateKey; } } `[2. 签名管理类]定义一个管理类，持有上述配置，并暴露生成签名和校验签名的方法。注意，生成的签名是将字节数组进行十六进制编码后的字符串，验签时需要将签名字符串进行十六进制解码成字节数组 `import cn.hutool.core.io.IoUtil; import cn.hutool.core.io.resource.ResourceUtil; import cn.hutool.core.util.HexUtil; import cn.hutool.crypto.SecureUtil; import cn.hutool.crypto.asymmetric.Sign; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import top.ysqorz.signature.model.SignatureProps; import java.nio.charset.StandardCharsets; @ConditionalOnBean(SignatureProps.class) @Component public class SignatureManager { private final SignatureProps signatureProps; public SignatureManager(SignatureProps signatureProps) { this.signatureProps = signatureProps; loadKeyPairByPath(); } /** * 验签。验证不通过可能抛出运行时异常CryptoException * * @param callerID 调用方的唯一标识 * @param rawData 原数据 * @param signature 待验证的签名(十六进制字符串) * @return 验证是否通过 */ public boolean verifySignature(String callerID, String rawData, String signature) { Sign sign = getSignByCallerID(callerID); if (ObjectUtils.isEmpty(sign)) { return false; } // 使用公钥验签 return sign.verify(rawData.getBytes(StandardCharsets.UTF_8), HexUtil.decodeHex(signature)); } /** * 生成签名 * * @param callerID 调用方的唯一标识 * @param rawData 原数据 * @return 签名(十六进制字符串) */ public String sign(String callerID, String rawData) { Sign sign = getSignByCallerID(callerID); if (ObjectUtils.isEmpty(sign)) { return null; } return sign.signHex(rawData); } public SignatureProps getSignatureProps() { return signatureProps; } public SignatureProps.KeyPairProps getKeyPairPropsByCallerID(String callerID) { return signatureProps.getKeyPair().get(callerID); } private Sign getSignByCallerID(String callerID) { SignatureProps.KeyPairProps keyPairProps = signatureProps.getKeyPair().get(callerID); if (ObjectUtils.isEmpty(keyPairProps)) { return null; // 无效的、不受信任的调用方 } return SecureUtil.sign(keyPairProps.getAlgorithm(), keyPairProps.getPrivateKey(), keyPairProps.getPublicKey()); } /** * 加载非对称密钥对 */ private void loadKeyPairByPath() { // 支持类路径配置，形如：classpath:secure/public.txt // 公钥和私钥都是base64编码后的字符串 signatureProps.getKeyPair() .forEach((key, keyPairProps) -> { // 如果配置了XxxKeyPath，则优先XxxKeyPath keyPairProps.setPublicKey(loadKeyByPath(keyPairProps.getPublicKeyPath())); keyPairProps.setPrivateKey(loadKeyByPath(keyPairProps.getPrivateKeyPath())); if (ObjectUtils.isEmpty(keyPairProps.getPublicKey()) || ObjectUtils.isEmpty(keyPairProps.getPrivateKey())) { throw new RuntimeException("No public and private key files configured"); } }); } private String loadKeyByPath(String path) { if (ObjectUtils.isEmpty(path)) { return null; } return IoUtil.readUtf8(ResourceUtil.getStream(path)); } } `[3. 自定义验签注解]有些接口需要验签，但有些接口并不需要，为了灵活控制哪些接口需要验签，自定义一个验签注解 `import java.lang.annotation.*; /** * 该注解标注于Controller类的方法上，表明该请求的参数需要校验签名 */ @Documented @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface VerifySignature { } `[4. AOP实现验签逻辑]验签逻辑不能放在拦截器中，因为拦截器中不能直接读取body的输入流，否则会造成后续@RequestBody的参数解析器读取不到body。由于body输入流只能读取一次，因此需要使用ContentCachingRequestWrapper包装请求，缓存body内容（见第5点），但是该类的缓存时机是在@RequestBody的参数解析器中。因此，满足2个条件才能获取到ContentCachingRequestWrapper中的body缓存：接口的入参必须存在@RequestBody读取body缓存的时机必须在@RequestBody的参数解析之后，比如说：AOP、Controller层的逻辑内。注意拦截器的时机是在参数解析之前的综上，标注了@VerifySignature注解的controlle层方法的入参必须存在@RequestBody，AOP中验签时才能获取到body的缓存！ `import cn.hutool.crypto.CryptoException; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.ServletWebRequest; import org.springframework.web.servlet.HandlerMapping; import org.springframework.web.util.ContentCachingRequestWrapper; import top.ysqorz.common.constant.BaseConstant; import top.ysqorz.config.SpringContextHolder; import top.ysqorz.config.aspect.PointCutDef; import top.ysqorz.exception.auth.AuthorizationException; import top.ysqorz.exception.param.ParamInvalidException; import top.ysqorz.signature.model.SignStatusCode; import top.ysqorz.signature.model.SignatureProps; import top.ysqorz.signature.util.CommonUtils; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import java.nio.charset.StandardCharsets; import java.util.Map; @ConditionalOnBean(SignatureProps.class) @Component @Slf4j @Aspect public class RequestSignatureAspect implements PointCutDef { @Resource private SignatureManager signatureManager; @Pointcut("@annotation(top.ysqorz.signature.enumeration.VerifySignature)") public void annotatedMethod() { } @Pointcut("@within(top.ysqorz.signature.enumeration.VerifySignature)") public void annotatedClass() { } @Before("apiMethod() && (annotatedMethod() || annotatedClass())") public void verifySignature() { HttpServletRequest request = SpringContextHolder.getRequest(); String callerID = request.getParameter(BaseConstant.PARAM_CALLER_ID); if (ObjectUtils.isEmpty(callerID)) { throw new AuthorizationException(SignStatusCode.UNTRUSTED_CALLER); // 不受信任的调用方 } // 从请求头中提取签名，不存在直接驳回 String signature = request.getHeader(BaseConstant.X_REQUEST_SIGNATURE); if (ObjectUtils.isEmpty(signature)) { throw new ParamInvalidException(SignStatusCode.REQUEST_SIGNATURE_INVALID); // 无效签名 } // 提取请求参数 String requestParamsStr = extractRequestParams(request); // 验签。验签不通过抛出业务异常 verifySignature(callerID, requestParamsStr, signature); } @SuppressWarnings("unchecked") public String extractRequestParams(HttpServletRequest request) { // @RequestBody String body = null; // 验签逻辑不能放在拦截器中，因为拦截器中不能直接读取body的输入流，否则会造成后续@RequestBody的参数解析器读取不到body // 由于body输入流只能读取一次，因此需要使用ContentCachingRequestWrapper包装请求，缓存body内容，但是该类的缓存时机是在@RequestBody的参数解析器中 // 因此满足2个条件才能使用ContentCachingRequestWrapper中的body缓存 // 1. 接口的入参必须存在@RequestBody // 2. 读取body缓存的时机必须在@RequestBody的参数解析之后，比如说：AOP、Controller层的逻辑内。注意拦截器的时机是在参数解析之前的 if (request instanceof ContentCachingRequestWrapper) { ContentCachingRequestWrapper requestWrapper = (ContentCachingRequestWrapper) request; body = new String(requestWrapper.getContentAsByteArray(), StandardCharsets.UTF_8); } // @RequestParam Map<String, String[]> paramMap = request.getParameterMap(); // @PathVariable ServletWebRequest webRequest = new ServletWebRequest(request, null); Map<String, String> uriTemplateVarNap = (Map<String, String>) webRequest.getAttribute( HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE, RequestAttributes.SCOPE_REQUEST); return CommonUtils.extractRequestParams(body, paramMap, uriTemplateVarNap); } /** * 验证请求参数的签名 */ public void verifySignature(String callerID, String requestParamsStr, String signature) { try { boolean verified = signatureManager.verifySignature(callerID, requestParamsStr, signature); if (!verified) { throw new CryptoException("The signature verification result is false."); } } catch (Exception ex) { log.error("Failed to verify signature", ex); throw new AuthorizationException(SignStatusCode.REQUEST_SIGNATURE_INVALID); // 转换为业务异常抛出 } } } import org.aspectj.lang.annotation.Pointcut; public interface PointCutDef { @Pointcut("execution(public * top.ysqorz..controller.*.*(..))") default void controllerMethod() { } @Pointcut("@annotation(org.springframework.web.bind.annotation.PostMapping)") default void postMapping() { } @Pointcut("@annotation(org.springframework.web.bind.annotation.GetMapping)") default void getMapping() { } @Pointcut("@annotation(org.springframework.web.bind.annotation.PutMapping)") default void putMapping() { } @Pointcut("@annotation(org.springframework.web.bind.annotation.DeleteMapping)") default void deleteMapping() { } @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)") default void requestMapping() { } @Pointcut("controllerMethod() && (requestMapping() || postMapping() || getMapping() || putMapping() || deleteMapping())") default void apiMethod() { } } `[5. 解决请求体只能读取一次]解决方案就是包装请求，缓存请求体。SpringBoot也提供了ContentCachingRequestWrapper来解决这个问题。但是第4点中也详细描述了，由于它的缓存时机，所以它的使用有限制条件。也可以参考网上的方案，自己实现一个请求的包装类来缓存请求体 `import lombok.NonNull; import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.stereotype.Component; import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.ContentCachingRequestWrapper; import top.ysqorz.signature.model.SignatureProps; import javax.servlet.FilterChain; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; @ConditionalOnBean(SignatureProps.class) @Component public class RequestCachingFilter extends OncePerRequestFilter { /** * This {@code doFilter} implementation stores a request attribute for * "already filtered", proceeding without filtering again if the * attribute is already there. * * @param request request * @param response response * @param filterChain filterChain * @see #getAlreadyFilteredAttributeName * @see #shouldNotFilter * @see #doFilterInternal */ @Override protected void doFilterInternal(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull FilterChain filterChain) throws ServletException, IOException { boolean isFirstRequest = !isAsyncDispatch(request); HttpServletRequest requestWrapper = request; if (isFirstRequest && !(request instanceof ContentCachingRequestWrapper)) { requestWrapper = new ContentCachingRequestWrapper(request); } filterChain.doFilter(requestWrapper, response); } } `注册过滤器 `import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; import org.springframework.boot.web.servlet.FilterRegistrationBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import top.ysqorz.signature.model.SignatureProps; @Configuration public class FilterConfig { @ConditionalOnBean(SignatureProps.class) @Bean public FilterRegistrationBean<RequestCachingFilter> requestCachingFilterRegistration( RequestCachingFilter requestCachingFilter) { FilterRegistrationBean<RequestCachingFilter> bean = new FilterRegistrationBean<>(requestCachingFilter); bean.setOrder(1); return bean; } } `[6. 自定义工具类] `import cn.hutool.core.util.StrUtil; import org.springframework.lang.Nullable; import org.springframework.util.ObjectUtils; import java.util.Arrays; import java.util.Map; import java.util.stream.Collectors; public class CommonUtils { /** * 提取所有的请求参数，按照固定规则拼接成一个字符串 * * @param body post请求的请求体 * @param paramMap 路径参数(QueryString)。形如：name=zhangsan&age=18&label=A&label=B * @param uriTemplateVarNap 路径变量(PathVariable)。形如：/{name}/{age} * @return 所有的请求参数按照固定规则拼接成的一个字符串 */ public static String extractRequestParams(@Nullable String body, @Nullable Map<String, String[]> paramMap, @Nullable Map<String, String> uriTemplateVarNap) { // body: { userID: "xxx" } // 路径参数 // name=zhangsan&age=18&label=A&label=B // => ["name=zhangsan", "age=18", "label=A,B"] // => name=zhangsan&age=18&label=A,B String paramStr = null; if (!ObjectUtils.isEmpty(paramMap)) { paramStr = paramMap.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .map(entry -> { // 拷贝一份按字典序升序排序 String[] sortedValue = Arrays.stream(entry.getValue()).sorted().toArray(String[]::new); return entry.getKey() + "=" + joinStr(",", sortedValue); }) .collect(Collectors.joining("&")); } // 路径变量 // /{name}/{age} => /zhangsan/18 => zhangsan,18 String uriVarStr = null; if (!ObjectUtils.isEmpty(uriTemplateVarNap)) { uriVarStr = joinStr(",", uriTemplateVarNap.values().stream().sorted().toArray(String[]::new)); } // { userID: "xxx" }#name=zhangsan&age=18&label=A,B#zhangsan,18 return joinStr("#", body, paramStr, uriVarStr); } /** * 使用指定分隔符，拼接字符串 * * @param delimiter 分隔符 * @param strs 需要拼接的多个字符串，可以为null * @return 拼接后的新字符串 */ public static String joinStr(String delimiter, @Nullable String... strs) { if (ObjectUtils.isEmpty(strs)) { return StrUtil.EMPTY; } StringBuilder sbd = new StringBuilder(); for (int i = 0; i < strs.length; i++) { if (ObjectUtils.isEmpty(strs[i])) { continue; } sbd.append(strs[i].trim()); if (!ObjectUtils.isEmpty(sbd) && i < strs.length - 1 && !ObjectUtils.isEmpty(strs[i + 1])) { sbd.append(delimiter); } } return sbd.toString(); } } `
识别代理工具流量特征：从SSR、V2Ray、Clash到Trojan1概述在今天的互联网世界中，代理工具的使用旨在绕过网络审查、保护隐私、提供更安全的网络通信，而其中一些代理工具，如ShadowsocksR（SSR）、V2Ray、Clash和Trojan等，由于其高度定制化和加密特性而备受欢迎。然而，对于一些网络管理员和监管机构来说，他们可能希望能够识别这些代理工具的流量特征，以便监控或限制其使用。本文将介绍如何更加深入地识别这些代理工具的流量特征。 2深度包检测深度包检测是一种常见的用于识别代理工具流量特征的方法。这种方法侧重于对网络数据包的内容进行详细分析，尤其是检测一些特定的加密协议或自定义协议。 ShadowsocksR（SSR）：SSR通常使用自定义的混淆协议来隐藏流量。识别SSR流量的方法包括检测一些特定的标志或数据包格式，但这通常需要深度包检测技术来实现。例如，SSR的数据包中可能包含Base64编码，通过分析数据包的Base64编码是否遵循SSR的特定模式，可以更准确地识别SSR流量。V2Ray：V2Ray也使用自定义协议，其流量特点包括一些特定的数据包结构和加密方式。通过检测这些特征，可以辨别V2Ray流量。例如，V2Ray的数据包通常以VMess协议进行通信，可以识别VMess数据包的特征字段，如alterId等，来确认V2Ray的存在。3特定的流量模式不同的代理工具通常会展现出独特的流量模式，包括数据包大小、通信模式以及流量特点。 Clash：Clash是一种代理工具，它允许用户配置多种代理协议，包括HTTP、SOCKS、VMess等。通过观察流量的目标端口和通信模式，可以推测出Clash的可能存在。例如，如果流量以HTTP协议进行通信，那么可能是Clash的迹象。Trojan：Trojan代理工具也有一些流量特征，例如使用的自定义协议和端口，以及加密方式。这些特点可以被用于识别Trojan流量。举例来说，Trojan通常使用自定义的密码加密流量，通过检测流量中的特定密码标识，可以确定Trojan的存在。4端口扫描代理工具通常使用非标准端口进行通信，而不是常见的HTTP或HTTPS端口。因此，监控非常用端口的流量可能有助于识别这些代理工具。端口号：ShadowsocksR、V2Ray和Trojan等代理工具通常会使用自定义端口号，而不是常见的80或443端口。监控端口号的活动可以帮助识别代理工具的流量。例如，如果网络上的非标准端口出现了大量传输数据的活动，那么可能存在代理工具的使用。5行为分析行为分析是一种重要的方法，它关注的是网络中终端和流量的行为特征，而不仅仅是数据包的结构或端口号。以下是关于行为分析在识别代理工具流量特征中的方法：数据传输频率：代理工具通常被用于加速网络访问或绕过审查。因此，它们可能在相对短的时间内传输大量数据。通过检测网络中数据传输频率异常高的终端，可以怀疑可能存在代理工具的使用。这种高频率传输数据的行为可能是识别代理工具的一个关键指标。响应时间：代理工具的特点之一是快速响应。在网络通信中，代理工具通常能够更快地建立连接和传输数据。通过监测终端的响应时间，可以检测到网络中是否存在代理工具。如果某个终端在建立连接和传输数据时的响应时间明显较短，可能是代理工具的存在信号。目标端口扫描：代理工具通常会在不同的端口上建立连接，以绕过网络防火墙或审查。通过监测终端对不同端口的连接行为，可以检测出代理工具的存在。代理工具可能会尝试多个端口，以找到一个可以正常工作的通道。数据流模式：代理工具通常会呈现出独特的数据流模式，这些模式可能与正常的网络通信模式有所不同。行为分析可以涵盖这些不同之处，以检测代理工具的存在。通过观察数据流的模式，可以识别出异常的、可能是代理工具引起的数据流。活动时间：代理工具的使用可能会在某些时间段内更为活跃，因此监控终端的活动时间可以帮助确定代理工具的存在。如果某个终端在非常规的时间段内产生大量的网络活动，这可能是代理工具的使用的迹象。非标准协议：代理工具通常使用非标准的或自定义协议进行通信。通过分析终端的通信协议，可以检测出非常见的协议，这可能是代理工具的存在迹象。需要指出的是，行为分析是一项综合性工作，需要对网络流量和终端行为有深入的理解。同时，正常的网络活动也可能具有类似的特点，因此需要与其他方法结合使用，以提高识别的准确性。6流量统计通过统计网络流量，特别关注未知流量来源或流量模式不规律的情况，也可以用来识别代理工具的流量特征。 不规律的流量模式：一些代理工具的流量模式可能会显得不规律，特别是当它们用于突破网络审查时。通过检测这种不规律的流量模式，可以识别代理工具的存在。例如，如果网络上出现了突然增加的不规律流量，可能需要进一步调查其来源并识别是否有代理工具的参与。需要注意的是，这些方法并不是绝对可靠的，因为代理工具的不断演化可能导致它们采取更高级的措施来隐藏其流量特征。因此，为了提高识别的准确性，可能需要结合多种技术手段和算法，也需要不断更新和调整识别方法。最重要的是，监控网络流量和识别代理工具的目的应该是合法的，并且应该遵循相关的法律和政策。7总结总结来说，识别代理工具的流量特征需要多种技术手段和方法的结合，以便更精确地检测它们的存在。随着代理工具的不断演化，网络管理员和监管机构需要不断改进他们的识别技术，以应对新的挑战。识别代理工具的流量特征不仅有助于网络安全，还有助于维护网络的稳定性和合法性。 END
Linux 快速换源yum install -y wget ＃必备工具 ＃使用之前请确保已经安装wget，如未安装请执行下面一条命令来安装 ＃备份原来的源 sudo mv /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.bk ＃下载阿里源 cd /etc/yum.repos.d wget -nc http://mirrors.aliyun.com/repo/Centos-7.repo ＃更改阿里yum源为默认源 sudo mv Centos-7.repo CentOS-Base.repo ＃更新本地yum缓存 # 全部清除 sudo yum clean all # 更新列表 sudo yum list # 缓存yum包信息到本机，提高搜索速度 sudo yum makecache网卡配置错误Cannot find a valid baseurl for repo: base/7/x86_64# 解决办法： # 修改网卡配置文件： vi /etc/sysconfig/network-scripts/ifcfg-eno16777736 # (注意：如果显示打开的是新文件，可能你的网卡的文件不叫ifcfg-eno16777736 ，有的叫ens33，你就使用命令“vi /etc/sysconfig/network-scripts/ifcfg-ens33”，来打开网卡配置文件) # 将onboot修改为“yes” #重启网络服务： service network restart #再使用安装Vim的命令： yum -y install vim* #就可以安装了。yum安装出现另一个程序被锁定Another app is currently holding the yum lock; waiting for it to exit 这句话的意思就是有一个应用程序正在锁定yum，等待退出。# 解决办法： # 1、杀死这个应用程序 #命令： kill -s 9 56722 #kill 的用法： #kill [信号代码] 进程ID #注：信号代码可以省略；我们常用的信号代码是 -s 9 ，表示强制终止； #56722是这个进程的ID # 2、强制关掉yum进程，并重新运行yum rm -f /var/run/yum.pid #注：强制删除：rm -f #解释：强制删除文件： /var/run/yum.pid Centos防火墙相关命令#永久关闭防火墙 systemctl disable firewalld #y systemctl enable firewalld #查看防火墙状态 systemctl status firewalld #关闭防火墙 systemctl stop firewalld #打开防火墙 systemctl start firewalldCentos查看自启动服务#查看自启动的服务 systemctl list-unit-filesCentos hostname相关命令#查看当前主机名 hostname #方法一：命令永久修改主机名 hostnamectl set-hostname 主机名 #方法二 通过修改文件 #修改文件 vi /etc/hostname #添加主机名 Slave01 vi /etc/sysconfig/network #添加IP和hostname对应关系 10.238.162.33 Slave01 #重启reboot生效 #方法三 临时修改主机名 hostname Slave01
报告以邮件发送到邮箱 在log下生成巡检报告。#!/bin/bash #主机信息每日巡检 IPADDR=$(ifconfig eth0|grep 'inet addr'|awk -F '[ :]' '{print $13}') #环境变量PATH没设好，在cron里执行时有很多命令会找不到 export PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/root/bin source /etc/profile [ $(id -u) -gt 0 ] && echo "请用root用户执行此脚本！" && exit 1 centosVersion=$(awk '{print $(NF-1)}' /etc/redhat-release) VERSION="2020-03-16" #日志相关 PROGPATH=`echo $0 | sed -e 's,[\\/][^\\/][^\\/]*$,,'` [ -f $PROGPATH ] && PROGPATH="." LOGPATH="$PROGPATH/log" [ -e $LOGPATH ] || mkdir $LOGPATH RESULTFILE="$LOGPATH/HostDailyCheck-$IPADDR-`date +%Y%m%d`.txt" #定义报表的全局变量 report_DateTime="" #日期 ok report_Hostname="" #主机名 ok report_OSRelease="" #发行版本 ok report_Kernel="" #内核 ok report_Language="" #语言/编码 ok report_LastReboot="" #最近启动时间 ok report_Uptime="" #运行时间（天） ok report_CPUs="" #CPU数量 ok report_CPUType="" #CPU类型 ok report_Arch="" #CPU架构 ok report_MemTotal="" #内存总容量(MB) ok report_MemFree="" #内存剩余(MB) ok report_MemUsedPercent="" #内存使用率% ok report_DiskTotal="" #硬盘总容量(GB) ok report_DiskFree="" #硬盘剩余(GB) ok report_DiskUsedPercent="" #硬盘使用率% ok report_InodeTotal="" #Inode总量 ok report_InodeFree="" #Inode剩余 ok report_InodeUsedPercent="" #Inode使用率 ok report_IP="" #IP地址 ok report_MAC="" #MAC地址 ok report_Gateway="" #默认网关 ok report_DNS="" #DNS ok report_Listen="" #监听 ok report_Selinux="" #Selinux ok report_Firewall="" #防火墙 ok report_USERs="" #用户 ok report_USEREmptyPassword="" #空密码用户 ok report_USERTheSameUID="" #相同ID的用户 ok report_PasswordExpiry="" #密码过期（天） ok report_RootUser="" #root用户 ok report_Sudoers="" #sudo授权 ok report_SSHAuthorized="" #SSH信任主机 ok report_SSHDProtocolVersion="" #SSH协议版本 ok report_SSHDPermitRootLogin="" #允许root远程登录 ok report_DefunctProsess="" #僵尸进程数量 ok report_SelfInitiatedService="" #自启动服务数量 ok report_SelfInitiatedProgram="" #自启动程序数量 ok report_RuningService="" #运行中服务数 ok report_Crontab="" #计划任务数 ok report_Syslog="" #日志服务 ok report_SNMP="" #SNMP OK report_NTP="" #NTP ok report_JDK="" #JDK版本 ok function version(){ echo "" echo "" echo "系统巡检脚本：Version $VERSION" } function getCpuStatus(){ echo "" echo "" echo "############################ CPU检查 #############################" Physical_CPUs=$(grep "physical id" /proc/cpuinfo| sort | uniq | wc -l) Virt_CPUs=$(grep "processor" /proc/cpuinfo | wc -l) CPU_Kernels=$(grep "cores" /proc/cpuinfo|uniq| awk -F ': ' '{print $2}') CPU_Type=$(grep "model name" /proc/cpuinfo | awk -F ': ' '{print $2}' | sort | uniq) CPU_Arch=$(uname -m) echo "物理CPU个数:$Physical_CPUs" echo "逻辑CPU个数:$Virt_CPUs" echo "每CPU核心数:$CPU_Kernels" echo " CPU型号:$CPU_Type" echo " CPU架构:$CPU_Arch" #报表信息 report_CPUs=$Virt_CPUs #CPU数量 report_CPUType=$CPU_Type #CPU类型 report_Arch=$CPU_Arch #CPU架构 } function getMemStatus(){ echo "" echo "" echo "############################ 内存检查 ############################" if [[ $centosVersion < 7 ]];then free -mo else free -h fi #报表信息 MemTotal=$(grep MemTotal /proc/meminfo| awk '{print $2}') #KB MemFree=$(grep MemFree /proc/meminfo| awk '{print $2}') #KB let MemUsed=MemTotal-MemFree MemPercent=$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}") report_MemTotal="$((MemTotal/1024))""MB" #内存总容量(MB) report_MemFree="$((MemFree/1024))""MB" #内存剩余(MB) report_MemUsedPercent="$(awk "BEGIN {if($MemTotal==0){printf 100}else{printf \"%.2f\",$MemUsed*100/$MemTotal}}")""%" #内存使用率% } function getDiskStatus(){ echo "" echo "" echo "############################ 磁盘检查 ############################" df -hiP | sed 's/Mounted on/Mounted/'> /tmp/inode df -hTP | sed 's/Mounted on/Mounted/'> /tmp/disk join /tmp/disk /tmp/inode | awk '{print $1,$2,"|",$3,$4,$5,$6,"|",$8,$9,$10,$11,"|",$12}'| column -t #报表信息 diskdata=$(df -TP | sed '1d' | awk '$2!="tmpfs"{print}') #KB disktotal=$(echo "$diskdata" | awk '{total+=$3}END{print total}') #KB diskused=$(echo "$diskdata" | awk '{total+=$4}END{print total}') #KB diskfree=$((disktotal-diskused)) #KB diskusedpercent=$(echo $disktotal $diskused | awk '{if($1==0){printf 100}else{printf "%.2f",$2*100/$1}}') inodedata=$(df -iTP | sed '1d' | awk '$2!="tmpfs"{print}') inodetotal=$(echo "$inodedata" | awk '{total+=$3}END{print total}') inodeused=$(echo "$inodedata" | awk '{total+=$4}END{print total}') inodefree=$((inodetotal-inodeused)) inodeusedpercent=$(echo $inodetotal $inodeused | awk '{if($1==0){printf 100}else{printf "%.2f",$2*100/$1}}') report_DiskTotal=$((disktotal/1024/1024))"GB" #硬盘总容量(GB) report_DiskFree=$((diskfree/1024/1024))"GB" #硬盘剩余(GB) report_DiskUsedPercent="$diskusedpercent""%" #硬盘使用率% report_InodeTotal=$((inodetotal/1000))"K" #Inode总量 report_InodeFree=$((inodefree/1000))"K" #Inode剩余 report_InodeUsedPercent="$inodeusedpercent""%" #Inode使用率% } function getSystemStatus(){ echo "" echo "" echo "############################ 系统检查 ############################" if [ -e /etc/sysconfig/i18n ];then default_LANG="$(grep "LANG=" /etc/sysconfig/i18n | grep -v "^#" | awk -F '"' '{print $2}')" else default_LANG=$LANG fi export LANG="en_US.UTF-8" Release=$(cat /etc/redhat-release 2>/dev/null) Kernel=$(uname -r) OS=$(uname -o) Hostname=$(uname -n) SELinux=$(/usr/sbin/sestatus | grep "SELinux status: " | awk '{print $3}') LastReboot=$(who -b | awk '{print $3,$4}') uptime=$(uptime | sed 's/.*up \([^,]*\), .*/\1/') echo " 系统：$OS" echo " 发行版本：$Release" echo " 内核：$Kernel" echo " 主机名：$Hostname" echo " SELinux：$SELinux" echo "语言/编码：$default_LANG" echo " 当前时间：$(date +'%F %T')" echo " 最后启动：$LastReboot" echo " 运行时间：$uptime" #报表信息 report_DateTime=$(date +"%F %T") #日期 report_Hostname="$Hostname" #主机名 report_OSRelease="$Release" #发行版本 report_Kernel="$Kernel" #内核 report_Language="$default_LANG" #语言/编码 report_LastReboot="$LastReboot" #最近启动时间 report_Uptime="$uptime" #运行时间（天） report_Selinux="$SELinux" export LANG="$default_LANG" } function getServiceStatus(){ echo "" echo "" echo "############################ 服务检查 ############################" echo "" if [[ $centosVersion > 7 ]];then conf=$(systemctl list-unit-files --type=service --state=enabled --no-pager | grep "enabled") process=$(systemctl list-units --type=service --state=running --no-pager | grep ".service") #报表信息 report_SelfInitiatedService="$(echo "$conf" | wc -l)" #自启动服务数量 report_RuningService="$(echo "$process" | wc -l)" #运行中服务数量 else conf=$(/sbin/chkconfig | grep -E ":on|:启用") process=$(/sbin/service --status-all 2>/dev/null | grep -E "is running|正在运行") #报表信息 report_SelfInitiatedService="$(echo "$conf" | wc -l)" #自启动服务数量 report_RuningService="$(echo "$process" | wc -l)" #运行中服务数量 fi echo "服务配置" echo "--------" echo "$conf" | column -t echo "" echo "正在运行的服务" echo "--------------" echo "$process" } function getAutoStartStatus(){ echo "" echo "" echo "############################ 自启动检查 ##########################" conf=$(grep -v "^#" /etc/rc.d/rc.local| sed '/^$/d') echo "$conf" #报表信息 report_SelfInitiatedProgram="$(echo $conf | wc -l)" #自启动程序数量 } function getLoginStatus(){ echo "" echo "" echo "############################ 登录检查 ############################" last | head } function getNetworkStatus(){ echo "" echo "" echo "############################ 网络检查 ############################" if [[ $centosVersion < 7 ]];then /sbin/ifconfig -a | grep -v packets | grep -v collisions | grep -v inet6 else #ip a for i in $(ip link | grep BROADCAST | awk -F: '{print $2}');do ip add show $i | grep -E "BROADCAST|global"| awk '{print $2}' | tr '\n' ' ' ;echo "" ;done fi GATEWAY=$(ip route | grep default | awk '{print $3}') DNS=$(grep nameserver /etc/resolv.conf| grep -v "#" | awk '{print $2}' | tr '\n' ',' | sed 's/,$//') echo "" echo "网关：$GATEWAY " echo " DNS：$DNS" #报表信息 IP=$(ip -f inet addr | grep -v 127.0.0.1 | grep inet | awk '{print $NF,$2}' | tr '\n' ',' | sed 's/,$//') MAC=$(ip link | grep -v "LOOPBACK\|loopback" | awk '{print $2}' | sed 'N;s/\n//' | tr '\n' ',' | sed 's/,$//') report_IP="$IP" #IP地址 report_MAC=$MAC #MAC地址 report_Gateway="$GATEWAY" #默认网关 report_DNS="$DNS" #DNS } function getListenStatus(){ echo "" echo "" echo "############################ 监听检查 ############################" TCPListen=$(ss -ntul | column -t) echo "$TCPListen" #报表信息 report_Listen="$(echo "$TCPListen"| sed '1d' | awk '/tcp/ {print $5}' | awk -F: '{print $NF}' | sort | uniq | wc -l)" } function getCronStatus(){ echo "" echo "" echo "############################ 计划任务检查 ########################" Crontab=0 for shell in $(grep -v "/sbin/nologin" /etc/shells);do for user in $(grep "$shell" /etc/passwd| awk -F: '{print $1}');do crontab -l -u $user >/dev/null 2>&1 status=$? if [ $status -eq 0 ];then echo "$user" echo "--------" crontab -l -u $user let Crontab=Crontab+$(crontab -l -u $user | wc -l) echo "" fi done done #计划任务 find /etc/cron* -type f | xargs -i ls -l {} | column -t let Crontab=Crontab+$(find /etc/cron* -type f | wc -l) #报表信息 report_Crontab="$Crontab" #计划任务数 } function getHowLongAgo(){ # 计算一个时间戳离现在有多久了 datetime="$*" [ -z "$datetime" ] && echo "错误的参数：getHowLongAgo() $*" Timestamp=$(date +%s -d "$datetime") #转化为时间戳 Now_Timestamp=$(date +%s) Difference_Timestamp=$(($Now_Timestamp-$Timestamp)) days=0;hours=0;minutes=0; sec_in_day=$((60*60*24)); sec_in_hour=$((60*60)); sec_in_minute=60 while (( $(($Difference_Timestamp-$sec_in_day)) > 1 )) do let Difference_Timestamp=Difference_Timestamp-sec_in_day let days++ done while (( $(($Difference_Timestamp-$sec_in_hour)) > 1 )) do let Difference_Timestamp=Difference_Timestamp-sec_in_hour let hours++ done echo "$days 天 $hours 小时前" } function getUserLastLogin(){ # 获取用户最近一次登录的时间，含年份 # 很遗憾last命令不支持显示年份，只有"last -t YYYYMMDDHHMMSS"表示某个时间之间的登录，我 # 们只能用最笨的方法了，对比今天之前和今年元旦之前（或者去年之前和前年之前……）某个用户 # 登录次数，如果登录统计次数有变化，则说明最近一次登录是今年。 username=$1 : ${username:="`whoami`"} thisYear=$(date +%Y) oldesYear=$(last | tail -n1 | awk '{print $NF}') while(( $thisYear >= $oldesYear));do loginBeforeToday=$(last $username | grep $username | wc -l) loginBeforeNewYearsDayOfThisYear=$(last $username -t $thisYear"0101000000" | grep $username | wc -l) if [ $loginBeforeToday -eq 0 ];then echo "从未登录过" break elif [ $loginBeforeToday -gt $loginBeforeNewYearsDayOfThisYear ];then lastDateTime=$(last -i $username | head -n1 | awk '{for(i=4;i<(NF-2);i++)printf"%s ",$i}')" $thisYear" #格式如: Sat Nov 2 20:33 2015 lastDateTime=$(date "+%Y-%m-%d %H:%M:%S" -d "$lastDateTime") echo "$lastDateTime" break else thisYear=$((thisYear-1)) fi done } function getUserStatus(){ echo "" echo "" echo "############################ 用户检查 ############################" #/etc/passwd 最后修改时间 pwdfile="$(cat /etc/passwd)" Modify=$(stat /etc/passwd | grep Modify | tr '.' ' ' | awk '{print $2,$3}') echo "/etc/passwd 最后修改时间：$Modify ($(getHowLongAgo $Modify))" echo "" echo "特权用户" echo "--------" RootUser="" for user in $(echo "$pwdfile" | awk -F: '{print $1}');do if [ $(id -u $user) -eq 0 ];then echo "$user" RootUser="$RootUser,$user" fi done echo "" echo "用户列表" echo "--------" USERs=0 echo "$( echo "用户名 UID GID HOME SHELL 最后一次登录" for shell in $(grep -v "/sbin/nologin" /etc/shells);do for username in $(grep "$shell" /etc/passwd| awk -F: '{print $1}');do userLastLogin="$(getUserLastLogin $username)" echo "$pwdfile" | grep -w "$username" |grep -w "$shell"| awk -F: -v lastlogin="$(echo "$userLastLogin" | tr ' ' '_')" '{print $1,$3,$4,$6,$7,lastlogin}' done let USERs=USERs+$(echo "$pwdfile" | grep "$shell"| wc -l) done )" | column -t echo "" echo "空密码用户" echo "----------" USEREmptyPassword="" for shell in $(grep -v "/sbin/nologin" /etc/shells);do for user in $(echo "$pwdfile" | grep "$shell" | cut -d: -f1);do r=$(awk -F: '$2=="!!"{print $1}' /etc/shadow | grep -w $user) if [ ! -z $r ];then echo $r USEREmptyPassword="$USEREmptyPassword,"$r fi done done echo "" echo "相同ID的用户" echo "------------" USERTheSameUID="" UIDs=$(cut -d: -f3 /etc/passwd | sort | uniq -c | awk '$1>1{print $2}') for uid in $UIDs;do echo -n "$uid"; USERTheSameUID="$uid" r=$(awk -F: 'ORS="";$3=='"$uid"'{print ":",$1}' /etc/passwd) echo "$r" echo "" USERTheSameUID="$USERTheSameUID $r," done #报表信息 report_USERs="$USERs" #用户 report_USEREmptyPassword=$(echo $USEREmptyPassword | sed 's/^,//') report_USERTheSameUID=$(echo $USERTheSameUID | sed 's/,$//') report_RootUser=$(echo $RootUser | sed 's/^,//') #特权用户 } function getPasswordStatus { echo "" echo "" echo "############################ 密码检查 ############################" pwdfile="$(cat /etc/passwd)" echo "" echo "密码过期检查" echo "------------" result="" for shell in $(grep -v "/sbin/nologin" /etc/shells);do for user in $(echo "$pwdfile" | grep "$shell" | cut -d: -f1);do get_expiry_date=$(/usr/bin/chage -l $user | grep 'Password expires' | cut -d: -f2) if | $get_expiry_date = 'never' ;then printf "%-15s 永不过期\n" $user result="$result,$user:never" else password_expiry_date=$(date -d "$get_expiry_date" "+%s") current_date=$(date "+%s") diff=$(($password_expiry_date-$current_date)) let DAYS=$(($diff/(60*60*24))) printf "%-15s %s天后过期\n" $user $DAYS result="$result,$user:$DAYS days" fi done done report_PasswordExpiry=$(echo $result | sed 's/^,//') echo "" echo "密码策略检查" echo "------------" grep -v "#" /etc/login.defs | grep -E "PASS_MAX_DAYS|PASS_MIN_DAYS|PASS_MIN_LEN|PASS_WARN_AGE" } function getSudoersStatus(){ echo "" echo "" echo "############################ Sudoers检查 #########################" conf=$(grep -v "^#" /etc/sudoers| grep -v "^Defaults" | sed '/^$/d') echo "$conf" echo "" #报表信息 report_Sudoers="$(echo $conf | wc -l)" } function getInstalledStatus(){ echo "" echo "" echo "############################ 软件检查 ############################" rpm -qa --last | head | column -t } function getProcessStatus(){ echo "" echo "" echo "############################ 进程检查 ############################" if [ $(ps -ef | grep defunct | grep -v grep | wc -l) -ge 1 ];then echo "" echo "僵尸进程"; echo "--------" ps -ef | head -n1 ps -ef | grep defunct | grep -v grep fi echo "" echo "内存占用TOP10" echo "-------------" echo -e "PID %MEM RSS COMMAND $(ps aux | awk '{print $2, $4, $6, $11}' | sort -k3rn | head -n 10 )"| column -t echo "" echo "CPU占用TOP10" echo "------------" top b -n1 | head -17 | tail -11 #报表信息 report_DefunctProsess="$(ps -ef | grep defunct | grep -v grep|wc -l)" } function getJDKStatus(){ echo "" echo "" echo "############################ JDK检查 #############################" java -version 2>/dev/null if [ $? -eq 0 ];then java -version 2>&1 fi echo "JAVA_HOME=\"$JAVA_HOME\"" #报表信息 report_JDK="$(java -version 2>&1 | grep version | awk '{print $1,$3}' | tr -d '"')" } function getSyslogStatus(){ echo "" echo "" echo "############################ syslog检查 ##########################" echo "服务状态：$(getState rsyslog)" echo "" echo "/etc/rsyslog.conf" echo "-----------------" cat /etc/rsyslog.conf 2>/dev/null | grep -v "^#" | grep -v "^\\$" | sed '/^$/d' | column -t #报表信息 report_Syslog="$(getState rsyslog)" } function getFirewallStatus(){ echo "" echo "" echo "############################ 防火墙检查 ##########################" #防火墙状态，策略等 if [[ $centosVersion < 7 ]];then /etc/init.d/iptables status >/dev/null 2>&1 status=$? if [ $status -eq 0 ];then s="active" elif [ $status -eq 3 ];then s="inactive" elif [ $status -eq 4 ];then s="permission denied" else s="unknown" fi else s="$(getState iptables)" fi echo "iptables: $s" echo "" echo "/etc/sysconfig/iptables" echo "-----------------------" cat /etc/sysconfig/iptables 2>/dev/null #报表信息 report_Firewall="$s" } function getSNMPStatus(){ #SNMP服务状态，配置等 echo "" echo "" echo "############################ SNMP检查 ############################" status="$(getState snmpd)" echo "服务状态：$status" echo "" if [ -e /etc/snmp/snmpd.conf ];then echo "/etc/snmp/snmpd.conf" echo "--------------------" cat /etc/snmp/snmpd.conf 2>/dev/null | grep -v "^#" | sed '/^$/d' fi #报表信息 report_SNMP="$(getState snmpd)" } function getState(){ if [[ $centosVersion < 7 ]];then if [ -e "/etc/init.d/$1" ];then if [ `/etc/init.d/$1 status 2>/dev/null | grep -E "is running|正在运行" | wc -l` -ge 1 ];then r="active" else r="inactive" fi else r="unknown" fi else #CentOS 7+ r="$(systemctl is-active $1 2>&1)" fi echo "$r" } function getSSHStatus(){ #SSHD服务状态，配置,受信任主机等 echo "" echo "" echo "############################ SSH检查 #############################" #检查受信任主机 pwdfile="$(cat /etc/passwd)" echo "服务状态：$(getState sshd)" Protocol_Version=$(cat /etc/ssh/sshd_config | grep Protocol | awk '{print $2}') echo "SSH协议版本：$Protocol_Version" echo "" echo "信任主机" echo "--------" authorized=0 for user in $(echo "$pwdfile" | grep /bin/bash | awk -F: '{print $1}');do authorize_file=$(echo "$pwdfile" | grep -w $user | awk -F: '{printf $6"/.ssh/authorized_keys"}') authorized_host=$(cat $authorize_file 2>/dev/null | awk '{print $3}' | tr '\n' ',' | sed 's/,$//') if [ ! -z $authorized_host ];then echo "$user 授权 \"$authorized_host\" 无密码访问" fi let authorized=authorized+$(cat $authorize_file 2>/dev/null | awk '{print $3}'|wc -l) done echo "" echo "是否允许ROOT远程登录" echo "--------------------" config=$(cat /etc/ssh/sshd_config | grep PermitRootLogin) firstChar=${config:0:1} if [ $firstChar == "#" ];then PermitRootLogin="yes" #默认是允许ROOT远程登录的 else PermitRootLogin=$(echo $config | awk '{print $2}') fi echo "PermitRootLogin $PermitRootLogin" echo "" echo "/etc/ssh/sshd_config" echo "--------------------" cat /etc/ssh/sshd_config | grep -v "^#" | sed '/^$/d' #报表信息 report_SSHAuthorized="$authorized" #SSH信任主机 report_SSHDProtocolVersion="$Protocol_Version" #SSH协议版本 report_SSHDPermitRootLogin="$PermitRootLogin" #允许root远程登录 } function getNTPStatus(){ #NTP服务状态，当前时间，配置等 echo "" echo "" echo "############################ NTP检查 #############################" if [ -e /etc/ntp.conf ];then echo "服务状态：$(getState ntpd)" echo "" echo "/etc/ntp.conf" echo "-------------" cat /etc/ntp.conf 2>/dev/null | grep -v "^#" | sed '/^$/d' fi #报表信息 report_NTP="$(getState ntpd)" } function uploadHostDailyCheckReport(){ json="{ \"DateTime\":\"$report_DateTime\", \"Hostname\":\"$report_Hostname\", \"OSRelease\":\"$report_OSRelease\", \"Kernel\":\"$report_Kernel\", \"Language\":\"$report_Language\", \"LastReboot\":\"$report_LastReboot\", \"Uptime\":\"$report_Uptime\", \"CPUs\":\"$report_CPUs\", \"CPUType\":\"$report_CPUType\", \"Arch\":\"$report_Arch\", \"MemTotal\":\"$report_MemTotal\", \"MemFree\":\"$report_MemFree\", \"MemUsedPercent\":\"$report_MemUsedPercent\", \"DiskTotal\":\"$report_DiskTotal\", \"DiskFree\":\"$report_DiskFree\", \"DiskUsedPercent\":\"$report_DiskUsedPercent\", \"InodeTotal\":\"$report_InodeTotal\", \"InodeFree\":\"$report_InodeFree\", \"InodeUsedPercent\":\"$report_InodeUsedPercent\", \"IP\":\"$report_IP\", \"MAC\":\"$report_MAC\", \"Gateway\":\"$report_Gateway\", \"DNS\":\"$report_DNS\", \"Listen\":\"$report_Listen\", \"Selinux\":\"$report_Selinux\", \"Firewall\":\"$report_Firewall\", \"USERs\":\"$report_USERs\", \"USEREmptyPassword\":\"$report_USEREmptyPassword\", \"USERTheSameUID\":\"$report_USERTheSameUID\", \"PasswordExpiry\":\"$report_PasswordExpiry\", \"RootUser\":\"$report_RootUser\", \"Sudoers\":\"$report_Sudoers\", \"SSHAuthorized\":\"$report_SSHAuthorized\", \"SSHDProtocolVersion\":\"$report_SSHDProtocolVersion\", \"SSHDPermitRootLogin\":\"$report_SSHDPermitRootLogin\", \"DefunctProsess\":\"$report_DefunctProsess\", \"SelfInitiatedService\":\"$report_SelfInitiatedService\", \"SelfInitiatedProgram\":\"$report_SelfInitiatedProgram\", \"RuningService\":\"$report_RuningService\", \"Crontab\":\"$report_Crontab\", \"Syslog\":\"$report_Syslog\", \"SNMP\":\"$report_SNMP\", \"NTP\":\"$report_NTP\", \"JDK\":\"$report_JDK\" }" #echo "$json" curl -l -H "Content-type: application/json" -X POST -d "$json" "$uploadHostDailyCheckReportApi" 2>/dev/null } function getchage_file_24h() { echo "############################ 文件检查 #############################" check2=$(find / -name '*.sh' -mtime -1) check21=$(find / -name '*.asp' -mtime -1) check22=$(find / -name '*.php' -mtime -1) check23=$(find / -name '*.aspx' -mtime -1) check24=$(find / -name '*.jsp' -mtime -1) check25=$(find / -name '*.html' -mtime -1) check26=$(find / -name '*.htm' -mtime -1) check9=$(find / -name core -exec ls -l {} \;) check10=$(cat /etc/crontab) check12=$(ls -alt /usr/bin | head -10) cat <<EOF ############################查看所有被修改过的文件返回最近24小时内的############################ ${check2} ${check21} ${check22} ${check23} ${check24} ${check25} ${check26} ${line} ############################检查定时文件的完整性############################ ${check10} ${line} ############################查看系统命令是否被替换############################ ${check12} ${line} EOF } function check(){ version getSystemStatus getCpuStatus getMemStatus getDiskStatus getNetworkStatus getListenStatus getProcessStatus getServiceStatus getAutoStartStatus getLoginStatus getCronStatus getUserStatus getPasswordStatus getSudoersStatus getJDKStatus getFirewallStatus getSSHStatus getSyslogStatus getSNMPStatus getNTPStatus getInstalledStatus getchage_file_24h } #执行检查并保存检查结果 check > $RESULTFILE echo "检查结果：$RESULTFILE" echo -e "`date "+%Y-%m-%d %H:%M:%S"` 阿里云PHP企业平台巡检报告" | mail -a $RESULTFILE -s "阿里云PHP企业平台巡检报告" h@163.com END
1）编写 hello world 脚本#!/bin/bash #编写hello world脚本 echo "Hello World!"2）通过位置变量创建 Linux 系统账户及密码#!/bin/bash #通过位置变量创建 Linux 系统账户及密码 #$1 是执行脚本的第一个参数,$2 是执行脚本的第二个参数 useradd "$1" echo "$2" | passwd ‐‐stdin "$1"3）备份日志#!/bin/bash #每周 5 使用 tar 命令备份/var/log 下的所有日志文件 #vim /root/logbak.sh #编写备份脚本,备份后的文件名包含日期标签,防止后面的备份将前面的备份数据覆盖 #注意 date 命令需要使用反引号括起来,反引号在键盘<tab>键上面 tar -czf log-`date +%Y%m%d`.tar.gz /var/log crontab ‐e #编写计划任务,执行备份脚本 00 03 * * 5 /root/logbak.sh4）一键部署 LNMP(RPM 包版本)#!/bin/bash #一键部署 LNMP(RPM 包版本)# 使用 yum 安装部署 LNMP,需要提前配置好 yum 源,否则该脚本会失败# 本脚本使用于 centos7.2 或 RHEL7.2 yum ‐y install httpd yum ‐y install mariadb mariadb‐devel mariadb‐server yum ‐y install php php‐mysql systemctl start httpd mariadb systemctl enable httpd mariadb5）监控内存和磁盘容量，小于给定值时报警#!/bin/bash #实时监控本机内存和硬盘剩余空间,剩余内存小于500M、根分区剩余空间小于1000M时,发送报警邮件给root管理员 #提取根分区剩余空间 disk_size=$(df / | awk '/\//{print $4}') #提取内存剩余空间 mem_size=$(free | awk '/Mem/{print $4}') while :do #注意内存和磁盘提取的空间大小都是以 Kb 为单位 if [ $disk_size -le 512000 -a $mem_size -le 1024000 ] then mail ‐s "Warning" root <<EOF Insufficient resources,资源不足 EOF fi done6）猜数字游戏#!/bin/bash #脚本生成一个 100 以内的随机数,提示用户猜数字,根据用户的输入,提示用户猜对了, #猜小了或猜大了,直至用户猜对脚本结束。 #RANDOM 为系统自带的系统变量,值为 0‐32767的随机数 #使用取余算法将随机数变为 1‐100 的随机数 num=$[RANDOM%100+1] echo "$num" #使用 read 提示用户猜数字#使用 if 判断用户猜数字的大小关系:‐eq(等于),‐ne(不等于),‐gt(大于),‐ge(大于等于),‐lt(小于),‐le(小于等于) while : do read -p "计算机生成了一个 1‐100 的随机数,你猜: " cai if [ $cai -eq $num ] then echo "恭喜,猜对了" exit elif [ $cai -gt $num ] then echo "Oops,猜大了" else echo "Oops,猜小了" fi done7）检测本机当前用户是否为超级管理员, 如果是管理员, 则使用 yum 安装 vsftpd, 如果不是, 则提示您非管理员 (使用字串对比版本)#!/bin/bash #检测本机当前用户是否为超级管理员,如果是管理员,则使用 yum 安装 vsftpd,如果不 #是,则提示您非管理员(使用字串对比版本) if [ $USER == "root" ] then yum ‐y install vsftpd else echo "您不是管理员,没有权限安装软件" fi8）检测本机当前用户是否为超级管理员, 如果是管理员, 则使用 yum 安装 vsftpd, 如果不是, 则提示您非管理员 (使用 UID 数字对比版本)#!/bin/bash #检测本机当前用户是否为超级管理员,如果是管理员,则使用 yum 安装 vsftpd,如果不是,则提示您非管理员(使用 UID 数字对比版本) if [ $UID -eq 0 ]; then yum ‐y install vsftpd else echo "您不是管理员,没有权限安装软件" fi9）编写脚本: 提示用户输入用户名和密码, 脚本自动创建相应的账户及配置密码。如果用户不输入账户名, 则提示必须输入账户名并退出脚本; 如果用户不输入密码, 则统一使用默认的 123456 作为默认密码。#!/bin/bash #编写脚本:提示用户输入用户名和密码,脚本自动创建相应的账户及配置密码。如果用户 #不输入账户名,则提示必须输入账户名并退出脚本;如果用户不输入密码,则统一使用默 #认的 123456 作为默认密码。 read -p "请输入用户名: " user #使用‐z 可以判断一个变量是否为空,如果为空,提示用户必须输入账户名,并退出脚本,退出码为 2 #没有输入用户名脚本退出后,使用$?查看的返回码为 2 if [ -z $user ]; then echo "您不需输入账户名" exit 2 fi #使用 stty ‐echo 关闭 shell 的回显功能 #使用 stty echo 打开 shell 的回显功能 stty -echo read -p "请输入密码: " pass stty echo pass=${pass:‐123456} useradd "$user" echo "$pass" | passwd ‐‐stdin "$user"10）输入三个数并进行升序排序#!/bin/bash #依次提示用户输入 3 个整数,脚本根据数字大小依次排序输出 3 个数字 read -p "请输入一个整数:" num1 read -p "请输入一个整数:" num2 read -p "请输入一个整数:" num3 #不管谁大谁小,最后都打印 echo "$num1,$num2,$num3" #num1 中永远存最小的值,num2 中永远存中间值,num3 永远存最大值 #如果输入的不是这样的顺序,则改变数的存储顺序,如:可以将 num1 和 num2 的值对调 tmp=0 #如果 num1 大于 num2,就把 num1 和和 num2 的值对调,确保 num1 变量中存的是最小值 if [ $num1 -gt $num2 ]; then tmp=$num1 num1=$num2 num2=$tmp fi #如果 num1 大于 num3,就把 num1 和 num3 对调,确保 num1 变量中存的是最小值 if [ $num1 -gt $num3 ]; then tmp=$num1 num1=$num3 num3=$tmp fi #如果 num2 大于 num3,就把 num2 和 num3 对标,确保 num2 变量中存的是小一点的值 if [ $num2 -gt $num3 ]; then tmp=$num2 num2=$num3 num3=$tmp fi echo "排序后数据(从小到大)为:$num1,$num2,$num3"11）石头、剪刀、布游戏#!/bin/bash #编写脚本,实现人机<石头,剪刀,布>游戏game=(石头 剪刀 布) num=$[RANDOM%3]computer=${game[$num]} #通过随机数获取计算机的出拳# 出拳的可能性保存在一个数组中,game[0],game[1],game[2]分别是 3 中不同的可能 echo "请根据下列提示选择您的出拳手势" echo "1.石头" echo "2.剪刀" echo "3.布" read -p "请选择 1‐3:" person case $person in 1) if [ $num -eq 0 ] then echo "平局" elif [ $num -eq 1 ] then echo "你赢" else echo "计算机赢" fi;; 2) if [ $num -eq 0 ] then echo "计算机赢" elif [ $num -eq 1 ] then echo "平局" else echo "你赢" fi;; 3) if [ $num -eq 0 ] then echo "你赢" elif [ $num -eq 1 ] then echo "计算机赢" else echo "平局" fi;; *) echo "必须输入 1‐3 的数字" esac12）编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态, 哪些主机处于关机状态 (for 版本)#!/bin/bash #编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态,哪些主机处于关机 #状态(for 版本) for i in {1..254} do #每隔0.3秒ping一次，一共ping2次，并以1毫秒为单位设置ping的超时时间 ping ‐c 2 ‐i 0.3 ‐W 1 192.168.4.$i &>/dev/null if [ $? -eq 0 ]; then echo "192.168.4.$i is up" else echo "192.168.4.$i is down" fi done13）编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态, 哪些主机处于关机状态 (while 版本)#!/bin/bash #编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态,哪些主机处于关机 #状态(while 版本) i=1 while [ $i -le 254 ] do ping ‐c 2 ‐i 0.3 ‐W 1 192.168.4.$i &>/dev/null if [ $? -eq 0 ]; then echo "192.168.4.$i is up" else echo "192.168.4.$i is down" fi let i++ done14）编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态, 哪些主机处于关机状态 (多进程版)#!/bin/bash #编写脚本测试 192.168.4.0/24 整个网段中哪些主机处于开机状态,哪些主机处于关机 #状态(多进程版) #定义一个函数,ping 某一台主机,并检测主机的存活状态 myping() { ping ‐c 2 ‐i 0.3 ‐W 1 $1 &>/dev/null if [ $? -eq 0 ]; then echo "$1 is up" else echo "$1 is down" fi } for i in {1..254} do myping 192.168.4.$i & done #使用&符号,将执行的函数放入后台执行 #这样做的好处是不需要等待ping第一台主机的回应,就可以继续并发ping第二台主机,依次类推。15）编写脚本, 显示进度条#!/bin/bash #编写脚本,显示进度条 jindu() { while : do echo -n '#' sleep 0.2 done } jindu & cp -a $1 $2 killall $0 echo "拷贝完成"16）进度条, 动态时针版本；定义一个显示进度的函数, 屏幕快速显示 | / ‐ \#!/bin/bash #进度条,动态时针版本 #定义一个显示进度的函数,屏幕快速显示| / ‐ \ rotate_line() { INTERVAL=0.5 #设置间隔时间 COUNT="0" #设置4个形状的编号,默认编号为 0(不代表任何图像) while : do COUNT=`expr $COUNT + 1` #执行循环,COUNT 每次循环加 1,(分别代表4种不同的形状) case $COUNT in #判断 COUNT 的值,值不一样显示的形状就不一样 "1") #值为 1 显示‐ echo -e '‐'"\b\c" sleep $INTERVAL ;; "2") #值为 2 显示\\,第一个\是转义 echo -e '\\'"\b\c" sleep $INTERVAL ;; "3") #值为 3 显示| echo -e "|\b\c" sleep $INTERVAL ;; "4") #值为 4 显示/ echo -e "/\b\c" sleep $INTERVAL ;; *) #值为其他时,将 COUNT 重置为 0 COUNT="0";; esac done } rotate_line17）9*9 乘法表#!/bin/bash #9*9 乘法表(编写 shell 脚本,打印 9*9 乘法表) for i in `seq 9` do for j in `seq $i` do echo -n "$j*$i=$[i*j] " done echo done18）使用死循环实时显示 eth0 网卡发送的数据包流量#!/bin/bash #使用死循环实时显示 eth0 网卡发送的数据包流量 while : do echo '本地网卡 eth0 流量信息如下: ' ifconfig eth0 | grep "RX pack" | awk '{print $5}' ifconfig eth0 | grep "TX pack" | awk '{print $5}' sleep 1 done19）使用 user.txt 文件中的人员名单, 在计算机中自动创建对应的账户并配置初始密码本脚本执行, 需要提前准备一个 user.txt 文件, 该文件中包含有若干用户名信息#!/bin/bash #使用 user.txt 文件中的人员名单,在计算机中自动创建对应的账户并配置初始密码 #本脚本执行,需要提前准备一个 user.txt 文件,该文件中包含有若干用户名信息 for i in `cat user.txt` do useradd $i echo "123456" | passwd ‐‐stdin $i done20）编写批量修改扩展名脚本#!/bin/bash #编写批量修改扩展名脚本,如批量将 txt 文件修改为 doc 文件 #执行脚本时,需要给脚本添加位置参数# 脚本名 txt doc(可以将 txt 的扩展名修改为 doc) #脚本名 doc jpg(可以将 doc 的扩展名修改为 jpg) for i in `ls *.$1` do mv $i ${i%.*}.$2 done21）使用 expect 工具自动交互密码远程其他主机安装 httpd 软件#!/bin/bash #使用 expect 工具自动交互密码远程其他主机安装 httpd 软件 #删除~/.ssh/known_hosts 后,ssh 远程任何主机都会询问是否确认要连接该主机 rm ‐rf ~/.ssh/known_hostsexpect <<EOF spawn ssh 192.168.4.254 expect "yes/no" {send "yes\r"} #根据自己的实际情况将密码修改为真实的密码字串 expect "password" {send "密码\r"} expect "#" {send "yum ‐y install httpd\r"} expect "#" {send "exit\r"} EOF22）一键部署 LNMP(源码安装版本)#!/bin/bash #一键部署 LNMP(源码安装版本) menu(){ clear echo " ##############‐‐‐‐Menu‐‐‐‐##############" echo "# 1. Install Nginx" echo "# 2. Install MySQL" echo "# 3. Install PHP" echo "# 4. Exit Program" echo " ########################################" } choice(){ read -p "Please choice a menu[1‐9]:" select } install_nginx(){ id nginx &>/dev/null if [ $? -ne 0 ]; then useradd -s /sbin/nologin nginx fi if [ -f nginx‐1.8.0.tar.gz ]; then tar -xf nginx‐1.8.0.tar.gz cd nginx‐1.8.0 yum -y install gcc pcre‐devel openssl‐devel zlib‐devel make ./configure ‐‐prefix=/usr/local/nginx ‐‐with‐http_ssl_module make make install ln -s /usr/local/nginx/sbin/nginx /usr/sbin/ cd .. else echo "没有 Nginx 源码包" fi } install_mysql(){ yum -y install gcc gcc‐c++ cmake ncurses‐devel perl id mysql &>/dev/null if [ $? -ne 0 ]; then useradd -s /sbin/nologin mysql fi if [ -f mysql‐5.6.25.tar.gz ]; then tar -xf mysql‐5.6.25.tar.gz cd mysql‐5.6.25 cmake . make make install /usr/local/mysql/scripts/mysql_install_db ‐‐user=mysql ‐‐datadir=/usr/local/mysql/data/ ‐‐basedir=/usr/local/mysql/ chown -R root.mysql /usr/local/mysql chown -R mysql /usr/local/mysql/data /bin/cp -f /usr/local/mysql/support‐files/mysql.server /etc/init.d/mysqld chmod +x /etc/init.d/mysqld /bin/cp -f /usr/local/mysql/support‐files/my‐default.cnf /etc/my.cnf echo "/usr/local/mysql/lib/" >> /etc/ld.so.conf ldconfig echo 'PATH=\$PATH:/usr/local/mysql/bin/' >> /etc/profile export PATH else echo "没有 mysql 源码包" exit fi } install_php(){ #安装 php 时没有指定启动哪些模块功能,如果的用户可以根据实际情况自行添加额外功能如‐‐with‐gd 等 yum -y install gcc libxml2‐devel if [ -f mhash‐0.9.9.9.tar.gz ]; then tar -xf mhash‐0.9.9.9.tar.gz cd mhash‐0.9.9.9 ./configure make make install cd .. if [ ! ‐f /usr/lib/libmhash.so ]; then ln -s /usr/local/lib/libmhash.so /usr/lib/fildconfigelse echo "没有 mhash 源码包文件" exit fi if [ -f libmcrypt‐2.5.8.tar.gz ]; then tar -xf libmcrypt‐2.5.8.tar.gz cd libmcrypt‐2.5.8 ./configure make make install cd .. if [ ! -f /usr/lib/libmcrypt.so ]; then ln -s /usr/local/lib/libmcrypt.so /usr/lib/ fi ldconfig else echo "没有 libmcrypt 源码包文件" exit fi if [ -f php‐5.4.24.tar.gz ]; then tar -xf php‐5.4.24.tar.gz cd php‐5.4.24 ./configure ‐‐prefix=/usr/local/php5 ‐‐with‐mysql=/usr/local/mysql ‐‐enable‐fpm ‐‐ enable‐mbstring ‐‐with‐mcrypt ‐‐with‐mhash ‐‐with‐config‐file‐path=/usr/local/php5/etc ‐‐with‐ mysqli=/usr/local/mysql/bin/mysql_config make && make install /bin/cp -f php.ini‐production /usr/local/php5/etc/php.ini /bin/cp -f /usr/local/php5/etc/php‐fpm.conf.default /usr/local/php5/etc/php‐fpm.conf cd .. else echo "没有 php 源码包文件" exit fi } while : do menu choice case $select in 1) install_nginx ;; 2) install_mysql ;; 3) install_php ;; 4) exit ;; *) echo Sorry! esac done23）编写脚本快速克隆 KVM 虚拟机#!/bin/bash #编写脚本快速克隆 KVM 虚拟机 #本脚本针对 RHEL7.2 或 Centos7.2 #本脚本需要提前准备一个 qcow2 格式的虚拟机模板, #名称为/var/lib/libvirt/images /.rh7_template 的虚拟机模板 #该脚本使用 qemu‐img 命令快速创建快照虚拟机 #脚本使用 sed 修改模板虚拟机的配置文件,将虚拟机名称、UUID、磁盘文件名、MAC 地址 #exit code: #65 ‐> user input nothing #66 ‐> user input is not a number #67 ‐> user input out of range #68 ‐> vm disk image existsIMG_DIR=/var/lib/libvirt/imagesBASEVM=rh7_template read -p "Enter VM number: " VMNUM if [ $VMNUM -le 9 ]; then VMNUM=0$VMNUM fi if [ -z "${VMNUM}" ]; then echo "You must input a number." exit 65 elif [[ ${VMNUM} =~ [a‐z] ]; then echo "You must input a number." exit 66 elif [ ${VMNUM} -lt 1 -o ${VMNUM} -gt 99 ]; then echo "Input out of range" exit 67 fi NEWVM=rh7_node${VMNUM} if [ -e $IMG_DIR/${NEWVM}.img ]; then echo "File exists." exit 68 fi echo -en "Creating Virtual Machine disk image......\t" qemu‐img create -f qcow2 ‐b $IMG_DIR/.${BASEVM}.img $IMG_DIR/${NEWVM}.img &> /dev/null echo -e "\e[32;1m[OK]\e[0m" virsh dumpxml ${BASEVM} > /tmp/myvm.xml cat /var/lib/libvirt/images/.rhel7.xml > /tmp/myvm.xmlsed -i "/<name>${BASEVM}/s/${BASEVM}/${NEWVM}/" /tmp/myvm.xmlsed -i "/uuid/s/<uuid>.*<\/uuid>/<uuid>$(uuidgen)<\/uuid>/" /tmp/myvm.xmlsed -i "/${BASEVM}\.img/s/${BASEVM}/${NEWVM}/" /tmp/myvm.xml #修改 MAC 地址,本例使用的是常量,每位使用该脚本的用户需要根据实际情况修改这些值 # 最好这里可以使用便利,这样更适合于批量操作,可以克隆更多虚拟机 sed -i "/mac /s/a1/0c/" /tmp/myvm.xml echo -en "Defining new virtual machine......\t\t" virsh define /tmp/myvm.xml &> /dev/null echo -e "\e[32;1m[OK]\e[0m"24）点名器脚本#!/bin/bash #编写一个点名器脚本 #该脚本,需要提前准备一个 user.txt 文件 #该文件中需要包含所有姓名的信息,一行一个姓名,脚本每次随机显示一个姓名 while : do #统计 user 文件中有多少用户 line=`cat user.txt |wc ‐l` num=$[RANDOM%line+1] sed -n "${num}p" user.txt sleep 0.2 clear done25）查看有多少远程的 IP 在连接本机#!/bin/bash #查看有多少远程的 IP 在连接本机(不管是通过 ssh 还是 web 还是 ftp 都统计) #使用 netstat ‐atn 可以查看本机所有连接的状态,‐a 查看所有,# -t仅显示 tcp 连接的信息,‐n 数字格式显示 #Local Address(第四列是本机的 IP 和端口信息) #Foreign Address(第五列是远程主机的 IP 和端口信息) #使用 awk 命令仅显示第 5 列数据,再显示第 1 列 IP 地址的信息 #sort 可以按数字大小排序,最后使用 uniq 将多余重复的删除,并统计重复的次数 netstat -atn | awk '{print $5}' | awk '{print $1}' | sort -nr | uniq -c26）对 100 以内的所有正整数相加求和 (1+2+3+4…+100)#!/bin/bash #对 100 以内的所有正整数相加求和(1+2+3+4...+100) #seq 100 可以快速自动生成 100 个整数 sum=0 for i in `seq 100` do sum=$[sum+i] done echo "总和是:$sum"27）统计 13:30 到 14:30 所有访问 apache 服务器的请求有多少个#!/bin/bash #统计 13:30 到 14:30 所有访问 apache 服务器的请求有多少个 #awk 使用‐F 选项指定文件内容的分隔符是/或者: #条件判断$7:$8 大于等于 13:30,并且要求,$7:$8 小于等于 14:30 #最后使用 wc ‐l 统计这样的数据有多少行,即多少个 awk -F "[ /:]" '$7":"$8>="13:30" && $7":"$8<="14:30"' /var/log/httpd/access_log |wc -l28）统计 13:30 到 14:30 所有访问本机 Aapche 服务器的远程 IP 地址是什么#!/bin/bash #统计 13:30 到 14:30 所有访问本机 Aapche 服务器的远程 IP 地址是什么 #awk 使用‐F 选项指定文件内容的分隔符是/或者: #条件判断$7:$8 大于等于 13:30,并且要求,$7:$8 小于等于 14:30 #日志文档内容里面,第 1 列是远程主机的 IP 地址,使用 awk 单独显示第 1 列即可 awk -F "[ /:]" '$7":"$8>="13:30" && $7":"$8<="14:30"{print $1}' /var/log/httpd/access_log29）打印国际象棋棋盘#!/bin/bash #打印国际象棋棋盘 #设置两个变量,i 和 j,一个代表行,一个代表列,国际象棋为 8*8 棋盘 #i=1 是代表准备打印第一行棋盘,第 1 行棋盘有灰色和蓝色间隔输出,总共为 8 列# i=1,j=1 代表第 1 行的第 1 列;i=2,j=3 代表第 2 行的第 3 列 #棋盘的规律是 i+j 如果是偶数,就打印蓝色色块,如果是奇数就打印灰色色块 #使用 echo ‐ne 打印色块,并且打印完成色块后不自动换行,在同一行继续输出其他色块 for i in {1..8} do for j in {1..8} do sum=$[i+j] if [ $[sum%2] -eq 0 ]; then echo -ne "\033[46m \033[0m" else echo -ne "\033[47m \033[0m" fi done echo done30）统计每个远程 IP 访问了本机 apache 几次?#!/bin/bash #统计每个远程 IP 访问了本机 apache 几次? awk '{ip[$1]++}END{for(i in ip){print ip[i],i}}' /var/log/httpd/access_log31）统计当前 Linux 系统中可以登录计算机的账户有多少个#!/bin/bash #统计当前 Linux 系统中可以登录计算机的账户有多少个 #方法 1: grep "bash$" /etc/passwd | wc -l ------------------------------------------------------------------------------------------- #方法 2: awk -f: '/bash$/{x++}end{print x}' /etc/passwd32）统计 / var/log 有多少个文件, 并显示这些文件名#!/bin/bash #统计/var/log 有多少个文件,并显示这些文件名 #使用 ls 递归显示所有,再判断是否为文件,如果是文件则计数器加 1 cd /var/log sum=0 for i in `ls -r *` do if [ -f $i ]; then let sum++ echo "文件名:$i" fi done echo "总文件数量为:$sum"33）自动为其他脚本添加解释器信息#!/bin/bash #自动为其他脚本添加解释器信息 #如脚本名为 test.sh 则效果如下: # ./test.sh abc.sh 自动为 abc.sh 添加解释器信息 # ./test.sh user.sh 自动为 user.sh 添加解释器信息 # 先使用 grep 判断对象脚本是否已经有解释器信息,如果没有则使用 sed 添加解释器以及描述信息 if ! grep -q "^#!" $1; then sed '1i #!/bin/bash' $1 sed '2i #Description: ' fi # 因为每个脚本的功能不同,作用不同,所以在给对象脚本添加完解释器信息,以及 Description 后还希望 # 继续编辑具体的脚本功能的描述信息,这里直接使用 vim 把对象脚本打开,并且光标跳转到该文件的第 2 行 vim +2 $134）自动化部署 varnish 源码包软件#!/bin/bash #自动化部署 varnish 源码包软件 #本脚本需要提前下载 varnish‐3.0.6.tar.gz 这样一个源码包软件,该脚本即可用自动源码安装部署软件 yum -y install gcc readline‐devel pcre‐develuseradd -s /sbin/nologin varnishtar -xf varnish‐3.0.6.tar.gzcd varnish‐3.0.6 # 使用 configure,make,make install 源码安装软件包 ./configure ‐‐prefix=/usr/local/varnish make && make install # 在源码包目录下,将相应的配置文件拷贝到 Linux 系统文件系统中 # 默认安装完成后,不会自动拷贝或安装配置文件到 Linux 系统,所以需要手动 cp 复制配置文件 # 并使用 uuidgen 生成一个随机密钥的配置文件 cp redhat/varnish.initrc /etc/init.d/varnish cp redhat/varnish.sysconfig /etc/sysconfig/varnish cp redhat/varnish_reload_vcl /usr/bin/ ln -s /usr/local/varnish/sbin/varnishd /usr/sbin/ ln -s /usr/local/varnish/bin/* /usr/binmkdir /etc/varnishcp /usr/local/varnish/etc/varnish/default.vcl /etc/varnish/uuidgen > /etc/varnish/secret35）编写 nginx 启动脚本#!/bin/bash #编写 nginx 启动脚本 #本脚本编写完成后,放置在/etc/init.d/目录下,就可以被 Linux 系统自动识别到该脚本 #如果本脚本名为/etc/init.d/nginx,则 service nginx start 就可以启动该服务 #service nginx stop 就可以关闭服务 #service nginx restart 可以重启服务 #service nginx status 可以查看服务状态 program=/usr/local/nginx/sbin/nginx pid=/usr/local/nginx/logs/nginx.pid start(){ if [ -f $pid ]; then echo "nginx 服务已经处于开启状态" else $program fi} stop(){ if [ -! -f $pid ]; then echo "nginx 服务已经关闭" else $program -s stop echo "关闭服务 ok" fi} status(){ if [ -f $pid ]; then echo "服务正在运行..." else echo "服务已经关闭" fi} case $1 in start) start;; stop) stop;; restart) stop sleep 1 start;; status) status;; *) echo "你输入的语法格式错误" esac36）自动对磁盘分区、格式化、挂载#!/bin/bash #自动对磁盘分区、格式化、挂载 #对虚拟机的 vdb 磁盘进行分区格式化,使用<<将需要的分区指令导入给程序 fdisk # n(新建分区),p(创建主分区),1(分区编号为 1),两个空白行(两个回车,相当于将整个磁盘分一个区) # 注意:1 后面的两个回车(空白行)是必须的! fdisk /dev/vdb << EOFnp1wqEOF #格式化刚刚创建好的分区 mkfs.xfs /dev/vdb1 #创建挂载点目录 if [ -e /data ]; then exit fi mkdir /data #自动挂载刚刚创建的分区,并设置开机自动挂载该分区 echo '/dev/vdb1 /data xfs defaults 1 2' >> /etc/fstabmount -a37）自动优化 Linux 内核参数#!/bin/bash #自动优化 Linux 内核参数 #脚本针对 RHEL7 cat >> /usr/lib/sysctl.d/00‐system.conf <<EOF fs.file‐max=65535 net.ipv4.tcp_timestamps = 0 net.ipv4.tcp_synack_retries = 5 net.ipv4.tcp_syn_retries = 5 net.ipv4.tcp_tw_recycle = 1 net.ipv4.tcp_tw_reuse = 1 net.ipv4.tcp_fin_timeout = 30 net.ipv4.tcp_keepalive_time = 120 net.ipv4.ip_local_port_range = 1024 65535 kernel.shmall = 2097152 kernel.shmmax = 2147483648 kernel.shmmni = 4096 kernel.sem = 5010 641280 5010 128 net.core.wmem_default=262144 net.core.wmem_max=262144 net.core.rmem_default=4194304 net.core.rmem_max=4194304 net.ipv4.tcp_fin_timeout = 10 net.ipv4.tcp_keepalive_time = 30 net.ipv4.tcp_window_scaling = 0 net.ipv4.tcp_sack = 0 EOF sysctl –p38）切割 Nginx 日志文件 (防止单个文件过大, 后期处理很困难)#mkdir /data/scripts#vim /data/scripts/nginx_log.sh #!/bin/bash #切割 Nginx 日志文件(防止单个文件过大,后期处理很困难) logs_path="/usr/local/nginx/logs/" mv ${logs_path}access.log ${logs_path}access_$(date -d "yesterday" +"%Y%m%d").log kill -USR1 `cat /usr/local/nginx/logs/nginx.pid` chmod +x /data/scripts/nginx_log.sh crontab ‐e #脚本写完后,将脚本放入计划任务每天执行一次脚本 0 1 * * * /data/scripts/nginx_log.sh39）检测 MySQL 数据库连接数量#!/bin/bash #检测 MySQL 数据库连接数量 #本脚本每 2 秒检测一次 MySQL 并发连接数,可以将本脚本设置为开机启动脚本,或在特定时间段执行 #以满足对 MySQL 数据库的监控需求,查看 MySQL 连接是否正常 #本案例中的用户名和密码需要根据实际情况修改后方可使用 log_file=/var/log/mysql_count.log user=root passwd=123456 while : do sleep 2 count=`mysqladmin -u "$user" -p "$passwd" status | awk '{print $4}'` echo "`date +%Y‐%m‐%d` 并发连接数为:$count" >> $log_file done40）根据 md5 校验码, 检测文件是否被修改#!/bin/bash #根据 md5 校验码,检测文件是否被修改 #本示例脚本检测的是/etc 目录下所有的 conf 结尾的文件,根据实际情况,您可以修改为其他目录或文件 # 本脚本在目标数据没有被修改时执行一次,当怀疑数据被人篡改,再执行一次 # 将两次执行的结果做对比,MD5 码发生改变的文件,就是被人篡改的文件 for i in $(ls /etc/*.conf) do md5sum "$i" >> /var/log/conf_file.log done41）检测 MySQL 服务是否存活#!/bin/bash #检测 MySQL 服务是否存活 #host 为你需要检测的 MySQL 主机的 IP 地址,user 为 MySQL 账户名,passwd 为密码 # 这些信息需要根据实际情况修改后方可使用 host=192.168.51.198 user=root passwd=123456 mysqladmin -h '$host' -u '$user' -p'$passwd' ping &>/dev/null if [ $? -eq 0 ] then echo "MySQL is UP" else echo "MySQL is down" fi42）备份 MySQL 的 shell 脚本 (mysqldump 版本)#!/bin/bash #备份 MySQL 的 shell 脚本(mysqldump版本) #定义变量 user(数据库用户名),passwd(数据库密码),date(备份的时间标签) #dbname(需要备份的数据库名称,根据实际需求需要修改该变量的值,默认备份 mysql 数据库) user=root passwd=123456 dbname=mysql date=$(date +%Y%m%d) #测试备份目录是否存在,不存在则自动创建该目录 [ ! -d /mysqlbackup ] && mkdir /mysqlbackup # 使用 mysqldump 命令备份数据库 mysqldump -u "$user" -p "$passwd" "$dbname" > /mysqlbackup/"$dbname"-${date}.sql43）将文件中所有的小写字母转换为大写字母#!/bin/bash #将文件中所有的小写字母转换为大写字母 #$1是位置参数,是你需要转换大小写字母的文件名称 #执行脚本,给定一个文件名作为参数,脚本就会将该文件中所有的小写字母转换为大写字母 tr "[a‐z]" "[A‐Z]" < $144）非交互自动生成 SSH 密钥文件#!/bin/bash #非交互自动生成 SSH 密钥文件 #‐t 指定 SSH 密钥的算法为 RSA 算法;‐N 设置密钥的密码为空;‐f 指定生成的密钥文件>存放在哪里 rm -rf ~/.ssh/{known_hosts,id_rsa*}ssh‐keygen -t RSA -N '' -f ~/.ssh/id_rsa45）检查特定的软件包是否已经安装#!/bin/bash #检查特定的软件包是否已经安装 if [ $# -eq 0 ]; then echo "你需要制定一个软件包名称作为脚本参数" echo "用法:$0 软件包名称 ..." fi #$@提取所有的位置变量的值,相当于$* for package in "$@" do if rpm -q ${package} &>/dev/null ; then echo -e "${package}\033[32m 已经安装\033[0m" else echo -e "${package}\033[34;1m 未安装\033[0m" fi done46）监控 HTTP 服务器的状态 (测试返回码)#!/bin/bash #监控 HTTP 服务器的状态(测试返回码) #设置变量,url为你需要检测的目标网站的网址(IP 或域名),比如百度 url=http://183.232.231.172/index.html # 定义函数 check_http: # 使用 curl 命令检查 http 服务器的状态 # ‐m 设置curl不管访问成功或失败,最大消耗的时间为 5 秒,5 秒连接服务为相应则视为无法连接 # ‐s 设置静默连接,不显示连接时的连接速度、时间消耗等信息 # ‐o 将 curl 下载的页面内容导出到/dev/null(默认会在屏幕显示页面内容) # ‐w 设置curl命令需要显示的内容%{http_code},指定curl返回服务器的状态码 check_http(){ status_code=$(curl -m 5 -s -o /dev/null -w %{http_code} $url) } while : do check_http date=$(date +%Y%m%d‐%H:%M:%S) # 生成报警邮件的内容 echo "当前时间为:$date $url 服务器异常,状态码为${status_code}. 请尽快排查异常." > /tmp/http$$.pid # 指定测试服务器状态的函数,并根据返回码决定是发送邮件报警还是将正常信息写入日志 if [ $status_code -ne 200 ]; then mail -s Warning root < /tmp/http$$.pid else echo "$url 连接正常" >> /var/log/http.log fi sleep 5 done47）自动添加防火墙规则, 开启某些服务或端口 (适用于 RHEL7)#!/bin/bash #自动添加防火墙规则,开启某些服务或端口(适用于 RHEL7) # 设置变量定义需要添加到防火墙规则的服务和端口号 # 使用 firewall‐cmd ‐‐get‐services 可以查看 firewall 支持哪些服务 service="nfs http ssh" port="80 22 8080" # 循环将每个服务添加到防火墙规则中 for i in $service do echo "Adding $i service to firewall" firewall‐cmd --add-service=${i} done #循环将每个端口添加到防火墙规则中 for i in $port do echo "Adding $i Port to firewall" firewall‐cmd --add-port=${i}/tcp done #将以上设置的临时防火墙规则,转换为永久有效的规则(确保重启后有效) firewall‐cmd --runtime-to-permanent48）使用脚本自动创建逻辑卷#!/bin/bash #使用脚本自动创建逻辑卷 # 清屏,显示警告信息,创建将磁盘转换为逻辑卷会删除数据 clear echo -e "\033[32m !!!!!!警告(Warning)!!!!!!\033[0m" echo echo "+++++++++++++++++++++++++++++++++++++++++++++++++" echo "脚本会将整个磁盘转换为 PV,并删除磁盘上所有数据!!!" echo "This Script will destroy all data on the Disk" echo "+++++++++++++++++++++++++++++++++++++++++++++++++" echo read -p "请问是否继续 y/n?:" sure # 测试用户输入的是否为 y,如果不是则退出脚本 [ $sure != y ] && exit # 提示用户输入相关参数(磁盘、卷组名称等数据),并测试用户是否输入了这些值,如果没有输入,则脚本退出 read -p "请输入磁盘名称,如/dev/vdb:" disk [ -z $disk ] && echo "没有输入磁盘名称" && exit read -p "请输入卷组名称:" vg_name [ -z $vg_name ] && echo "没有输入卷组名称" && exit read -p "请输入逻辑卷名称:" lv_name [ -z $lv_name ] && echo "没有输入逻辑卷名称" && exit read -p "请输入逻辑卷大小:" lv_size [ -z $lv_size ] && echo "没有输入逻辑卷大小" && exit # 使用命令创建逻辑卷 pvcreate $diskvgcreate $vg_name $disklvcreate -L ${lv_size}M -n ${lv_name} ${vg_name}49）显示 CPU 厂商信息#!/bin/bash # 显示 CPU 厂商信息 awk '/vendor_id/{print $3}' /proc/cpuinfo | uniq50）删除某个目录下大小为 0 的文件#!/bin/bash # 删除某个目录下大小为 0 的文件 #/var/www/html 为测试目录,脚本会清空该目录下所有 0 字节的文件 dir="/var/www/html" find $dir -type f -size 0 -exec rm -rf {} \;51）查找 Linux 系统中的僵尸进程#!/bin/bash # 查找 Linux 系统中的僵尸进程 # awk 判断 ps 命令输出的第 8 列为 Z 是,显示该进程的 PID 和进程命令 ps aux | awk '{if($8 == "Z"){print $2,$11}}'52）提示用户输入年份后判断该年是否为闰年#!/bin/bash # 提示用户输入年份后判断该年是否为闰年 # 能被4整除并且并不能被100整除的年份是闰年 # 能被400整除的年份也是闰年 read -p "请输入一个年份:" year if [ "$year" = "" ]; then echo "没有输入年份" exit fi #使用正则测试变量 year 中是否包含大小写字母 if [[ "$year" =~ [a‐Z] ]]; then echo "你输入的不是数字" exit fi # 判断是否为闰年 if [ $[year % 4] -eq 0 ] && [ $[year % 100] -ne 0 ]; then echo "$year年是闰年" elif [ $[year % 400] -eq 0 ]; then echo "$year年是闰年" else echo "$year年不是闰年" fi53）生成随机密码 (urandom 版本)#!/bin/bash # 生成随机密码(urandom 版本) # /dev/urandom 文件是 Linux 内置的随机设备文件 # cat /dev/urandom 可以看看里面的内容,ctrl+c 退出查看 # 查看该文件内容后,发现内容有些太随机,包括很多特殊符号,我们需要的密码不希望使用这些符号 # tr ‐dc '_A‐Za‐z0‐9' < /dev/urandom# 该命令可以将随机文件中其他的字符删除,仅保留大小写字母,数字,下划线,但是内容还是太多 # 我们可以继续将优化好的内容通过管道传递给 head 命令,在大量数据中仅显示头 10 个字节 # 注意 A 前面有个下划线 tr -dc '_A‐Za‐z0‐9' </dev/urandom | head -c 1054）生成随机密码 (字串截取版本)#!/bin/bash # 生成随机密码(字串截取版本) # 设置变量 key,存储密码的所有可能性(密码库),如果还需要其他字符请自行添加其他密码字符 # 使用$#统计密码库的长度 key="0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ" num=${#key} # 设置初始密码为空 pass='' # 循环 8 次,生成随机密码 # 每次都是随机数对密码库的长度取余,确保提取的密码字符不超过密码库的长度 # 每次循环提取一位随机密码,并将该随机密码追加到 pass 变量的最后 for i in {1..8} do index=$[RANDOM%num] pass=$pass${key:$index:1} done echo $pass55）生成随机密码 (UUID 版本, 16 进制密码)#!/bin/bash # 生成随机密码(UUID 版本,16 进制密码) uuidgen56）生成随机密码 (进程 ID 版本, 数字密码)#!/bin/bash # 生成随机密码(进程 ID 版本,数字密码) echo $$57）测试用户名与密码是否正确#!/bin/bash # 测试用户名与密码是否正确 #用户名为 tom 并且密码为 123456,则提示登录成功,否则提示登录失败 read -p "请输入用户名:" user read -p "请输入密码:" pass if [ "$user" == 'tom' -a "$pass" == '123456' ]; then echo "Login successful" else echo "Login Failed" fi58）循环测试用户名与密码是否正确#!/bin/bash # 循环测试用户名与密码是否正确 # 循环测试用户的账户名和密码,最大测试 3 次,输入正确提示登录成功,否则提示登录失败 # 用户名为 tom 并且密码为 123456 for i in {1..3} do read -p "请输入用户名:" user read -p "请输入密码:" pass if [ "$user" == 'tom' -a "$pass" == '123456' ]; then echo "Login successful" exit fi done echo "Login Failed"59）Shell 脚本的 fork 炸弹#!/bin/bash # Shell 脚本的 fork 炸弹 # 快速消耗计算机资源,致使计算机死机 # 定义函数名为.(点), 函数中递归调用自己并放入后台执行 .() { .|.& };.60）批量下载有序文件 (pdf、图片、视频等等)#!/bin/bash # 批量下载有序文件(pdf、图片、视频等等) # 本脚本准备有序的网络资料进行批量下载操作(如 01.jpg,02.jpg,03.jpg) # 设置资源来源的域名连接 url="http://www.baidu.com/" echo "开始下载..." sleep 2 type=jpg for i in `seq 100` echo "正在下载$i.$type" curl $url/$i.$type -o /tmp/${i}$type sleep 1 done #curl 使用-o 选项指定下载文件另存到哪里.61）显示当前计算机中所有账户的用户名称#!/bin/bash # 显示当前计算机中所有账户的用户名称 # 下面使用3种不同的方式列出计算机中所有账户的用户名 # 指定以:为分隔符,打印/etc/passwd 文件的第 1 列 awk -F: '{print $1}' /etc/passwd # 指定以:为分隔符,打印/etc/passwd 文件的第 1 列 cut -d: -f1 /etc/passwd # 使用 sed 的替换功能,将/etc/passwd 文件中:后面的所有内容替换为空(仅显示用户名) sed 's/:.*//' /etc/passwd62）制定目录路径, 脚本自动将该目录使用 tar 命令打包备份到 / data 目录#!/bin/bash # 制定目录路径,脚本自动将该目录使用 tar 命令打包备份到/data目录 [ ! -d /data ] && mkdir /data[ -z $1 ] && exit if [ -d $1 ]; then tar -czf /data/$1.-`date +%Y%m%d`.tar.gz $1 else echo "该目录不存在" fi63）显示进度条 (回旋镖版)#!/bin/bash # 显示进度条(回旋镖版) while : do clear for i in {1..20} do echo ‐e "\033[3;${i}H*" sleep 0.1 done clear for i in {20..1} do echo ‐e "\033[3;${i}H*" sleep 0.1 done clear done64）安装 LAMP 环境 (yum 版本)#!/bin/bash # 安装 LAMP 环境(yum 版本) # 本脚本适用于 RHEL7(RHEL6 中数据库为 mysql) yum makecache &>/dev/null num=$(yum repolist | awk '/repolist/{print $2}' | sed 's/,//') if [ $num -lt 0 ]; then yum -y install httpd yum -y install mariadb mariadb-server mariadb-devel yum -y install php php-mysql else echo "未配置 yum 源..." fi65）循环关闭局域网中所有主机#!/bin/bash # 循环关闭局域网中所有主机 # 假设本机为 192.168.4.100,编写脚本关闭除自己外的其他所有主机 # 脚本执行,需要提前给所有其他主机传递 ssh 密钥,满足无密码连接 for i in {1..254} do [ $i -eq 100 ] && continue echo "正在关闭 192.168.4.$i..." ssh 192.168.4.$i poweroff done66）获取本机 MAC 地址#!/bin/bash # 获取本机 MAC 地址 ip a s | awk 'BEGIN{print " 本 机 MAC 地 址 信 息 如 下 :"}/^[0‐9]/{print $2;getline;if($0~/link\/ether/){print $2}}' | grep -v lo: # awk 读取 ip 命令的输出,输出结果中如果有以数字开始的行,先显示该行的地 2 列(网卡名称), # 接着使用 getline 再读取它的下一行数据,判断是否包含 link/ether# 如果保护该关键词,就显示该行的第 2 列(MAC 地址) # lo 回环设备没有 MAC,因此将其屏蔽,不显示67）自动配置 rsynd 服务器的配置文件 rsyncd.conf#!/bin/bash # 自动配置 rsynd 服务器的配置文件 rsyncd.conf # See rsyncd.conf man page for more options. [ ! -d /home/ftp ] && mkdir /home/ftp echo 'uid = nobodygid = nobodyuse chroot = yesmax connections = 4pid file = /var/run/rsyncd.pidexclude = lost+found/transfer logging = yestimeout = 900ignore nonreadable = yesdont compress = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2[ftp] path = /home/ftp comment = share' > /etc/rsyncd.conf68）修改 Linux 系统的最大打开文件数量#!/bin/bash # 修改 Linux 系统的最大打开文件数量 # 往/etc/security/limits.conf 文件的末尾追加两行配置参数,修改最大打开文件数量为 65536 cat >> /etc/security/limits.conf <<EOF* soft nofile 65536* hard nofile 65536 EOF69）设置 Python 支持自动命令补齐功能#!/bin/bash # 设置 Python 支持自动命令补齐功能 # Summary:Enable tab complete for python # Description:Needs import readline and rlcompleter module #import readline #import rlcompleter #help(rlcompleter) display detail: readline.parse_and_bind('tab: complete') #man python display detail: PYTHONSTARTUP variable if [ ! -f /usr/bin/tab.py ]; then cat >> /usr/bin/tab.py <<EOF import readline import rlcompleter readline.parse_and_bind('tab: complete') EOF fi sed -i '$a export PYTHONSTARTUP=/usr/bin/tab.py' /etc/profilesource /etc/profile70）自动修改计划任务配置文件#!/bin/bash # 自动修改计划任务配置文件 read -p "请输入分钟信息(00‐59):" min read -p "请输入小时信息(00‐24):" hour read -p "请输入日期信息(01‐31):" date read -p "请输入月份信息(01‐12):" month read -p "请输入星期信息(00‐06):" weak read -p "请输入计划任务需要执行的命令或脚本:" program echo "$min $hour $date $month $weak $program" >> /etc/crontab71）使用脚本循环创建三位数字的文本文件 (111-999 的文件)#!/bin/bash # 使用脚本循环创建三位数字的文本文件(111-999 的文件) for i in {1..9} do for j in {1..9} do for k in {1..9} do touch /tmp/$i$j$k.txt done done done72）找出 / etc/passwd 中能登录的用户, 并将对应在 / etc/shadow 中第二列密码提出处理#!/bin/bash # 找出/etc/passwd 中能登录的用户,并将对应在/etc/shadow 中第二列密码提出处理 user=$(awk -F: '/bash$/{print $1}' /etc/passwd) for i in $user do awk -F: -v x=$i '$1==x{print $1,$2}' /etc/shadow done73）统计 / etc/passwd 中 root 出现的次数#!/bin/bash # 统计/etc/passwd 中 root 出现的次数 #每读取一行文件内容,即从第 1 列循环到最后 1 列,依次判断是否包含 root 关键词,如果包含则 x++ awk -F: '{i=1;while(i<=NF){if($i~/root/){x++};i++}} END{print "root 出现次数为"x}' /etc/passwd74）统计 Linux 进程相关数量信息#!/bin/bash # 统计 Linux 进程相关数量信息 running=0 sleeping=0 stoped=0 zombie=0 # 在 proc 目录下所有以数字开始的都是当前计算机正在运行的进程的进程 PID # 每个 PID 编号的目录下记录有该进程相关的信息 for pid in /proc/[1‐9]* do procs=$[procs+1] stat=$(awk '{print $3}' $pid/stat) # 每个 pid 目录下都有一个 stat 文件,该文件的第 3 列是该进程的状态信息 case $stat in R) running=$[running+1] ;; T) stoped=$[stoped+1] ;; S) sleeping=$[sleeping+1] ;; Z) zombie=$[zombie+1] ;; esac done echo "进程统计信息如下" echo "总进程数量为:$procs" echo "Running 进程数为:$running" echo "Stoped 进程数为:$stoped" echo "Sleeping 进程数为:$sleeping" echo "Zombie 进程数为:$zombie"75）从键盘读取一个论坛积分, 判断论坛用户等级#!/bin/bash # 从键盘读取一个论坛积分,判断论坛用户等级#等级分类如下: # 大于等于 90 神功绝世 # 大于等于 80,小于 90 登峰造极 # 大于等于 70,小于 80 炉火纯青 # 大于等于 60,小于 70 略有小成 # 小于 60 初学乍练 read -p "请输入积分(0‐100):" JF if [ $JF -ge 90 ] ; then echo "$JF 分,神功绝世" elif [ $JF -ge 80 ] ; then echo "$JF 分,登峰造极" elif [ $JF -ge 70 ] ; then echo "$JF 分,炉火纯青" elif [ $JF -lt 60 ] ; then echo "$JF 分,略有小成" else echo "$JF 分,初学乍练" fi76）判断用户输入的数据类型 (字母、数字或其他)#!/bin/bash # 判断用户输入的数据类型(字母、数字或其他) read -p "请输入一个字符:" KEY case "$KEY" in [a‐z]|[A‐Z]) echo "字母" ;; [0‐9]) echo "数字" ;; *) echo "空格、功能键或其他控制字符" esac77）显示进度条 (数字版)#!/bin/bash # 显示进度条(数字版) # echo 使用‐e 选项后,在打印参数中可以指定 H,设置需要打印内容的 x,y 轴的定位坐标 # 设置需要打印内容在第几行,第几列 for i in {1..100} do echo -e "\033[6;8H[" echo -e "\033[6;9H$i%" echo -e "\033[6;13H]" sleep 0.1 done78）打印斐波那契数列#!/bin/bash # 打印斐波那契数列(该数列的特点是后一个数字,永远都是前 2 个数字之和) # 斐波那契数列后一个数字永远是前 2 个数字之和 # 如:0 1 1 2 3 5 8 13 ... ... list=(0 1) for i in `seq 2 11` do list[$i]=`expr ${list[‐1]} + ${list[‐2]}` done echo ${list[@]}79）判断用户输入的是 Yes 或 NO#!/bin/bash # 判断用户输入的是 Yes 或 NO read -p "Are you sure?[y/n]:" sure case $sure in y|Y|Yes|YES) echo "you enter $a" ;; n|N|NO|no) echo "you enter $a" ;; *) echo "error";; esac80）显示本机 Linux 系统上所有开放的端口列表#!/bin/bash # 显示本机 Linux 系统上所有开放的端口列表 # 从端口列表中观测有没有没用的端口,有的话可以将该端口对应的服务关闭,防止意外的攻击可能性 ss -nutlp | awk '{print $1,$5}' | awk -F"[: ]" '{print "协议:"$1,"端口号:"$NF}' | grep "[0‐9]" | uniq81）将 Linux 系统中 UID 大于等于 1000 的普通用户都删除#!/bin/bash # 将 Linux 系统中 UID 大于等于 1000 的普通用户都删除 # 先用 awk 提取所有 uid 大于等于 1000 的普通用户名称 # 再使用 for 循环逐个将每个用户删除即可 user=$(awk -F: '$3>=1000{print $1}' /etc/passwd) for i in $user do userdel -r $i done82）使用脚本开启关闭虚拟机#!/bin/bash # 使用脚本开启关闭虚拟机 # 脚本通过调用virsh命令实现对虚拟机的管理,如果没有该命令,需要安装 libvirt‐client 软件包 # $1是脚本的第1个参数,$2是脚本的第2个参数 # 第1个参数是你希望对虚拟机进行的操作指令,第2个参数是虚拟机名称 case $1 in list) virsh list --all ;; start) virsh start $2 ;; stop) virsh destroy $2 ;; enable) virsh autostart $2 ;; disable) virsh autostart --disable $2 ;; *) echo "Usage:$0 list" echo "Usage:$0 [start|stop|enable|disable] VM_name" cat << EOF #list 显示虚拟机列表 #start 启动虚拟机 #stop 关闭虚拟机 #enable 设置虚拟机为开机自启 #disable 关闭虚拟机开机自启功能 EOF ;; esac83）调整虚拟机内存参数的 shell 脚本#!/bin/bash # 调整虚拟机内存参数的 shell 脚本 # 脚本通过调用 virsh 命令实现对虚拟机的管理,如果没有该命令,需要安装 libvirt‐client 软件包 cat << EOF 1.调整虚拟机最大内存数值 2.调整实际分配给虚拟机的内存数值 EOF read -p "请选择[1‐2]:" select case $select in 1) read -p "请输入虚拟机名称" name read -p "请输入最大内存数值(单位:k):" size virsh setmaxmem $name --size $size --config ;; 2) read -p "请输入虚拟机名称" name read -p "请输入实际分配内存数值(单位:k):" size virsh setmem $name $size ;; *) echo "Error" ;; esac84）查看 KVM 虚拟机中的网卡信息 (不需要进入启动或进入虚拟机)#!/bin/bash # 查看 KVM 虚拟机中的网卡信息(不需要进入启动或进入虚拟机) # 该脚本使用 guestmount 工具,可以将虚拟机的磁盘系统挂载到真实机文件系统中 # Centos7.2 中安装 libguestfs‐tools‐c 可以获得 guestmount 工具 # 虚拟机可以启动或者不启动都不影响该脚本的使用# 将虚拟机磁盘文件挂载到文件系统后,就可以直接读取磁盘文件中的网卡配置文件中的数据 clear mountpoint="/media/virtimage"[ ! -d $mountpoint ] && mkdir $mountpoint read -p "输入虚拟机名称:" name echo "请稍后..." # 如果有设备挂载到该挂载点,则先 umount 卸载 if mount | grep -q "$mountpoint" ; then umount $mountpointfi # 只读的方式,将虚拟机的磁盘文件挂载到特定的目录下,这里是/media/virtimage 目录 guestmount -r -d $name -i $mountpoint echo echo "‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐" echo -e "\033[32m$name 虚拟机中网卡列表如下:\033[0m" dev=$(ls /media/virtimage/etc/sysconfig/network‐scripts/ifcfg-* |awk -F"[/‐]" '{print $9}') echo $dev echo "‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐‐" echo echo echo "+++++++++++++++++++++++++++++++++++++++++++" echo -e "\033[32m 网卡 IP 地址信息如下:\033[0m" for i in $dev do echo -n "$i:" grep -q "IPADDR" /media/virtimage/etc/sysconfig/network‐scripts/ifcfg-$i || echo "未配置 IP地址" awk -F= '/IPADDR/{print $2}' /media/virtimage/etc/sysconfig/network-scripts/ifcfg-$i done echo "+++++++++++++++++++++++++++++++++++++++++++"85）不登陆虚拟机, 修改虚拟机网卡 IP 地址#!/bin/bash # 不登陆虚拟机,修改虚拟机网卡 IP 地址 # 该脚本使用 guestmount 工具,Centos7.2 中安装 libguestfs‐tools‐c 可以获得 guestmount 工具 # 脚本在不登陆虚拟机的情况下,修改虚拟机的 IP 地址信息 # 在某些环境下,虚拟机没有 IP 或 IP 地址与真实主机不在一个网段 # 真实主机在没有 virt‐manger 图形的情况下,远程连接虚拟机很麻烦 # 该脚本可以解决类似的问题 read -p "请输入虚拟机名称:" name if virsh domstate $name | grep -q running ; then echo "修改虚拟机网卡数据,需要关闭虚拟机" virsh destroy $namefimountpoint="/media/virtimage"[ ! -d $mountpoint ] && mkdir $mountpoint echo "请稍后..." if mount | grep -q "$mountpoint" ; then umount $mountpointfiguestmount -d $name -i $mountpoint read -p "请输入需要修改的网卡名称:" dev read -p "请输入 IP 地址:" addr # 判断原本网卡配置文件中是否有 IP 地址,有就修改该 IP,没有就添加一个新的 IP 地址 if grep -q "IPADDR" $mountpoint/etc/sysconfig/network‐scripts/ifcfg‐$dev ; then sed -i "/IPADDR/s/=.*/=$addr/" $mountpoint/etc/sysconfig/network‐scripts/ifcfg‐$dev else echo "IPADDR=$addr" >> $mountpoint/etc/sysconfig/network‐scripts/ifcfg‐$dev fi # 如果网卡配置文件中有客户配置的 IP 地址,则脚本提示修改 IP 完成 awk -F= -v x=$addr '$2==x{print "完成..."}' $mountpoint/etc/sysconfig/network‐scripts/ifcfg-$dev86）破解虚拟机密码, 无密码登陆虚拟机系统#!/bin/bash # 破解虚拟机密码,无密码登陆虚拟机系统 # 该脚本使用 guestmount 工具,Centos7.2 中安装 libguestfs‐tools‐c 可以获得 guestmount 工具 read -p "请输入虚拟机名称:" name if virsh domstate $name | grep -q running ; then echo "破解,需要关闭虚拟机" virsh destroy $namefimountpoint="/media/virtimage"[ ! -d $mountpoint ] && mkdir $mountpoint echo "请稍后..." if mount | grep -q "$mountpoint" ; then umount $mountpointfiguestmount -d $name -i $mountpoint # 将 passwd 中密码占位符号 x 删除,该账户即可实现无密码登陆系统 sed -i "/^root/s/x//" $mountpoint/etc/passwd87）Shell 脚本对信号的处理, 执行脚本后, 按键盘 Ctrl+C 无法终止的脚本#!/bin/bash # Shell 脚本对信号的处理,执行脚本后,按键盘 Ctrl+C 无法终止的脚本 # 使用 trap 命令可以拦截用户通过键盘或 kill 命令发送过来的信号 # 使用 kill ‐l 可以查看 Linux 系统中所有的信号列表,其中 2 代表 Ctrl+C # trap 当发现有用户 ctrl+C 希望终端脚本时,就执行 echo "暂停 10s";sleep 10 这两条命令 # 另外用户使用命令:[ kill ‐2 脚本的 PID ] 也可以中断脚本和 Ctrl+C 一样的效果,都会被 trap 拦截 trap 'echo "暂停 10s";sleep 10' 2 while : do echo "go go go" done88）一键部署 memcached#!/bin/bash # 一键部署 memcached # 脚本用源码来安装 memcached 服务器 # 注意:如果软件的下载链接过期了,请更新 memcached 的下载链接 wget http://www.memcached.org/files/memcached-1.5.1.tar.gz yum -y install gcctar -xf memcached‐1.5.1.tar.gzcd memcached‐1.5.1 ./configure make make install89）一键配置 VNC 远程桌面服务器 (无密码版本)#!/bin/bash # 一键配置 VNC 远程桌面服务器(无密码版本) # 脚本配置的 VNC 服务器,客户端无需密码即可连接 # 客户端仅有查看远程桌面的权限,没有鼠标和键盘的操作权限 rpm --quiet -q tigervnc‐server if [ $? -ne 0 ]; then yum -y tigervnc‐server fi x0vncserver AcceptKeyEvents=0 AlwaysShared=1 \AcceptPointerEvents=0 SecurityTypes=None rfbport=590890）关闭 SELinux#!/bin/bash # 关闭 SELinux sed -i '/^SELINUX/s/=.*/=disabled/' /etc/selinux/configsetenforce 091）查看所有虚拟机磁盘使用量以及 CPU 使用量信息#!/bin/bash # 查看所有虚拟机磁盘使用量以及CPU使用量信息 virt‐dfread -n1 "按任意键继续" keyvirt‐top92）使用 shell 脚本打印图形#!/bin/bash # 使用 shell 脚本打印如下图形: # 打印第一组图片 # for(())为类 C 语言的语法格式,也可以使用 for i in;do ;done 的格式替换 # for((i=1;i<=9;i++))循环会执行 9 次,i 从 1 开始到 9,每循环一次 i 自加 1 clear for (( i=1; i<=9; i++ )) do for (( j=1; j<=i; j++ )) do echo -n "$i" done echo "" done read -n1 "按任意键继续" key #打印第二组图片 clear for (( i=1; i<=5; i++ )) do for (( j=1; j<=i; j++ )) do echo -n " |" done echo "_ " done read -n1 "按任意键继续" key #打印第三组图片 clear for (( i=1; i<=5; i++ )) do for (( j=1; j<=i; j++ )) do echo -n " *" done echo "" done for (( i=5; i>=1; i‐‐ )) do for (( j=1; j<=i; j++ )) do echo -n " *" done echo "" done93）根据计算机当前时间, 返回问候语, 可以将该脚本设置为开机启动#!/bin/bash # 根据计算机当前时间,返回问候语,可以将该脚本设置为开机启动 # 00‐12 点为早晨,12‐18 点为下午,18‐24 点为晚上 # 使用 date 命令获取时间后,if 判断时间的区间,确定问候语内容 tm=$(date +%H) if [ $tm -le 12 ]; then msg="Good Morning $USER" elif [ $tm -gt 12 -a $tm -le 18 ]; then msg="Good Afternoon $USER" else msg="Good Night $USER" fi echo "当前时间是:$(date +"%Y‐%m‐%d %H:%M:%S")" echo -e "\033[34m$msg\033[0m"94）读取用户输入的账户名称, 将账户名写入到数组保存#!/bin/bash # 读取用户输入的账户名称,将账户名写入到数组保存 # 定义数组名称为 name,数组的下标为 i,小标从 0 开始,每输入一个账户名,下标加 1,继续存下一个账户 # 最后,输入 over,脚本输出总结性信息后脚本退出 i=0 while : do read -p "请输入账户名,输入 over 结束:" key if [ $key == "over" ]; then break else name[$i]=$key let i++ fi done echo "总账户名数量:${#name[*]}" echo "${name[@]}"95）判断文件或目录是否存在#!/bin/bash # 判断文件或目录是否存在 if [ $# -eq 0 ] ; then echo "未输入任何参数,请输入参数" echo "用法:$0 [文件名|目录名]" fi if [ -f $1 ]; then echo "该文件,存在" ls -l $1 else echo "没有该文件" fi if [ -d $1 ]; then echo "该目录,存在" ls -ld $2 else echo "没有该目录" fi96）打印各种格式的时间#!/bin/bash # 打印各种时间格式 echo "显示星期简称(如:Sun)" date +%a echo "显示星期全称(如:Sunday)" date +%A echo "显示月份简称(如:Jan)" date +%b echo "显示月份全称(如:January)" date +%B echo "显示数字月份(如:12)" date +%m echo "显示数字日期(如:01 号)" date +%d echo "显示数字年(如:01 号)" date +%Y echo "显示年‐月‐日" date +%F echo "显示小时(24 小时制)" date +%H echo "显示分钟(00..59)" date +%M echo "显示秒" date +%S echo "显示纳秒" date +%N echo "组合显示" date +"%Y%m%d %H:%M:%S"97）使用 egrep 过滤 MAC 地址#!/bin/bash # 使用 egrep 过滤 MAC 地址 # MAC 地址由 16 进制组成,如 AA:BB:CC:DD:EE:FF# [0‐9a‐fA‐F]{2}表示一段十六进制数值,{5}表示连续出现5组前置:的十六进制 egrep "[0‐9a‐fA‐F]{2}(:[0‐9a‐fA‐F]{2}){5}" $198）统计双色球各个数字的中奖概率#!/bin/bash # 统计双色球各个数字的中奖概率 # 往期双色球中奖号码如下: # 01 04 11 28 31 32 16 # 04 07 08 18 23 24 02 # 02 05 06 16 28 29 04 # 04 19 22 27 30 33 01 # 05 10 18 19 30 31 03 # 02 06 11 12 19 29 06 # 统计篮球和红球数据出现的概率次数(篮球不分顺序,统计所有篮球混合在一起的概率) awk '{print $1"\n"$2"\n"$3"\n"$4"\n"$5"\n"$6}' 1.txt | sort | uniq -c | sort awk '{print $7}' 1.txt | sort | uniq -c | sort99）生成签名私钥和证书#!/bin/bash # 生成签名私钥和证书 read -p "请输入存放证书的目录:" dir if [ ! -d $dir ]; then echo "该目录不存在" exit fi read -p "请输入密钥名称:" name # 使用 openssl 生成私钥 openssl genrsa -out ${dir}/${name}.key # 使用 openssl 生成证书 #subj 选项可以在生成证书时,非交互自动填写 Common Name 信息 openssl req -new -x509 -key ${dir}/${name}.key -subj "/CN=common" -out ${dir}/${name}.crt100）使用 awk 编写的 wc 程序#!/bin/bash # 使用awk编写的wc程序 # 自定义变量 chars 变量存储字符个数,自定义变量 words 变量存储单词个数 # awk 内置变量 NR 存储行数 # length()为 awk 内置函数,用来统计每行的字符数量,因为每行都会有一个隐藏的$,所以每次统计后都+1 # wc 程序会把文件结尾符$也统计在内,可以使用 cat ‐A 文件名,查看该隐藏字符 awk '{chars+=length($0)+1;words+=NF} END{print NR,words,chars}' $1
系统信息arch #显示机器的处理器架构(1) uname -m #显示机器的处理器架构(2) uname -r #显示正在使用的内核版本 dmidecode -q #显示硬件系统部件 - (SMBIOS / DMI) hdparm -i /dev/hda #罗列一个磁盘的架构特性 hdparm -tT /dev/sda #在磁盘上执行测试性读取操作 cat /proc/cpuinfo #显示CPU info的信息 cat /proc/interrupts #显示中断 cat /proc/meminfo #校验内存使用 cat /proc/swaps #显示哪些swap被使用 cat /proc/version #显示内核的版本 cat /proc/net/dev #显示网络适配器及统计 cat /proc/mounts #显示已加载的文件系统 lspci -tv #罗列PCI设备 lsusb -tv #显示USB设备date 显示系统日期cal 2007 #显示2007年的日历表 date 041217002007.00 #设置日期和时间 - 月日时分年.秒 clock -w #将时间修改保存到 BIOS关机 (系统的关机、重启以及登出 )shutdown -h now #关闭系统(1) init 0 #关闭系统(2) telinit 0 #关闭系统(3) shutdown -h hours:minutes & #按预定时间关闭系统 shutdown -c #取消按预定时间关闭系统 shutdown -r now #重启(1) reboot #重启(2) logout #注销文件和目录cd /home #进入 '/ home' 目录' cd .. #返回上一级目录 cd ../.. #返回上两级目录 cd #进入个人的主目录 cd ~user1 #进入个人的主目录 cd - #返回上次所在的目录 pwd #显示工作路径 ls #查看目录中的文件 ls -F #查看目录中的文件 ls -l #显示文件和目录的详细资料 ls -a #显示隐藏文件 ls *[0-9]* #显示包含数字的文件名和目录名 tree #显示文件和目录由根目录开始的树形结构(1) lstree #显示文件和目录由根目录开始的树形结构(2) mkdir dir1 #创建一个叫做 'dir1' 的目录' mkdir dir1 dir2 #同时创建两个目录 mkdir -p /tmp/dir1/dir2 #创建一个目录树 rm -f file1 #删除一个叫做 'file1' 的文件' rmdir dir1 #删除一个叫做 'dir1' 的目录' rm -rf dir1 #删除一个叫做 'dir1' 的目录并同时删除其内容 rm -rf dir1 dir2 #同时删除两个目录及它们的内容 mv dir1 new_dir #重命名/移动 一个目录 cp file1 file2 #复制一个文件 cp dir/* . #复制一个目录下的所有文件到当前工作目录 cp -a /tmp/dir1 . #复制一个目录到当前工作目录 cp -a dir1 dir2 #复制一个目录 ln -s file1 lnk1 #创建一个指向文件或目录的软链接 ln file1 lnk1 #创建一个指向文件或目录的物理链接 touch -t 0712250000 file1 #修改一个文件或目录的时间戳 - (YYMMDDhhmm) file file1 outputs the mime type of the file as text iconv -l #列出已知的编码 iconv -f fromEncoding -t toEncoding inputFile > outputFile creates a new from the given input file by assuming it is encoded in fromEncoding and converting it to toEncoding. find . -maxdepth 1 -name *.jpg -print -exec convert "{}" -resize 80x60 "thumbs/{}" \; batch resize files in the current directory and send them to a thumbnails directory (requires convert from Imagemagick)文件搜索find / -name file1 #从 '/' 开始进入根文件系统搜索文件和目录 find / -user user1 #搜索属于用户 'user1' 的文件和目录 find /home/user1 -name \*.bin #在目录 '/ home/user1' 中搜索带有'.bin' 结尾的文件 find /usr/bin -type f -atime +100 #搜索在过去100天内未被使用过的执行文件 find /usr/bin -type f -mtime -10 #搜索在10天内被创建或者修改过的文件 find / -name \*.rpm -exec chmod 755 '{}' \; #搜索以 '.rpm' 结尾的文件并定义其权限 find / -xdev -name \*.rpm #搜索以 '.rpm' 结尾的文件，忽略光驱、捷盘等可移动设备 locate \*.ps #寻找以 '.ps' 结尾的文件 - 先运行 'updatedb' 命令 whereis halt #显示一个二进制文件、源码或man的位置 which halt #显示一个二进制文件或可执行文件的完整路径挂载一个文件系统mount /dev/hda2 /mnt/hda2 #挂载一个叫做hda2的盘 - 确定目录 '/ mnt/hda2' 已经存在 umount /dev/hda2 #卸载一个叫做hda2的盘 - 先从挂载点 '/ mnt/hda2' 退出 fuser -km /mnt/hda2 #当设备繁忙时强制卸载 umount -n /mnt/hda2 #运行卸载操作而不写入 /etc/mtab 文件- 当文件为只读或当磁盘写满时非常有用 mount /dev/fd0 /mnt/floppy #挂载一个软盘 mount /dev/cdrom /mnt/cdrom #挂载一个cdrom或dvdrom mount /dev/hdc /mnt/cdrecorder #挂载一个cdrw或dvdrom mount /dev/hdb /mnt/cdrecorder #挂载一个cdrw或dvdrom mount -o loop file.iso /mnt/cdrom #挂载一个文件或ISO镜像文件 mount -t vfat /dev/hda5 /mnt/hda5 #挂载一个Windows FAT32文件系统 mount /dev/sda1 /mnt/usbdisk #挂载一个usb 捷盘或闪存设备 mount -t smbfs -o username=user,password=pass //WinClient/share /mnt/share #挂载一个windows网络共享磁盘空间df -h #显示已经挂载的分区列表 ls -lSr |more #以尺寸大小排列文件和目录 du -sh dir1 #估算目录 'dir1' 已经使用的磁盘空间' du -sk * | sort -rn #以容量大小为依据依次显示文件和目录的大小 rpm -q -a --qf '%10{SIZE}t%{NAME}n' | sort -k1,1n #以大小为依据依次显示已安装的rpm包所使用的空间 (fedora, redhat类系统) dpkg-query -W -f='${Installed-Size;10}t${Package}n' | sort -k1,1n #以大小为依据显示已安装的deb包所使用的空间 (ubuntu, debian类系统)用户和群组groupadd group_name #创建一个新用户组 groupdel group_name #删除一个用户组 groupmod -n new_group_name old_group_name #重命名一个用户组 useradd -c "Name Surname " -g admin -d /home/user1 -s /bin/bash user1 #创建一个属于 "admin" 用户组的用户 useradd user1 #创建一个新用户 userdel -r user1 #删除一个用户 ( '-r' 排除主目录) usermod -c "User FTP" -g system -d /ftp/user1 -s /bin/nologin user1 #修改用户属性 passwd #修改口令 passwd user1 #修改一个用户的口令 (只允许root执行) chage -E 2005-12-31 user1 #设置用户口令的失效期限 pwck #检查 '/etc/passwd' 的文件格式和语法修正以及存在的用户 grpck #检查 '/etc/passwd' 的文件格式和语法修正以及存在的群组 newgrp group_name #登陆进一个新的群组以改变新创建文件的预设群组文件的权限使用 "+" 设置权限，使用 "-" 用于取消 ls -lh #显示权限 ls /tmp | pr -T5 -W$COLUMNS #将终端划分成5栏显示 chmod ugo+rwx directory1 #设置目录的所有人(u)、群组(g)以及其他人(o)以读（r ）、写(w)和执行(x)的权限 chmod go-rwx directory1 #删除群组(g)与其他人(o)对目录的读写执行权限 chown user1 file1 #改变一个文件的所有人属性 chown -R user1 directory1 #改变一个目录的所有人属性并同时改变改目录下所有文件的属性 chgrp group1 file1 #改变文件的群组 chown user1:group1 file1 #改变一个文件的所有人和群组属性 find / -perm -u+s #罗列一个系统中所有使用了SUID控制的文件 chmod u+s /bin/file1 #设置一个二进制文件的 SUID 位 - 运行该文件的用户也被赋予和所有者同样的权限 chmod u-s /bin/file1 #禁用一个二进制文件的 SUID位 chmod g+s /home/public #设置一个目录的SGID 位 - 类似SUID ，不过这是针对目录的 chmod g-s /home/public #禁用一个目录的 SGID 位 chmod o+t /home/public #设置一个文件的 STIKY 位 - 只允许合法所有人删除文件 chmod o-t /home/public #禁用一个目录的 STIKY 位文件的特殊属性- 使用 "+" 设置权限，使用 "-" 用于取消 chattr +a file1 #只允许以追加方式读写文件 chattr +c file1 #允许这个文件能被内核自动压缩/解压 chattr +d file1 #在进行文件系统备份时，dump程序将忽略这个文件 chattr +i file1 #设置成不可变的文件，不能被删除、修改、重命名或者链接 chattr +s file1 #允许一个文件被安全地删除 chattr +S file1 #一旦应用程序对这个文件执行了写操作，使系统立刻把修改的结果写到磁盘 chattr +u file1 #若文件被删除，系统会允许你在以后恢复这个被删除的文件 lsattr #显示特殊的属性打包和压缩文件bunzip2 file1.bz2 #解压一个叫做 'file1.bz2'的文件 bzip2 file1 #压缩一个叫做 'file1' 的文件 gunzip file1.gz #解压一个叫做 'file1.gz'的文件 gzip file1 #压缩一个叫做 'file1'的文件 gzip -9 file1 #最大程度压缩 rar a file1.rar test_file #创建一个叫做 'file1.rar' 的包 rar a file1.rar file1 file2 dir1 #同时压缩 'file1', 'file2' 以及目录 'dir1' rar x file1.rar #解压rar包 unrar x file1.rar #解压rar包 tar -cvf archive.tar file1 #创建一个非压缩的 tarball tar -cvf archive.tar file1 file2 dir1 #创建一个包含了 'file1', 'file2' 以及 'dir1'的档案文件 tar -tf archive.tar #显示一个包中的内容 tar -xvf archive.tar #释放一个包 tar -xvf archive.tar -C /tmp #将压缩包释放到 /tmp目录下 tar -cvfj archive.tar.bz2 dir1 #创建一个bzip2格式的压缩包 tar -jxvf archive.tar.bz2 #解压一个bzip2格式的压缩包 tar -cvfz archive.tar.gz dir1 #创建一个gzip格式的压缩包 tar -zxvf archive.tar.gz #解压一个gzip格式的压缩包 zip file1.zip file1 #创建一个zip格式的压缩包 zip -r file1.zip file1 file2 dir1 #将几个文件和目录同时压缩成一个zip格式的压缩包 unzip file1.zip #解压一个zip格式压缩包RPM 包 - （Fedora, Redhat及类似系统）rpm -ivh package.rpm #安装一个rpm包 rpm -ivh --nodeeps package.rpm #安装一个rpm包而忽略依赖关系警告 rpm -U package.rpm #更新一个rpm包但不改变其配置文件 rpm -F package.rpm #更新一个确定已经安装的rpm包 rpm -e package_name.rpm #删除一个rpm包 rpm -qa #显示系统中所有已经安装的rpm包 rpm -qa | grep httpd #显示所有名称中包含 "httpd" 字样的rpm包 rpm -qi package_name #获取一个已安装包的特殊信息 rpm -qg "System Environment/Daemons" #显示一个组件的rpm包 rpm -ql package_name #显示一个已经安装的rpm包提供的文件列表 rpm -qc package_name #显示一个已经安装的rpm包提供的配置文件列表 rpm -q package_name --whatrequires #显示与一个rpm包存在依赖关系的列表 rpm -q package_name --whatprovides #显示一个rpm包所占的体积 rpm -q package_name --scripts #显示在安装/删除期间所执行的脚本l rpm -q package_name --changelog #显示一个rpm包的修改历史 rpm -qf /etc/httpd/conf/httpd.conf #确认所给的文件由哪个rpm包所提供 rpm -qp package.rpm -l #显示由一个尚未安装的rpm包提供的文件列表 rpm --import /media/cdrom/RPM-GPG-KEY #导入公钥数字证书 rpm --checksig package.rpm #确认一个rpm包的完整性 rpm -qa gpg-pubkey #确认已安装的所有rpm包的完整性 rpm -V package_name #检查文件尺寸、 许可、类型、所有者、群组、MD5检查以及最后修改时间 rpm -Va #检查系统中所有已安装的rpm包- 小心使用 rpm -Vp package.rpm #确认一个rpm包还未安装 rpm2cpio package.rpm | cpio --extract --make-directories *bin* #从一个rpm包运行可执行文件 rpm -ivh /usr/src/redhat/RPMS/`arch`/package.rpm #从一个rpm源码安装一个构建好的包 rpmbuild --rebuild package_name.src.rpm #从一个rpm源码构建一个 rpm 包YUM 软件包升级器 - （Fedora, RedHat及类似系统）yum install package_name #下载并安装一个rpm包 yum localinstall package_name.rpm #将安装一个rpm包，使用你自己的软件仓库为你解决所有依赖关系 yum update package_name.rpm #更新当前系统中所有安装的rpm包 yum update package_name #更新一个rpm包 yum remove package_name #删除一个rpm包 yum list #列出当前系统中安装的所有包 yum search package_name #在rpm仓库中搜寻软件包 yum clean packages #清理rpm缓存删除下载的包 yum clean headers #删除所有头文件 yum clean all #删除所有缓存的包和头文件DEB 包 (Debian, Ubuntu 以及类似系统)dpkg -i package.deb #安装/更新一个 deb 包 dpkg -r package_name #从系统删除一个 deb 包 dpkg -l #显示系统中所有已经安装的 deb 包 dpkg -l | grep httpd #显示所有名称中包含 "httpd" 字样的deb包 dpkg -s package_name #获得已经安装在系统中一个特殊包的信息 dpkg -L package_name #显示系统中已经安装的一个deb包所提供的文件列表 dpkg --contents package.deb #显示尚未安装的一个包所提供的文件列表 dpkg -S /bin/ping #确认所给的文件由哪个deb包提供APT 软件工具 (Debian, Ubuntu 以及类似系统)apt-get install package_name #安装/更新一个 deb 包 apt-cdrom install package_name #从光盘安装/更新一个 deb 包 apt-get update #升级列表中的软件包 apt-get upgrade #升级所有已安装的软件 apt-get remove package_name #从系统删除一个deb包 apt-get check #确认依赖的软件仓库正确 apt-get clean #从下载的软件包中清理缓存 apt-cache search searched-package #返回包含所要搜索字符串的软件包名称查看文件内容cat file1 #从第一个字节开始正向查看文件的内容 tac file1 #从最后一行开始反向查看一个文件的内容 more file1 #查看一个长文件的内容 less file1 #类似于 'more' 命令，但是它允许在文件中和正向操作一样的反向操作 head -2 file1 #查看一个文件的前两行 tail -2 file1 #查看一个文件的最后两行 tail -f /var/log/messages #实时查看被添加到一个文件中的内容文本处理cat file1 file2 ... | command <> file1_in.txt_or_file1_out.txt general syntax for text manipulation using PIPE, STDIN and STDOUT cat file1 | command( sed, grep, awk, grep, etc...) > result.txt #合并一个文件的详细说明文本，并将简介写入一个新文件中 cat file1 | command( sed, grep, awk, grep, etc...) >> result.txt #合并一个文件的详细说明文本，并将简介写入一个已有的文件中 grep Aug /var/log/messages #在文件 '/var/log/messages'中查找关键词"Aug" grep ^Aug /var/log/messages #在文件 '/var/log/messages'中查找以"Aug"开始的词汇 grep [0-9] /var/log/messages #选择 '/var/log/messages' 文件中所有包含数字的行 grep Aug -R /var/log/* #在目录 '/var/log' 及随后的目录中搜索字符串"Aug" sed 's/stringa1/stringa2/g' example.txt #将example.txt文件中的 "string1" 替换成 "string2" sed '/^$/d' example.txt #从example.txt文件中删除所有空白行 sed '/ *#/d; /^$/d' example.txt #从example.txt文件中删除所有注释和空白行 echo 'esempio' | tr '[:lower:]' '[:upper:]' #合并上下单元格内容 sed -e '1d' result.txt #从文件example.txt 中排除第一行 sed -n '/stringa1/p' #查看只包含词汇 "string1"的行 sed -e 's/ *$//' example.txt #删除每一行最后的空白字符 sed -e 's/stringa1//g' example.txt #从文档中只删除词汇 "string1" 并保留剩余全部 sed -n '1,5p;5q' example.txt #查看从第一行到第5行内容 sed -n '5p;5q' example.txt #查看第5行 sed -e 's/00*/0/g' example.txt #用单个零替换多个零 cat -n file1 #标示文件的行数 cat example.txt | awk 'NR%2==1' #删除example.txt文件中的所有偶数行 echo a b c | awk '{print $1}' #查看一行第一栏 echo a b c | awk '{print $1,$3}' #查看一行的第一和第三栏 paste file1 file2 #合并两个文件或两栏的内容 paste -d '+' file1 file2 #合并两个文件或两栏的内容，中间用"+"区分 sort file1 file2 #排序两个文件的内容 sort file1 file2 | uniq #取出两个文件的并集(重复的行只保留一份) sort file1 file2 | uniq -u #删除交集，留下其他的行 sort file1 file2 | uniq -d #取出两个文件的交集(只留下同时存在于两个文件中的文件) comm -1 file1 file2 #比较两个文件的内容只删除 'file1' 所包含的内容 comm -2 file1 file2 #比较两个文件的内容只删除 'file2' 所包含的内容 comm -3 file1 file2 #比较两个文件的内容只删除两个文件共有的部分字符设置和文件格式转换dos2unix filedos.txt fileunix.txt #将一个文本文件的格式从MSDOS转换成UNIX unix2dos fileunix.txt filedos.txt #将一个文本文件的格式从UNIX转换成MSDOS recode ..HTML < page.txt > page.html #将一个文本文件转换成html recode -l | more #显示所有允许的转换格式文件系统分析badblocks -v /dev/hda1 #检查磁盘hda1上的坏磁块 fsck /dev/hda1 #修复/检查hda1磁盘上linux文件系统的完整性 fsck.ext2 /dev/hda1 #修复/检查hda1磁盘上ext2文件系统的完整性 e2fsck /dev/hda1 #修复/检查hda1磁盘上ext2文件系统的完整性 e2fsck -j /dev/hda1 #修复/检查hda1磁盘上ext3文件系统的完整性 fsck.ext3 /dev/hda1 #修复/检查hda1磁盘上ext3文件系统的完整性 fsck.vfat /dev/hda1 #修复/检查hda1磁盘上fat文件系统的完整性 fsck.msdos /dev/hda1 #修复/检查hda1磁盘上dos文件系统的完整性 dosfsck /dev/hda1 #修复/检查hda1磁盘上dos文件系统的完整性初始化一个文件系统mkfs /dev/hda1 #在hda1分区创建一个文件系统 mke2fs /dev/hda1 #在hda1分区创建一个linux ext2的文件系统 mke2fs -j /dev/hda1 #在hda1分区创建一个linux ext3(日志型)的文件系统 mkfs -t vfat 32 -F /dev/hda1 #创建一个 FAT32 文件系统 fdformat -n /dev/fd0 #格式化一个软盘 mkswap /dev/hda3 #创建一个swap文件系统SWAP文件系统mkswap /dev/hda3 #创建一个swap文件系统 swapon /dev/hda3 #启用一个新的swap文件系统 swapon /dev/hda2 /dev/hdb3 #启用两个swap分区备份dump -0aj -f /tmp/home0.bak /home #制作一个 '/home' 目录的完整备份 dump -1aj -f /tmp/home0.bak /home #制作一个 '/home' 目录的交互式备份 restore -if /tmp/home0.bak #还原一个交互式备份 rsync -rogpav --delete /home /tmp #同步两边的目录 rsync -rogpav -e ssh --delete /home ip_address:/tmp #通过SSH通道rsync rsync -az -e ssh --delete ip_addr:/home/public /home/local #通过ssh和压缩将一个远程目录同步到本地目录 rsync -az -e ssh --delete /home/local ip_addr:/home/public #通过ssh和压缩将本地目录同步到远程目录 dd bs=1M if=/dev/hda | gzip | ssh user@ip_addr 'dd of=hda.gz' #通过ssh在远程主机上执行一次备份本地磁盘的操作 dd if=/dev/sda of=/tmp/file1 #备份磁盘内容到一个文件 tar -Puf backup.tar /home/user 执行一次对 '/home/user' #目录的交互式备份操作 ( cd /tmp/local/ && tar c . ) | ssh -C user@ip_addr 'cd /home/share/ && tar x -p' #通过ssh在远程目录中复制一个目录内容 ( tar c /home ) | ssh -C user@ip_addr 'cd /home/backup-home && tar x -p' #通过ssh在远程目录中复制一个本地目录 tar cf - . | (cd /tmp/backup ; tar xf - ) #本地将一个目录复制到另一个地方，保留原有权限及链接 find /home/user1 -name '*.txt' | xargs cp -av --target-directory=/home/backup/ --parents #从一个目录查找并复制所有以 '.txt' 结尾的文件到另一个目录 find /var/log -name '*.log' | tar cv --files-from=- | bzip2 > log.tar.bz2 #查找所有以 '.log' 结尾的文件并做成一个bzip包 dd if=/dev/hda of=/dev/fd0 bs=512 count=1 #做一个将 MBR (Master Boot Record)内容复制到软盘的动作 dd if=/dev/fd0 of=/dev/hda bs=512 count=1 #从已经保存到软盘的备份中恢复MBR内容光盘cdrecord -v gracetime=2 dev=/dev/cdrom -eject blank=fast -force #清空一个可复写的光盘内容 mkisofs /dev/cdrom > cd.iso #在磁盘上创建一个光盘的iso镜像文件 mkisofs /dev/cdrom | gzip > cd_iso.gz #在磁盘上创建一个压缩了的光盘iso镜像文件 mkisofs -J -allow-leading-dots -R -V "Label CD" -iso-level 4 -o ./cd.iso data_cd #创建一个目录的iso镜像文件 cdrecord -v dev=/dev/cdrom cd.iso #刻录一个ISO镜像文件 gzip -dc cd_iso.gz | cdrecord dev=/dev/cdrom - #刻录一个压缩了的ISO镜像文件 mount -o loop cd.iso /mnt/iso #挂载一个ISO镜像文件 cd-paranoia -B #从一个CD光盘转录音轨到 wav 文件中 cd-paranoia -- "-3" #从一个CD光盘转录音轨到 wav 文件中（参数-3） cdrecord --scanbus #扫描总线以识别scsi通道 dd if=/dev/hdc | md5sum #校验一个设备的md5sum编码，例如一张 CD网络 - （以太网和WIFI无线）ifconfig eth0 #显示一个以太网卡的配置 ifup eth0 #启用一个 'eth0' 网络设备 ifdown eth0 #禁用一个 'eth0' 网络设备 ifconfig eth0 192.168.1.1 netmask 255.255.255.0 #控制IP地址 ifconfig eth0 promisc #设置 'eth0' 成混杂模式以嗅探数据包 (sniffing) dhclient eth0 #以dhcp模式启用 'eth0' route -n #查看路由表 route add -net 0/0 gw IP_Gateway #配置默认网关 route add -net 192.168.0.0 netmask 255.255.0.0 gw 192.168.1.1 #配置静态路由到达网络'192.168.0.0/16' route del 0/0 gw IP_gateway #删除静态路由 hostname #查看机器名 host www.example.com #把一个主机名解析到一个网际地址或把一个网际地址解析到一个主机名。 nslookup www.example.com #用于查询DNS的记录，查看域名解析是否正常，在网络故障的时候用来诊断网络问题。 ip link show #查看网卡信息 mii-tool #用于查看、管理介质的网络接口的状态 ethtool #用于查询和设置网卡配置 netstat -tupl #用于显示TCP/UDP的状态信息 tcpdump tcp port 80 #显示所有http协议的流量JPS工具jps(Java Virtual Machine Process Status Tool)是JDK 1.5提供的一个显示当前所有java进程pid的命令，简单实用，非常适合在linux/unix平台上简单察看当前java进程的一些简单情况。我想很多人都是用过unix系统里的ps命令，这个命令主要是用来显示当前系统的进程情况，有哪些进程，及其 id。jps 也是一样，它的作用是显示当前系统的java进程情况，及其id号。我们可以通过它来查看我们到底启动了几个java进程（因为每一个java程序都会独占一个java虚拟机实例），和他们的进程号（为下面几个程序做准备），并可通过opt来查看这些进程的详细启动参数。使用方法：在当前命令行下打 jps(需要JAVA_HOME，没有的话，到改程序的目录下打) 。jps存放在JAVA_HOME/bin/jps，使用时为了方便请将JAVA_HOME/bin/加入到Path. $> jps 23991 Jps 23789 BossMain 23651 Resin 比较常用的参数： #-q 只显示pid，不显示class名称,jar文件名和传递给main 方法的参数 $> jps -q 28680 23789 23651 #-m 输出传递给main 方法的参数，在嵌入式jvm上可能是null $> jps -m 28715 Jps -m 23789 BossMain 23651 Resin -socketwait 32768 -stdout /data/aoxj/resin/log/stdout.log -stderr /data/aoxj/resin/log/stderr.log #-l 输出应用程序main class的完整package名 或者 应用程序的jar文件完整路径名 $> jps -l 28729 sun.tools.jps.Jps 23789 com.asiainfo.aimc.bossbi.BossMain 23651 com.caucho.server.resin.Resin #-v 输出传递给JVM的参数 $> jps -v 23789 BossMain 28802 Jps -Denv.class.path=/data/aoxj/bossbi/twsecurity/java/trustwork140.jar:/data/aoxj/bossbi/twsecurity/java/:/data/aoxj/bossbi/twsecurity/java/twcmcc.jar:/data/aoxj/jdk15/lib/rt.jar:/data/aoxj/jd k15/lib/tools.jar -Dapplication.home=/data/aoxj/jdk15 -Xms8m 23651 Resin -Xss1m -Dresin.home=/data/aoxj/resin -Dserver.root=/data/aoxj/resin -Djava.util.logging.manager=com.caucho.log.LogManagerImpl - Djavax.management.builder.initial=com.caucho.jmx.MBeanServerBuilderImpl jps 192.168.0.77 #列出远程服务器192.168.0.77机器所有的jvm实例，采用rmi协议，默认连接端口为1099（前提是远程服务器提供jstatd服务） #注：jps命令有个地方很不好，似乎只能显示当前用户的java进程，要显示其他用户的还是只能用unix/linux的ps命令
环境初始化# 1.环境准备，关闭防火墙服务 [root@teach_jumpserver-]iptables -F [root@teach jumpserver-]systemctl disable firewalld [root@teach_jumpserver-]systemctl stop firewalld [root@teach jumpserver-]getenforce Disabled # 2.配置yum源，准备好阿里云的yum源，以及epel源 wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo wget -O /etc/yum.repos.d/epel.repo http://mirrors.aliyun.com/repo/epel-7.repo yum cleann all #清空原有的yum缓存 yum makecache #生成新的yum缓存，便于加速软件下载 # 3.安装系统初始化所需的软件 yum install -y bash-completion vim lrzsz wget expect net-tools nc nmap tree dos2unix htop iftop iotop unzip telnet sl psmisc nethogs glances bc ntpdate openldap-devel gcc # 4.安装jumpserver运行所需的依赖环境 yum -y install git python-pip gcc automake autoconf python-devel vim sshpass lrzsz readline-devel zlib zlib-devel # 5.修改系统的字符集，改为是中文的 localedef -c -f UTF-8 -i zh_CN zh_CN.UTF-8 export LC_ALL=zh_CN.UTF-8 #把修改字符集的命令，写入全局配置文件 echo 'LANG="zh_CN.UTF-8"'> /etc/locale.conf # 6,检查系统编码 locale部署数据库mysql 5.6# 1.获取mysg15.6的软件包 # wget https://cdn.mysql.com//Downloads/MySQL-5.6/MySQL-5.6.49-1.el7.x86_64.rpm-bundle.tar mkdir /teach_jmp cd /teach jmp/ wget https://cdn.mysql.com//Downloads/MySQL-5.6/MySQL-5.6.49-1.el7.x86_64.rpm-bundle.tar # 2.解压缩该mysql压缩包 mkdir mysql_rpm tar -xf MySoL-5.6.49-1.el7.x86_64.rpm-bundle.tar -C ./mysql_rpm/ # 3,使用yum命令，安装一系列的rpm包 cd mysql rpm/ 1s yum localinstall ./* # 4.安装完毕后，检查mysg1的配置文件，做如下的修改 [root@teach jumpserver mysql_rpm]#cat /etc/my.cnf [mysqld] datadir=/var/lib/mysql socket=/var/lib/mysql/mysql.sock # Disabling symbolic-links is recommended to prevent assorted security risks symbolic-links=0 # Settings user and group are ignored when systemd is used. # If you need to run mysqld under a different user or group, # customize your systemd unit file for mariadb according to the # instructions in http://fedoraproject.org/wiki/Systemd [mysqld_safe] log-error=/var/log/mariadb/mariadb.log pid-file=/var/run/mariadb/mariadb.pid # # include all files from the config directory # !includedir /etc/my.cnf.d systemctl start mysql netstat -tunlp # 5.对mysql进行初始化，mysql5.6版本在安装完毕后，会默认生成一个root的随机密码，如下 cat ~/.mysql secret # 6,是否要修改原有的密码，自行决定 mysqladmin -uroot -pXXXXX password XXXXXX #更为安全的修改root密码的操作 update mysql.user set password=password('XXXXXX')where user='root'; flush privileges; # 7.再次用新密码登录mysql5.6 mysql -uroot -pXXXXXX # 8.登录数据库后，创建运行jumpserver所需的用户信息 create database jumpserver default charset 'utf8' collate 'utf8_bin'; #创建完毕数据库后，再创建用户，且设置密码 create user 'jumpserver'@'%' IDENTIFIED BY 'XXXXXX'; # 9.给该用户授予访问数据库的权限 grant all privileges on jumpserver.* to 'jumpserver'@'%' identified by 'XXXXXX'; flush privileges; 部署 Python 3.6# 1.下载python3.6的源代码，可以在线下载 cd /teach_jmp && \ wget https://www.python.org/ftp/python/3.6.10/Python-3.6.10.tgz # 2.开始源码安装python3,进行编译三部曲 tar -zxf Python-3.6.10.tgz # 指定python3的安装目录 # 编译第一曲，指定安装路径，与编译参数 ./configure --prefix=/teach_jmp/python3.6.10/ # 第二曲，第三曲 make &make install # 3.配置python3的环境变量，可以直接使用python3的命令 tail -1 /etc/profile PATH="/teach_jmp/python3.6.10/bin:SPATH" source /etc/profile echo SPATH Python3 创建虚拟环境# 1.安装虚拟环境工具 # 2.如果你的oython.3在安装模块的时候报错了，由于缺少ssl,python3无法使用，解决方式如下,删掉编译安装的python3,然后安装openssl工具，然后重新编译安装python3才行 yum install openssl openssl-devel -y # 3.再次编译安装完成python3后，再次尝试，安装python3的模块 # 先更新一下pip3的下载源，就如同更换yum源一个概念 # 操作步骤如下 mkdir -/.pip touch -/.pip/pip.conf #最终pip3的源，文件内容如下 cat ~/.pip/pip.conf [global] index-url https://mirrors.aliyun.com/pypi/simple/ # 4.下载虚拟环境工具 pip3 install firtualenv # 5.使用虚拟环境工具，再创建出一个python3解释器，用于运行代码 virtualenv --python=python3 jmp_venv1 # 6.此时你的1inux服务器上就有2个python3解释器了 解释器本体是：/teach_jmp/python3.6.10/bin/python3 我们创建了一个虚拟的解释器，路径是：/teach_jmp/jmp_venv1/bin/python3 # 7.激活虚拟环境，其实是默认修改了环境变量 source /teach_jmp/jmp_venv1/bin/activate #可以退出虚拟环境，查看解释器的路径，效果 deactivate部署 Redis 数据库# 1.安装redis的形式 # rpm包手动安装，需要手动解决依赖，不推荐使用 # yum自动化安装，适合软件调试学习使用，安装自动解决依赖，很好用源代码编译安装redis # 2.选择yum自动化安装即可 # 配置好yum源才行，epel源 yum install redis -y systemctl start redis netstat -tunlpgrep 6379部署 jumpserver 服务# 1.获取jumpserver程序的代码，github有公共仓库，所有人都可以下载，私有仓库，只有企业内部人员，用账号密码登录后下载 wget https://github.com/jumpserver/jumpserver/releases/download/v2.1.0/jumpserver-v2.1.0.tar.gz # 2,解压缩源码，且安装运行jumpserver系统必须的依赖组件 tar -zxf jumpserver-v2.1.0.tar.gz ln -s /teach_jmp/jumpserver-v2.1.0 /teach_jmp/jumpserver # 安装依赖关系 yum install -y bash-completion vim lrzsz wget expect net-tools nc nmap tree dos2unix htop iftop iotop unzip telnet sl psmisc nethogs glances bc ntpdate openldap-devel # 3.安装运行jumpserver所需要的模块（由python开发的程序，必须安装该程序使用到的一些模块，才能正确运行） # 安装jumpserver模块，听老师的，先激活虚拟环境，然后再安装 source /teach_jmp/jmp_venv1/bin/activate #安装模块 pip3 install -r /teach_jmp/jumpserver/requirements/requirements.txt修改 jumpserver 配置文件# 1.修改配置文件，默认未修改的配置文件如下，我们需要做一些定制修改 (jmp_venvl)[root@teach_jumpserverjumpserver]#grep -Ev '#$config.yml SECRET KEY: BOOTSTRAP TOKEN: DB ENGINE:mysql DBH0ST:127.0.0.1 DB PORT:3306 DB USER:jumpserver DB PASSWORD: DB NAME:jumpserver HTTP BIND HOST:0.0.0.0 HTTP LISTEN PORT:8080 WSLISTEN PORT:8070 REDIS HOST:127.0.0.1 REDIS PORT:6379 # 2.生成密钥 if "$SECRET_KEY"=""];then SECRIT_KEY=`cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 50`;echo "SECRET_KEY=$SECRET_KEY" >> ~/.bashrc; echo $SECRET_KEY;else echo $SECRET_KEY; fi #生成token密钥 if "$BOOTSTRAP TOKEN"=""];then BOOTSTRAP TOKEN=`cat /dev/urandom tr -dc A-Za-z0-9 head -c 16`;echo "BOOTSTRAP_TOKEN=$BOOTSTRAP_TOKEN" >> ~/.bashrc;echo $BOOTSTRAP_TOKEN;else echo $BOOTSTRAP_TOKEN; fi # 3.配置文件填入生成的密钥，数据库密码对 python 程序进行数据库迁移# 1.jumpserver后台程序，数据库迁移命令 python3 /teach_jmp/jumpserver/apps/manage.py makemigrations python3 /teach_jmp/jumpserver/apps/manage.py migrate启动 jumpserver/teach_jmp/jumpserver/jms start -d部署KoKo组件# 1.下载koko源代码 wget https://github.com/jumpserver/koko/releases/download/v2.1.0/koko-v2.1.0-linux-amd64.tar.gz # 2.解压缩配置koko软件 tar -zxf koko-v2.1.0-linux-amd64.tar.gz chown -R root:root koko-v2.1.0-linux-amd64 1n -s /teach_jmp/koko-v2.1.0-linux-amd64 /teach_jmp/koko #3.修改koko配置文件信息 (jmp_venv1)[root@teach_jumpserver koko]#grep -Ev '^#|^$'/teach_jmp/koko/config.yml CORE_HOST: http://127.0.0.1:8080 BOOTSTRAP_TOKEN: "$BOOTSTRAP_TOKEN" LOG_LEVEL: INFO REDIS_HOST: 127.0.0.1 REDIS_PORT: 6379 REDIS_PASSWORD: REDIS_CLUSTERS: REDIS_DB_ROOM: # 4.启动koko程序 /teach_jmp/koko -d ps -ef |grep koko # 5.可以检查koko的日志，明确koko是否正确启动 tail /teach_jmp/koko/data/logs/koko.log ## 部署Guacamole组件# 1.获取软件代码 ll 2020-07-22-16-48-00-docker-guacamole-v2.1.0.tar.gz # 2.解压缩配置 tar -zxvf 2020-07-22-16-48-00-docker-guacamole-v2.1.0.tar.gz mv docker-guacamole-2.1.0/ guacamole cd guacamole/ ls # 3.继续解压执行程序 tar -zxf guacamole-server-1.2.0.tar.gz tar -zxf ssh-forward.tar.gz # 4.编译安装该软件程序 cd guacamole-server-1.2.0/ ls #编译软件之前，基本上都要吧编译环境准备好 yum install cairo-devel libjpeg-turbo-devel libjpeg-devel libpng-devel libtool uuid-devel -y #可选的软件依赖 yum install freerdp-devel pango-devel libssh2-devel libtelnet-devel libvncserver-devel libwebsockets-devel pulseaudio-libs-devel openssl-devel libvorbis-devel libwebp-devel -y # 5.安装FFmpeg.工具 yum install epel-release -y rpm -v --import http://li.nux.ro/download/nux/RPM-GPG-KEY-nux.ro rpm -Uvh http://li.nux.ro/download/nux/dextop/el7/x86_64/nux-dextop-release-0 -5.el7.nux.noarch.rpm yum install ffmpeg ffmpeg-devell -y # 6.编译安装guacamole ./configure --with-init-dir=/etc/init.d make && make install # 7.部署java开发环境 yum install -y java-1.8.0-openjdk # 8.创建运行guacamole所需的文件夹 mkdir -p /config/guacamole /config/guacamole/extensions /config/guacamole/record /config/guacamole/drive && \ chown daemon:daemon /config/guacamole/record /config/guacamole/drive && \ cd /config # 9.下载tomcat工具，用于运行java项目 cd /opt && \ wget http://mirrors.tuna.tsinghua.edu.cn/apache/tomcat/tomcat-9/v9.0.36/bin/apache- tomcat-9.0.36.tar.gz # 10.部署guacamole和tomcat工具的结合，需要修改他们的配置文件 cd /opt && \ tar -xf apache-tomcat-9.0.36.tar.gz && \ mv apache-tomcat-9.0.36 tomcat9 && \ rm -rf /opt/tomcat9/webapps/* && \ sed -i 's/Connector port="8080"/Connector port="8081"/g' /opt/tomcat9/conf/server.xml && \ echo "java.util.logging.ConsoleHandler.encoding = UTF-8">> /opt/tomcat9/conf/logging.properties && \ ln -sf /teach_jmp/guacamole/guacamole-1.0.0.war /opt/tomcat9/webapps/ROOT.war && \ ln -sf /teach_jmp/guacamole/guacamole-auth-jumpserver-1.0.0.jar /config/guacamole/extensions/guacamole-auth-jumpserver-1.0.0.jar && \ ln -sf /teach_jmp/guacamole/root/app/guacamoe/guacamole.properties /config/guacamole/guacamole.properties # 11.设置guacamolel的运行环境变量 export JUMPSERVER_SERVER=http://127.0.0.1:8080 echo "export JUMPSERVER_SERVER=http://127.0.0.1:8080" >> ~/bashrc export BOOTSTRAP_TOKEN=zxffNymGjP79j6BN echo "export BOOTSTRAP_TOKEN=zxffNymGjP79j6BN">>~/.bashrc export JUMPSERVER_KEY_DIR=/config/guacamole/keys echo "export JUMPSERVER_KEY_DIR=/config/guacamole/keys" >> ~/.bashrc export GUACAMOLE_HOME=/config/guacamole echo "export GUACAMOLE_HOME=/config/guacamole" >> ~/.bashrc export GUACAMOLE_LOG_LEVEL=ERROR echo "export GUACAMOLE_LOG_LEVEL=ERROR" >> ~/.bashrc export JUMPSERVER_ENABLE_DRIVE=true echo "export JUMPSERVER_ENABLE_DRIVE=true" >> ~/.bashrc # 12.启动服务 /etc/init.d/guacd start sh /opt/tomcat9/bin/startup.sh Lina 组件部署#提前准备好nginx服务 yum install nginx -y # 1.获取代码 wget https://github.com/jumpserver/lina/releases/download/v2.1.0/lina-v2.1.0.tar.gz # 2.解压缩1inna组件 tar -zxf lina-v2.1.0.tar.gz mv lina-v2.1.0 lina chown -R nginx:nginx lina 部署 Luna组件# 1.获取1una代码 wget https://github.com/jumpserver/luna/releases/download/v2.1.1/luna-v2.1.1.tar.gz tar -zxf luna-v2.1.1.tar.gz mv luna-v2.1.1 luna chown -R root.root luna 部署 nginx# 1.安装nginx yum install nginx -y # 2。修改nginxi配置文件，删除一些默认的配置，然后添加新的配置 sed -i '38,58d' /etc/nginx/nginx.conf # 3.加入新的虚拟主机配置 server { listen 80; client_max_body_size 100m;#录像及文件上传大小限制 location /ui/ { try_files $uri / /index.html; alias /opt/lina/; } location /luna/ { try_files $uri / /index.html; alias/opt/Iuna/;#1una路径，如果修改安装目录，此处需要修改 } location /media/ { add_header Content-Encoding gzip; root /opt/jumpserver/data/;#录像位置，如果修改安装目录，此处需要修改 } location /static/ { root /opt/jumpserver/data/;#静态资源，如果修改安装目录，此处需要修改 } location /koko/ { proxy_pass http://localhost:5000; proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; access_log off; } location /guacamole/ { proxy_pass http://localhost:8081/; proxy_buffering off; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection $http_connection; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; access_log off; } location /ws/ { proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; proxy_pass http://localhost:8070; proxy_http_version 1.1; proxy_buffering off; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "upgrade"; } location /api/{ proxy_pass http://localhost:8080; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location /core/ { proxy_pass http://localhost:8080; proxy_set_header X-Real-IP $remote_addr; proxy_set_header Host $host; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; } location / { rewrite ^/(.*)$ /ui/$1 last; } } # 4.启动nginx nginx -t nginx补充Koko启动# 新版jumpserver启动koko组件时，经常会出现问题 2020-08-3021:18:01[ERR0]P0 ST http://127.0.0.1:8080/api/v2/termina1/termina1- registrations/failed,get code:4o1,{"detail":"身份认证信息未提供。"}# 想要彻底解决，如下方案 # 1.删除koko的data目录下的.access_,key文件 (jmp_venv1)[root@teach_jumpserver keys]#pwd /teach_jmp/koko/data/keys ls -a rm -rf .access_key # 2.重新生成获取两个重要的密钥，然后修改jumpserver,后台所有组件的配置，重启服务 SECRET_KEY BOOTSTRAP_TOKEN # 重新生成这2个key # 第一步：修改环境变量配置文件 vim ~/.bashrc # 删除该2个变量 SECRET_KEY BOOTSTRAP_TOKEN # 第二步：重新登陆liux会话，检查该变量是否存在，没有几正确 echo $SECRET_KEY echo $BOOTSTRAP_TOKEN # 第三步：重新生成这2个密钥 if ["$SECRET_KEY" = ""]; then SECRET_KEY=` cat /dev/urandom | tr -dc A-Za-z0-9 | head -c 50`; echo "SECRET_KEY=$SECRET_KEY" >> ~/bashrc; echo $SECRET_KEY ;else echo $SECRET_KEY; fi if ["$BOOTSTRAP_TOKEN" = ""]; then BOOTSTRAP_TOKEN=` cat /dev/urandom tr -dc A-Za-z0-9 | head -c 16`; echo "BOOTSTRAP_TOKEN=$BOOTSTRAP_TOKEN" >> ~/bashrc; echo $BOOTSTRAP_TOKEN; else echo $BOOTSTRAPTOKEN; fi source /teach_jmp/jmp_venv1/bin/activate # 第四步：修改jumpserver后台配置文件config.yml vim config.yml grep -Ev '^#|^&' config.yml # 第五步：重新启动jumpserver核心后台程序 /teach_jmp/jumpserver/jms stop /teach_jmp/jumpserver/jms start -d # 第六步：修改koko的配置文件，准备启动koko vim config.yml grep -Ev '^#|^&' config.yml # 第七步：见证koko的正确启动 ./koko -d netstat -tunlp|grep 2222 netstat -tunlp|grep 5000 # 第八步：由于修改了密钥，还会影响到其他的服务，需要修改配置 # 第九步：重启服务 /etc/init.d/guacd restart /opt/tomcat9/bin/shutdown.sh /opt/tomcat9/bin/startup.sh
安装1Panel服务器运维管理面板项目：https://github.com/1Panel-dev/1Panel这个面板项目，基本都是容器化部署，这个我比较喜欢用来做一些中间件的部署，像数据库、配置中心、kafka等！部署网站不是很推荐，因为这个证书申请需要账号。部署：curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && sudo bash quick_start.sh安装宝塔服务器运维管理面板这个面板项目部署不是容器化部署，是直接安装在系统里面的，部署网站非常方便，还支持免费申请证书，不需要账号！部署yum install -y wget && wget -O install.sh https://download.bt.cn/install/install_6.0.sh && sh install.sh ed8484bec宝塔面板安装完后，直接在应用商店安装 LNMP 四件套。部署typecho本次部署博客使用的是 typecho 框架和宝塔面板官网：https://typecho.org/从官网下载完压缩包之后，先在宝塔面板的网站选项中添加网站，输入域名等，然后直接在/www/wwwroot/域名/解压typecho如果是在云服务器中一定要开放各个服务的端口，否则在外网无法访问。之后就是访问typecho页面完成初始化设置，建立一个新的数据库用户和数据库专门负责typecho。选择主题在Github上搜 typecho 可以搜到很多主题nexmoe：https://github.com/theme-nexmoe/typecho-theme-nexmoeCuckoo：https://github.com/bhaoo/Cuckoo我选择的是Cuckoo，Cuckoo的后台页面做的不错，而且官方文档写的也很详细，部署起来很方便。也可以多试试别的主题，选择适合自己的一款。安装主题也很简单，在typecho文件中进入/usr/themes，把下载的主题压缩包直接上传到这个目录解压即可，然后进入typecho后台启用主题。关于主题的更多操作，多看官方文档即可。