最近有一个系统蜜罐需求,即设置一些假接口以供访问,当接口被访问时即有可能系统正在被扫描,需要将访问的路径和ip地址输出到指定日志文件中以便观察。由于输出日志写在了util中但是不可能将所有的util日志都当作是攻击日志,所以需要过滤指定内容的日志输出到蜜罐日志文件中。

定义日志输出util

private static final String IP_UNKNOWN = "unknown";
private static final String IP_LOCAL = "127.0.0.1";
private static final int IP_LEN = 15;

    /**
     * 获取客户端真实ip
     * @param request request
     * @return 返回ip
     */
    public static String getIP(ServerHttpRequest request) {
        HttpHeaders headers = request.getHeaders();
        String ipAddress = headers.getFirst("x-forwarded-for");
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = headers.getFirst("Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = headers.getFirst("WL-Proxy-Client-IP");
        }
        if (ipAddress == null || ipAddress.length() == 0 || IP_UNKNOWN.equalsIgnoreCase(ipAddress)) {
            ipAddress = Optional.ofNullable(request.getRemoteAddress())
                    .map(address -> address.getAddress().getHostAddress())
                    .orElse("");
            if (IP_LOCAL.equals(ipAddress)) {
                // 根据网卡取本机配置的IP
                try {
                    InetAddress inet = InetAddress.getLocalHost();
                    ipAddress = inet.getHostAddress();
                } catch (UnknownHostException e) {
                    // ignore
                }
            }
        }

        // 对于通过多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
        if (ipAddress != null && ipAddress.length() > IP_LEN) {
            int index = ipAddress.indexOf(",");
            if (index > 0) {
                ipAddress = ipAddress.substring(0, index);
            }
        }
        return ipAddress;
    }

public static void printServerLog(ServerHttpRequest request){
String target = request.getURI().toASCIIString();
String uri = request.getPath().value();
String clientIp = getIp(request);
log.info("HoneyPotLog: clientIp={},uri={},target={}",clientIp,uri,clientIp);
}
}

定义蜜罐接口

@RestController
public class HoneyPotController{
  @RequestMapping("/admin")
  public void admin(ServerHttpRequest request){
    WebUtil.printServerLog(request);
  }
  @RequestMapping("/config")
  public void config(ServerHttpRequest request){
    WebUtil.printServerLog(request);
  }
}

自定义logback过滤器

public class HoneyPotLogFilter extends Filter<ILoggingEvent>{
  @Override
  public FilterReply decide(ILoggingEvent event){
    if (StrUtil.startWith(event.getMessage,"HoneyPotLog: ")){
      return FilterReply.ACCEPT;
    }else{
      return FilterReply.DENY;
    }
  }
}

使用自定义过滤器

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
  <property name="LOG_HOME" value="/test/logs"/>
  <appender name="HONEYPOT_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
  <File>${LOG_HOME}/honeypot.log</File>
    <encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
    <pattern>%d{yyyy-MM-dd HH:mm:ss} [%X{traceId}] %-4relative [%thread] %-5level %logger{35} - %msg %n</pattern>
    </encoder>
    <!--使用自定义过滤器-->
    <filter class="com.test.gateway.filter.logback.HoneyPotLogFilter"/>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindRollingPolicy">
    <fileNamePattern>${LOG_HOME}/honeypot.log.%i.gz</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>3</maxIndex>
    </rollingPolicy>
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
    <maxFileSize>50MB</maxFileSize>
    </triggeringPolicy>
  </appender>
  <root level="INFO">
    <appender-ref ref="HONEYPOT_FILE"/>
  </root>
</configuration>