Excluding private/protected posts via IP

Bypass Password Protected Posts

You are, unfortunately right about the lack of filters, and hacking the core is inevitable. One solutions that seems to work revolves around the sanitize_post_field() function which fires off a number of interesting filters if its $context argument is not set to raw.

The template function that is responsible for deciding whether to show the password field or the content is http://core.trac.wordpress.org/browser/tags/3.2.1/wp-includes/post-template.php#L556

Notice how nice it would be to void the post_password? Let’s dig in…

See how the post is acquired through the get_post() function:
http://core.trac.wordpress.org/browser/tags/3.2.1/wp-includes/post.php#L370

The function contains no filters or actions to hook into, but sanitize_post_field() contains some filters that can be hooked into, and post_password goes through that function.

Please refer to the function http://core.trac.wordpress.org/browser/tags/3.2.1/wp-includes/post.php#L1676

Notice, however, that if $context == 'raw'; the function returns, and none of the filters are fired off. So a little core hack is required that will make the flow of the code reach those filters.

// wp-includes/post.php, line 557
-- $post = get_post($post);
++ $post = get_post($post, null, 'display');

Will force the $context to be anything but raw. This will permit to do something along the following lines:

function wpse31407_bypass_password( $value, $post_id, $context ) {
    $allowed_ips = array( '127.0.0.1', '255.255.255.255', ... );
    if (in_array( $_SERVER['REMOTE_ADDR'], $allowed_ips )) return '';
    else return $value;
}
add_filter( 'post_password', 'wpse31407_bypass_password', null, 3 );

This will effectively remove the password, so the test http://core.trac.wordpress.org/browser/tags/3.2.1/wp-includes/post-template.php#L556 if ( empty($post->post_password) ) is evaluated to true allowing you to bypass the password. There should be no implications by getting the post with a display context, as it is used only for the retrieval of the post_password.

You can as well create your own filter in the core around the post_password_required() function mentioned above, not to overcomplicate things, maybe. Up to you.

Bypass Private Posts

Now, as for private posts, refer to this bit or query.php http://core.trac.wordpress.org/browser/tags/3.2.1/wp-includes/query.php#L2649 as this is where the found post get stripped off of private entries.

And you’d be tempted to hook into the http://core.trac.wordpress.org/browser/tags/3.2.1/wp-includes/query.php#L2625 posts_results filter and go in and alter the post_status for each post from private to public… however, as you may have seen the get_post_status() function works on its own copy of a post that it gets from get_post()… …which again has no filters but sanitize_post_field() does, which again requires a $context of anything but raw. So again:

// wp-includes/post.php, line 563
-- $post = get_post($post);
++ $post = get_post($post, null, 'display');

And hook into a filter to trick the function into thinking that the post is published by doing this:

function wpse31407_bypass_private( $value, $post_id, $context ) {
    $allowed_ips = array( '127.0.0.1', '255.255.255.255', ... );
    if (in_array( $_SERVER['REMOTE_ADDR'], $allowed_ips )) return 'publish';
    else return $value;
}
add_filter( 'post_status', 'wpse31407_bypass_private', null, 3 );

Or wrap your own filter around the get_post_status() function.

Conclusion

Once you understand which parts of the core are responsible for blocking the posts you can attempt to modify them however you require, so I hope my long answer helps to an extent, and someone follows up with more tips and perhaps better solutions. Great question, got me digging and thinking.